簡體   English   中英

如何在sequelize中對連接表上的其他列進行ORM?

[英]How do I ORM additional columns on a join table in sequelize?

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

我有兩個一流的模型: Driver (特定人)和Car (通用make +模型組合)。 到目前為止,它們已通過多對多連接表連接。 現在我想開始跟蹤該連接表上的其他屬性,但是在聲明這些關系時遇到了問題,因此它們確實可以正常工作。

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'
})

現在我想開始跟蹤有關汽車與其駕駛員之間關系的更多信息,例如特定汽車的顏色。

第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
    }
})

第2步:我為DriverCar定義了一個新的sqlz模型:

const DriverCar = sqlz.define('DriverCar', {
    color: DataTypes.string
})

(我假設我只需要定義有趣的屬性,並且仍然會從將要定義的關聯中推斷出driverIdcarId 。)

第3步:我需要更新DriverCarDriverCar之間存在的關聯。

這就是我被困住的地方。 我試圖更新現有的關聯,如下所示:

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'
})

這會driver.getCarList()執行,但是當我嘗試使用driver.getCarList()時,不會從連接表中獲取新的color屬性。 (Sqlz配置為記錄每個SQL語句,我已經驗證沒有請求連接表中的屬性。)

所以,相反,我嘗試通過將DriverDriverCar相關聯,然后將CarDriverCar相關聯來更明確地拼寫出這種關系:

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

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

我還告訴sqlz DriverCar沒有標准的行id:

DriverCar.removeAttribute('id')

此時,請求驅動程序的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)

錯誤:

SequelizeDatabaseError: column "undefined" does not exist

我假設發生此錯誤是因為sqzl不理解在連接表中標識行的正確方法,因為我無法建立必要的關聯。 坦率地說,我對自己沒有做到這一點並不自信; 我是ORM的新手,但我期待我需要指定4個關聯:

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

回顧一下:我有兩個一流的實體,加入了多對多的關系。 我正在嘗試向關系添加數據,發現ORM需要以不同的方式定義這些關聯,並且無法闡明新的關聯。

關於你的別名的說明

臨睡前的答案,我想指出的是您的別名的選擇( carListdriverList )可能會更好,因為雖然自動生成的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));
    });

上面的代碼按預期記錄(正如我所料,至少):

[
    {
        "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
                }
            }
        ]
    }
]

請注意,沒有秘密。 請注意,您要查找的額外屬性color會嵌套在查詢結果中,而不是嵌套在CarDriver的相同嵌套級別中。 這是Sequelize的正確行為。

確保您可以運行此代碼並獲得與我相同的結果。 我的Node版本不同,但我懷疑這可能與任何事情有關。 然后,將我的代碼與您的代碼進行比較,看看您是否可以找出導致問題的原因。 如果您需要進一步的幫助,請隨時在評論中提問:)


關於與額外字段的多對多關系的說明

由於我偶然發現了這個問題,這與你的情況有關,我想我應該在我的回答中添加一個部分,提醒你設置一個過於復雜的多對多關系的“陷阱”(這是一個教訓,經過一段時間的努力,我學會了自己。

我不會重復自己,而只是簡單地引用我在Sequelize Issue 9158中所說的內容 ,並添加鏈接以供進一步閱讀:

連接表,即存在於關系數據庫中以表示多對多關系的表,最初只有兩個字段(每個表的外鍵定義多對多關系)。 雖然確實可以在該表上定義額外的字段/屬性,即關聯本身的額外屬性(正如您在問題標題中所述),但應該注意:如果它變得過於復雜,那么這表明你應該將你的聯絡表“推廣”給一個成熟的實體。

進一步閱讀:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM