简体   繁体   中英

sequelize querying: response gives me non-exist column

I'm creating small REST API in express.js using ORM sequelize.

I run into following issue: I would like to get all customers with their Photos. I have two tables Customer and CustomerPhoto with 1:1 relation (so PrimaryKey is also Foreign key)

The problem is that sequelize would like to make a join with no exist column CustomerPhotoCustomerPhotoID . How Can I figure out? I ran into similar problem when I wanted all rows just only from customer table. I "solved" it with attributes and retrieved only rows which I exactly needed.

Customer.js model:

module.exports = (sequelize, DataTypes) => {
  const Customer = sequelize.define('Customer', {
    customerID: {
      type: DataTypes.INTEGER,
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
    },
    firstname: {
      type: DataTypes.STRING,
      validate: {
        len: [3, 10],
      },
    },
    lastname: {
      type: DataTypes.STRING,
      validate: {
        len: [3, 10],
      },
    },
    phone: {
      type: DataTypes.STRING,
    },
    email: {
      type: DataTypes.STRING,
      unique: true,
      validate: {
        isEmail: true,
      },
    },
  }, {
    classMethods: {
      associate(models) {
        Customer.hasOne(models.CustomerPhoto, { onDelete: 'cascade', hooks: 'true' });
        Customer.hasMany(models.Order, { onDelete: 'cascade', hooks: 'true' });
      },
    },
  });
  return Customer;
};

CustomerPhoto.js model:

module.exports = (sequelize, DataTypes) => {
  const CustomerPhoto = sequelize.define('CustomerPhoto', {
    customerPhotoID: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER,
      references: {
        model: 'Customer',
        key: 'customerID',
        deferrable: sequelize.Deferrable.INITIALLY_IMMEDIATE,
      },
    },
    filename: {
      type: DataTypes.STRING,
      validate: {
        len: [3, 15],
      },
    },
    filetype: {
      type: DataTypes.BLOB,
    },
  }, {
    classMethods: {
      associate(models) {
        CustomerPhoto.hasOne(models.Customer, { onDelete: 'cascade', hooks: 'true' })
      },
    },
  })
  return CustomerPhoto
}


export function fetchCustomers(req, res) {
  models.Customer.findAll({
    attributes: ['firstname', 'lastname', 'phone', 'email', 'filetype'],
    include: [{
      model: models.CustomerPhoto,
      // if true inner join otherwise left join
      required: true,
    }],
  }).then((result) => {
    res.json(result)
  }).catch((error) => {
    res.send(error)
  })
}

I get following response in Postman:

{
  "name": "SequelizeDatabaseError",
  "message": "column \"CustomerPhotoCustomerPhotoID\" does not exist",
  "parent": {
    "name": "error",
    "length": 128,
    "severity": "ERROR",
    "code": "42703",
    "position": "91",
    "file": "parse_relation.c",
    "line": "3183",
    "routine": "errorMissingColumn",
    "sql": "SELECT \"customerID\", \"firstname\", \"lastname\", \"phone\", \"email\", \"createdAt\", \"updatedAt\", \"CustomerPhotoCustomerPhotoID\", \"OrderOrderID\" FROM \"Customers\" AS \"Customer\" WHERE \"Customer\".\"customerID\" = '1';"
  },
  "original": {
    "name": "error",
    "length": 128,
    "severity": "ERROR",
    "code": "42703",
    "position": "91",
    "file": "parse_relation.c",
    "line": "3183",
    "routine": "errorMissingColumn",
    "sql": "SELECT \"customerID\", \"firstname\", \"lastname\", \"phone\", \"email\", \"createdAt\", \"updatedAt\", \"CustomerPhotoCustomerPhotoID\", \"OrderOrderID\" FROM \"Customers\" AS \"Customer\" WHERE \"Customer\".\"customerID\" = '1';"
  },
  "sql": "SELECT \"customerID\", \"firstname\", \"lastname\", \"phone\", \"email\", \"createdAt\", \"updatedAt\", \"CustomerPhotoCustomerPhotoID\", \"OrderOrderID\" FROM \"Customers\" AS \"Customer\" WHERE \"Customer\".\"customerID\" = '1';"
}

Ok There are some things around all here. First of all, you don't need to declare the 1:1 relation on both models. Just do it on one model and depends on how you do it.

You can use belongsTo or hasOne , both are use for 1:1 relations but work different.

belongsTo create the foreign key on the origin model, and hasOne on the destination model. Sequelize even recommend to use belongsTo on most of the cases. Here is the link.

"Even though it is called a HasOne association, for most 1:1 relations you usually want the BelongsTo association since BelongsTo will add the foreignKey on the source where hasOne will add on the target."

Now for your db schema, do you really need that the Primary key of CustomerPhoto has to be the foreign key refering Customer id Model? I recommend that you use regular primary key for CustomerPhotos and just make the reference on the Custommer Model that will create a column with the id association.

classMethods: {
      associate(models) {
        Customer.belongsTo(models.CustomerPhoto, { as : 'CustomerPhotoID', onDelete: 'cascade', hooks: 'true' });
        Customer.hasMany(models.Order, { onDelete: 'cascade', hooks: 'true' });
      },
    },

and on your query call the include like this:

models.Customer.findAll({
    attributes: ['firstname', 'lastname', 'phone', 'email', 'filetype'],
    include: [{
      model: models.CustomerPhoto,
      as : 'CustomerPhotoID',
      // if true inner join otherwise left join
      required: true,
    }],
  }).then

the "as" argumment will help sequelize find the relations sort of speaking.

@Ellebkey. Thanks for your answer. Yes, I realized before your post that I did this mistake with hasOne association. I modified my code and use belongTo instead. I also created foreign key in my Customer table but that didn't solve my problem.

The problem was in definition of my foreign key. Sequelize creates by default FK but I defined it by myself and didn't write it in association.

In the following example I decided to create customerPhotoID as FK in customer table. After that I added it into association. And it works! It was really bullshit mistake. I have to be more carefully next time.

module.exports = (sequelize, DataTypes) => {
  const Customer = sequelize.define('Customer', {
    customerID: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER,
    },
    customerPhotoID: {
      type: DataTypes.INTEGER,
      references: {
        model: 'Customers',
        key: 'customerID',
        deferrable: sequelize.Deferrable.INITIALLY_IMMEDIATE,
      },
    },
    firstname: {
      type: DataTypes.STRING,
      validate: {
        len: [3, 10],
      },
    },
    lastname: {
      type: DataTypes.STRING,
      validate: {
        len: [3, 10],
      },
    },
    phone: {
      type: DataTypes.STRING,
    },
    email: {
      type: DataTypes.STRING,
      unique: true,
      validate: {
        isEmail: true,
      },
    },
  }, {
    classMethods: {
      associate(models) {
        Customer.belongsTo(models.CustomerPhoto, { foreignKey: 'customerPhotoID', onDelete: 'cascade', hooks: 'true' });
        Customer.hasMany(models.Order, { onDelete: 'cascade', hooks: 'true' });
      },
    },
  });
  return Customer;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM