如何在sequelize中对连接表上的其他列进行ORM?

Tom*_*Tom 9 javascript sequelize.js

我正在使用node v9.5,sequelize v4.33(postgres方言).

我有两个一流的模型:( Driver特定的人)和Car(通用的制造+模型组合).到目前为止,它们已通过多对多连接表连接.现在我想开始跟踪该连接表上的其他属性,但是在声明这些关系时遇到了问题,因此它们确实可以正常工作.

const Driver = sqlz.define('Driver', {
    id: { primaryKey: true, type: DataTypes.UUID },
    name: DataTypes.string
})

const Car = sqlz.define('Car', {
    id: { primaryKey: true, type: DataTypes.UUID },
    make: DataTypes.string,
    model: DataTypes.string
})

// old associations; worked great when requirements were simpler
Driver.belongsToMany(Car, {
    through: 'DriverCar',
    as: 'carList',
    foreignKey: 'driverId'
})

Car.belongsToMany(Driver, {
    through: 'DriverCar',
    as: 'driverList',
    foreignKey: 'carId'
})
Run Code Online (Sandbox Code Playgroud)

现在我想开始跟踪有关汽车与其驾驶员之间关系的更多信息,例如特定汽车的颜色.

第1步:我更新了迁移脚本,在连接表中添加了一个新列,如下所示:

queryInterface.createTable( 'DriverCar', {
    driverId: {
        type: sqlz.UUID,
        allowNull: false,
        primaryKey: true,
        references: {
            model: 'Driver',
            key: 'id'
        }
    },
    carId: {
        type: sqlz.UUID,
        allowNull: false,
        primaryKey: true,
        references: {
            model: 'Car',
            key: 'id'
        }
    },
    createdAt: {
        type: sqlz.DATE,
        allowNull: false
    },
    updatedAt: {
        type: sqlz.DATE,
        allowNull: false
    },

    // new column for join table
    color: {
      type: Sequelize.STRING
    }
})
Run Code Online (Sandbox Code Playgroud)

第2步:我定义了一个新的sqlz模型DriverCar:

const DriverCar = sqlz.define('DriverCar', {
    color: DataTypes.string
})
Run Code Online (Sandbox Code Playgroud)

(我想我只需要定义有趣的性质,以及driverId和carId仍然会从将被定义的关联推断.)

第3步:我需要更新之间存在的关联Driver,Car和DriverCar.

这就是我被困住的地方.我试图更新现有的关联,如下所示:

Driver.belongsToMany(Car, {
    through: DriverCar, // NOTE: no longer a string, but a reference to new DriverCar model
    as: 'carList',
    foreignKey: 'driverId'
})

Car.belongsToMany(Driver, {
    through: DriverCar, // NOTE: no longer a string, but a reference to new DriverCar model
    as: 'driverList',
    foreignKey: 'carId'
})
Run Code Online (Sandbox Code Playgroud)

这会毫无错误地执行,但是color当我尝试时,不会从连接表中获取新属性driver.getCarList().(Sqlz配置为记录每个SQL语句,我已经验证没有请求连接表中的属性.)

所以,相反,我想阐明这种关系更加明确,通过关联Driver到DriverCar,然后Car到DriverCar:

// Driver -> Car
Driver.hasMany(DriverCar, {
    as: 'carList',
    foreignKey: 'driverId'
})

// Car -> Driver
Car.hasMany(DriverCar, {
    foreignKey: 'carId'
})
Run Code Online (Sandbox Code Playgroud)

我还告诉sqlz DriverCar没有标准的行id:

DriverCar.removeAttribute('id')
Run Code Online (Sandbox Code Playgroud)

此时,请求驱动程序的carList(driver.getCarList())似乎工作,因为我可以看到在SQL中获取连接表道具.但保存失败:

driverModel.setCarList([ carModel1 ])

UPDATE DriverCar
SET "driverId"='a-uuid',"updatedAt"='2018-02-23 22:01:02.126 +00:00'
WHERE "undefined" in (NULL)
Run Code Online (Sandbox Code Playgroud)

错误:

SequelizeDatabaseError: column "undefined" does not exist
Run Code Online (Sandbox Code Playgroud)

我假设发生此错误是因为sqzl不理解在连接表中标识行的正确方法,因为我无法建立必要的关联.坦率地说,我对自己没有做到这一点并不自信; 我是ORM的新手,但我期待我需要指定4个关联:

  1. Driver - > DriverCar
  2. DriverCar - > Car
  3. Car - > DriverCar
  4. DriverCar - > Driver

