sequelize 模型、迁移文件和外键。哪一个是对的?

Posted

技术标签:

【中文标题】sequelize 模型、迁移文件和外键。哪一个是对的?【英文标题】:sequelize model, migration file and foreign key. which one is right? 【发布时间】:2018-11-08 03:04:46 【问题描述】:

第一次开始学习sequelize的时候,我只是不知道,只是复制粘贴代码来建立两个模型之间的关系。

我是否必须将外键同时插入模型文件和迁移文件或仅用于迁移文件,这让我很困惑。

我知道迁移是具有更改数据库命令的文件。

所以我们必须手动将外键插入迁移文件,以便数据库可以创建它们。

在sequelize doc中,如果我们添加了has many和before的关系,sequelize会自动添加外键。

所以我真的很困惑是否必须添加它们。

我在得到 550 个答案之前提出的一些问题。

有人说我们不必手动将外键添加到模型中,因为 sequelize 会自动添加它们。

但有人说我们必须手动将外键添加到模型中,因为我们必须匹配(同步)模型和迁移文件之间的列。

更糟糕的是,解释sequelize关系的文章各不相同。

那么,哪个是正确的??

我真的很想得到明确的答案。

能得到一些理由真是太感谢了(如果我们必须给模型添加外键)

不夸张,我对这个问题已经好奇了大约六个月了。

【问题讨论】:

【参考方案1】:

在创建迁移时,您还应该编写外键。也写在模型中,这样就很容易处理查询。 我们可以通过例子看到它。考虑它们是两个表 Item 和 Item_Types

1 Item_type 有很多 Items 所以,

Item_type 的迁移文件 (migration_create_item_type.js)

'use strict';
module.exports = 
up: function (queryInterface, Sequelize) 
    return queryInterface.createTable('item_types', 
        id: 
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
            type: Sequelize.INTEGER
        ,
        item_type: 
            type: Sequelize.STRING
        ,
        type_desc: 
            type: Sequelize.STRING
        ,
        createdAt: 
            allowNull: true,
            type: Sequelize.DATE,
            defaultValue: Sequelize.NOW
        ,
        updatedAt: 
            allowNull: true,
            type: Sequelize.DATE,
            defaultValue: Sequelize.NOW
        
    );
,
down: function (queryInterface, Sequelize) 
    return queryInterface.dropTable('item_types');

;

项目的迁移文件 (migration_create_item.js)

'use strict';
module.exports = 
up: function (queryInterface, Sequelize) 
    return queryInterface.createTable('items', 
        id: 
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
            type: Sequelize.INTEGER
        ,
        item_name: 
            type: Sequelize.STRING
        ,
        item_desc: 
            type: Sequelize.STRING
        ,
        item_type_id: 
            type: Sequelize.INTEGER,
            references: 
                model: 'item_types',
                key: 'id'
            
        ,
        createdAt: 
            allowNull: false,
            type: Sequelize.DATE
        ,
        updatedAt: 
            allowNull: false,
            type: Sequelize.DATE
        
    );
,
down: function (queryInterface, Sequelize) 
    return queryInterface.dropTable('items');

;

请注意,始终先创建父表,然后再创建其他表,即创建所有没有外键的表,然后再创建其他表

模型文件 Item_type (item_type.js)

'use strict';
module.exports = function (sequelize, DataTypes) 
var item_type = sequelize.define('item_type', 
    item_type: DataTypes.STRING,
    type_desc: DataTypes.STRING
);
item_type.associate = function (models) 
    item_type.hasMany(models.item, foreignKey: 'item_type_id');
;
return item_type;
;

模型文件项目(item.js)

'use strict';
 var Logger = require('./../utils/logger');

 var log = new Logger('item_type_factory');
 module.exports = function (sequelize, DataTypes) 
 var item = sequelize.define('item', 
    item_name: DataTypes.STRING,
    item_desc: DataTypes.STRING
 );
 item.associate = function (models) 
    item.item_type = item.belongsTo(models.item_type, foreignKey: 'id', target_key: 'item_type_id');
    item.order_details = item.hasMany(models.order_details);
    item.user = item.belongsToMany(models.user, through: 'supplier_items')
;

item.addNewItem = function (data) 
    return item.create(data, include: [association: item.item_type]);
;

item.findAndCreate = function (data, item_name) 
    return new Promise(function (resolve, reject) 
        item.findOrCreate(
            where: 'item_name': item_name, defaults: data
        ).spread(function (record_data, created) 
            resolve(record_data);
        ).catch(function (insert_error) 
            reject(insert_error);
        );
    );
;

item.findAllItems = function () 
    return item.findAll(
        include: [association: item.item_type]
    );
;
return item;
;

有关 sequlize 的基础知识,您可以参考以下文章, Getting started with Sequelize 仅供参考。

【讨论】:

这真的很棒。感谢您对代码的详细解释。我还想知道的一件事是,根据答案,***.com/questions/50386288/… 即使我们不添加任何外键,数据库也会创建外键列。 (同步的情况) 所以如果我们不向模型添加外键,就没有办法访问它们.. 对吧? 是的,你是对的。迁移和模型是两个不同的东西,迁移可能是为了改变表,插入默认值。这应该与模型无关。根据定义,模型是数据库表的更新版本。!! 是的,如果我们不添加外键我们无法通过orm编写正确的查询。 基本上在使用迁移时,数据库包含 1 个附加表“SequelizeMeta”,其中包含正确执行的迁移名称。您还可以使用迁移查询降级迁移【参考方案2】:

我们应该如何创建数据库架构

我们在这里有两个选择。使用sync 进行迁移或续集。总是更喜欢迁移而不是sync。迁移功能更强大,您可以使用它撤消、重做等等。 sync 不反映表格更改。例如,您定义了某个模型说User,却忘记添加gender。现在,如果您想使用 Sequelize 添加此列,则必须使用 force:true,这将删除您的所有 User 数据,这在生产中是不可取的。

谁应该定义外键约束

根据软件设计原则,无论应用程序逻辑 (Sequelize) 是否实现相同的逻辑,您的数据库约束和验证都应始终到位。例如,新开发人员可以编写原始查询,如果您没有正确的约束条件,可能会弄乱您的整个数据库。

但是,我们还想使用 sequelize 对所有关联进行正确的查询。如果 sequelize 知道数据库中存在哪些关联以及应该是什么外键,那么 sequelize 可以做到这一点的唯一方法。

因此,应在迁移和续集级别定义外键约束。

数据库约束示例

如上图所示,约束是在我的数据库架构上定义的。

sequelize 约束示例

module.exports = (sequelize, DataTypes) => 
  const Designation = sequelize.define('designation', 
    doctorId: 
      type: DataTypes.STRING,
      allowNull: false,
      field: 'doctor_id',
    ,
    name: 
      type: DataTypes.STRING,
      allowNull: false,
    ,
  , );
  Designation.associate = (models) => 
    models.designation.belongsTo(models.doctor, 
      onDelete: 'cascade',
    );
  ;

【讨论】:

感谢您的回答。因此向模型添加外键的目的是用非查询(js 语法)控制数据库。我理解正确吗? ***.com/questions/50386288/… 根据这篇文章,如果我使用同步和外键列会自动创建到数据库,即使我没有将外键添加到模型中。但是即使我们使用sync,还是建议在模型中添加外键,因为我们必须控制db,对吧? 如果以上这两个问题是正确的,我的困惑将完全消除。谢谢! @AbhinavD 对第一条评论:是的。 Sequelize 是一个 ORM,我们可以使用它的所有功能,以便我们的查询变得更容易,并且 sequelize 还带来了许多其他好处。对于评论 2:如果您已定义关联,sequelize 将尝试假定外键名称并使用它来进行关联。 现在我解决了我的好奇心。因此,无论是使用同步还是迁移,将外键插入模型只是一种选择。但是如果我们想用 orm 查询,我们必须这样做。所以可能需要可选的东西。非常感谢。

以上是关于sequelize 模型、迁移文件和外键。哪一个是对的?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 关系和外键约束

Sqlite外键不匹配sequelize迁移

Django 中的抽象模型和外键

Django 模型继承和外键

我应该让 Sequelize 模型和迁移保持同步吗?

NodeJS sequelize 自动生成模型并运行迁移 SQL 语法错误