回顾一下:我有两个一流的实体,加入了多对多的关系.我正在尝试向关系添加数据,发现ORM需要以不同的方式定义这些关联,并且无法阐明新的关联.

Ped*_*o A 14

关于你的别名的说明

临睡前的答案,我想指出的是您的别名的选择(carList和driverList)可能会更好,因为虽然自动生成sequelize方法.setCarList()和.setDriverList()根本意义,方法.addCarList(),.addDriverList(),.removeCarList()和.removeDriverList()都是无稽之谈,因为他们只需要单个实例作为参数,而不是列表.

我的回答,我不会用任何别名,让Sequelize默认为.setCars(),.setDrivers(),.addCar(),.removeCar(),等,这使得更多的意义对我来说.


工作代码示例

我已经制作了100%自包含的代码来测试它.只需复制粘贴并运行它(运行后npm install sequelize sqlite3):

const Sequelize = require("sequelize");
const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'db.sqlite' });

const Driver = sequelize.define("Driver", {
    name: Sequelize.STRING
});
const Car = sequelize.define("Car", {
    make: Sequelize.STRING,
    model: Sequelize.STRING
});
const DriverCar = sequelize.define("DriverCar", {
    color: Sequelize.STRING
});
Driver.belongsToMany(Car, { through: DriverCar, foreignKey: "driverId" });
Car.belongsToMany(Driver, { through: DriverCar, foreignKey: "carId" });

var car, driver;

sequelize.sync({ force: true })
    .then(() => {
        // Create a driver
        return Driver.create({ name: "name test" });
    })
    .then(created => {
        // Store the driver created above in the 'driver' variable
        driver = created;

        // Create a car
        return Car.create({ make: "make test", model: "model test" });
    })
    .then(created => {
        // Store the car created above in the 'car' variable
        car = created;

        // Now we want to define that car is related to driver.
        // Option 1:
        return car.addDriver(driver, { through: { color: "black" }});

        // Option 2:
        // return driver.setCars([car], { through: { color: "black" }});

        // Option 3:
        // return DriverCar.create({
        //     driverId: driver.id,
        //     carId: car.id,
        //     color: "black"
        // });
    })
    .then(() => {
        // Now we get the things back from the DB.

        // This works:
        return Driver.findAll({ include: [Car] });

        // This also works:
        // return car.getDrivers();

        // This also works:
        // return driver.getCars();
    })
    .then(result => {
        // Log the query result in a readable way
        console.log(JSON.stringify(result.map(x => x.toJSON()), null, 4));
    });
Run Code Online (Sandbox Code Playgroud)

上面的代码按预期记录(正如我所料,至少):

[
    {
        "id": 1,
        "name": "name test",
        "createdAt": "2018-03-11T03:04:28.657Z",
        "updatedAt": "2018-03-11T03:04:28.657Z",
        "Cars": [
            {
                "id": 1,
                "make": "make test",
                "model": "model test",
                "createdAt": "2018-03-11T03:04:28.802Z",
                "updatedAt": "2018-03-11T03:04:28.802Z",
                "DriverCar": {
                    "color": "black",
                    "createdAt": "2018-03-11T03:04:28.961Z",
                    "updatedAt": "2018-03-11T03:04:28.961Z",
                    "driverId": 1,
                    "carId": 1
                }
            }
        ]
    }
]
Run Code Online (Sandbox Code Playgroud)

请注意,没有秘密.观察你正在寻找额外的属性color,自带嵌套查询结果,而不是在的同一嵌套级别Car或Driver.这是Sequelize的正确行为.

确保您可以运行此代码并获得与我相同的结果.我的Node版本不同,但我怀疑这可能与任何事情有关.然后,将我的代码与您的代码进行比较,看看您是否可以找出导致问题的原因.如果您需要进一步的帮助,请随时在评论中提问:)


关于与额外字段的多对多关系的说明

由于我偶然发现了这个问题,这与你的情况有关,我想我应该在我的回答中添加一个部分,提醒你设置一个过于复杂的多对多关系的"陷阱"(这是一个教训,经过一段时间的努力,我学会了自己.

我不会重复自己,而只是简单地引用我在Sequelize Issue 9158中所说的内容,并添加链接以供进一步阅读:

连接表,即存在于关系数据库中以表示多对多关系的表,最初只有两个字段(每个表的外键定义多对多关系).虽然确实可以在该表上定义额外的字段/属性,即关联本身的额外属性(正如您在问题标题中所述),但应该注意:如果它变得过于复杂,那么这表明你应该将你的联络表"推广"给一个成熟的实体.

进一步阅读: