为什么 Sequelize“包含/属性”不适用于 findAll 和 findOne 请求

问题描述

我正在开发通过 Sequelize 与 MysqL 通信的 API。 我正在与用户及其出版物打交道。这工作得很好,我可以创建与 userId 相关联的用户和出版物。但现在我想获取发布用户的姓名,而不仅仅是 userId。

所以我使用我的模型“用户”和“出版物”之间的关联。我已经在我的续集模型和迁移文件中定义了关联,但是当我添加到我的控制器时:

            model : User,attributes: ['firstName']
        }],

我收到一个没有详细信息的错误 400。

如果我只写没有属性的“模型:用户”,它也不起作用,所以我猜它找不到模型用户

我按照 Sequelize 手册中解释的步骤操作,尝试与其他 github 示例进行比较,理论上似乎没问题。我花了两天时间尝试更改字符大小写,我尝试了 sequelize here 提供的许多选项,我删除了所有表格并重新创建了它们,但我现在正在兜圈子:-)

在我的迁移文件和模型下方。 感谢您的帮助!

出版物控制者

const models = require('../models');        // importation des modèles sequelize
const Publication = models.publication;
const User = models.user;

exports.getAllPublications = (req,res,next) => {
    Publication.findAll({
        include: [{
            model : User,order: [['createdAt','DESC']]
    })

    .then(        
        (publications) => {        
            res.status(200).json(publications);
        }
    ).catch(
        (error) => {
            res.status(400).json({   
                error: error
            });
        }
    );
};

用户续集模型

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize,DataTypes) => {
    class User extends Model {
      static associate(models) {
        models.User.hasMany(models.Publication)

      }
    };

  User.init({
    firstName: DataTypes.STRING,lastName: DataTypes.STRING,email: DataTypes.STRING,password: DataTypes.STRING,isAdmin: DataTypes.BOOLEAN
  },{
    sequelize,modelName: 'User',});
  return User;
};

出版续集模型

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize,DataTypes) => {
  class Publication extends Model {
    static associate(models) {
      models.Publication.belongsTo(models.User,{
        foreignKey: {
      
          allowNull: false,}
        });
    }
  };
  
  Publication.init({
    userId: DataTypes.INTEGER,title: DataTypes.STRING,content: DataTypes.STRING
  },modelName: 'Publication',});
  return Publication;
};

索引

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
require('dotenv').config()               // importation dotenv pour sécuriser passwords
const db = {};


let sequelize;
  sequelize = new Sequelize(
    process.env.DB_NAME,process.env.DB_USER,process.env.DB_PASSWORD,{

      host: process.env.DB_HOST,dialect: process.env.DB_DIALECT
  });

fs
  .readdirsync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname,file))(sequelize,Sequelize.DataTypes);


    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

db.user = require("./user.js")(sequelize,Sequelize)
db.publication = require("./publication.js")(sequelize,Sequelize)
db.comment = require("./comment.js")(sequelize,Sequelize)

module.exports = db

出版物迁移文件

'use strict';
module.exports = {
  up: async (queryInterface,Sequelize) => {
    await queryInterface.createTable('Publications',{
      id: {
        allowNull: false,autoIncrement: true,primaryKey: true,type: Sequelize.INTEGER
      },userId: {
        allowNull: false,type: Sequelize.INTEGER,references: {
          model: 'Users',key: 'id'
        }
      },title: {
        allowNull: false,type: Sequelize.STRING,len: [2,50]
      },content: {
        allowNull: false,type: Sequelize.STRING(1234),1000]
      },createdAt: {
        allowNull: false,type: Sequelize.DATE
      },updatedAt: {
        allowNull: false,type: Sequelize.DATE
      }
    });
  },down: async (queryInterface,Sequelize) => {
    await queryInterface.dropTable('Publications');
  }
};

用户创建控制器

exports.signup = (req,next) => {
    
  bcrypt.hash(req.body.password,10)    // On crypte le mot de passe (algorithme exécuté 10 fois) / asynchrone
  
        .then(hash => {   
          console.log(hash)
            const newUser = User.create({           // modèle sequelize
              lastName : req.body.lastName,firstName : req.body.firstName,email: req.body.email,password: hash,// On enregistre le mdp crypté plutôt que le mdp simple
             
              })
              

        .then((newUser )=> res.status(201).json({ message: 'created' })) // Requête traitée avec succès et création d’un document
        .catch(error => 
          res.status(400).json({ error })); // Bad Request*/
        
      })
    .catch(error => { 
      console.log("erreur 500")
      res.status(500).json({ error })}); // Erreur interne du serveur
  };

按照建议更改模型后的新 App.js

'use strict'

const express = require('express');         // importation application Express
require('dotenv').config()                  // importation dotenv pour sécuriser passwords
const MysqLTable = process.env.MysqLTABLE;
const MysqLUsername = process.env.MysqLUSERNAME;     
const MysqLPassword = process.env.MysqLPASSWORD; 
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,// 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

const bodyParser = require('body-parser');  // importation fonction body parser pour extraction objet json de la demande
const cors = require('cors');               // module CORS
const { Sequelize } = require('sequelize'); // importation application Sequelize pour communiquer avec MysqL
    
const userRoutes = require('./routes/user');   // Importation routeur users
const publicationRoutes = require('./routes/publication');   // Importation routeur posts
const commentRoutes = require('./routes/comment');   // Importation routeur posts

const Publication = require('./models/publication');
const User = require('./models/user')
Publication.belongsTo(User,{ Constraints: true,onDelete: 'CASCADE'});
User.hasMany(Publication);

const sequelize = new Sequelize(MysqLTable,MysqLUsername,MysqLPassword,{ // Connexion à la base de données MysqL
  host : 'localhost',dialect: 'MysqL'
  })

const app = express();      // application Express
app.use(bodyParser.json()); // Enregistrement body parser
app.use(cors());            // module CORS
app.use(limiter);           // rate limit

app.use((req,next) => {  // Ajout headers pour résoudre les erreurs CORS
    res.setHeader('Access-Control-Allow-Origin','*'); // accéder à notre API depuis n'importe quelle origine
    res.setHeader('Access-Control-Allow-Headers','Origin,X-Requested-With,Content,Accept,Content-Type,Authorization'); // ajouter les headers mentionnés aux requêtes envoyées vers notre API
    res.setHeader('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,PATCH,OPTIONS'); // envoyer des requêtes avec les méthodes mentionnées 
    next();
  });


app.use('/api/auth',userRoutes)      // Enregistrement routeur users
app.use('/api/publication',publicationRoutes)    // Enregistrement routeur publications
app.use('/api/comment',commentRoutes)    // Enregistrement routeur publications
module.exports = app;

解决方法

我从我们的意见交流中了解到,问题出在关联声明上。

就我个人而言,我不喜欢像您所做的那样定义关联(我没有说这是不正确的!),我个人喜欢这样做。

我在特定文件下(例如在 ./util/database.js 下)创建了一个 Sequelize 实例:

database.js

const Sequelize = require('sequelize');
const sequelize = new Sequelize('database_name','user_name','password',{
  dialect: 'mysql',storage: "./session.mysql",host: 'localhost'
});

module.exports = sequelize;

我这样定义我的模型:

用户模型

const Sequelize = require('sequelize');
const sequelize = require('../util/database');

const User = sequelize.define('user',{
  id: {
    type: Sequelize.INTEGER,autoIncrement: true,allowNull: false,primaryKey: true
  },firstName: Sequelize.STRING,lastName: Sequelize.STRING,email: Sequelize.STRING,password: Sequelize.STRING,isAdmin: Sequelize.BOOLEAN
});

module.exports = User;

出版模式

const Sequelize = require('sequelize');
const sequelize = require('../util/database');

const Publication = sequelize.define('publication',{
      id: {
        type: Sequelize.INTEGER,primaryKey: true
      },title: Sequelize.STRING,content: Sequelize.STRING
    });
    
    module.exports = Publication;

然后,我在 app.js 文件中定义关联:

Publication.belongsTo(User,{ Constraints: true,onDelete: 'CASCADE'});
User.hasMany(Publication);

为了确保新数据库的架构很好地集成,我使用带有sync属性的force方法(只是第一次,然后你应该删除这个属性并只使用同步方法)

// this will run on starting the server
sequelize
  .sync({ force: true }) // run it just in the first time after changing the database,this command will re-draw the database
  // .sync()
  .then(() => app.listen(8080))
  .catch(err => console.log(err));

最后,您可以保留为获取数据所做的工作,也可以执行以下操作:

User.findByPk(userId,{include: ['publications']})

//supposing that you have an instance of a User (user)
user.getPublications({})
user.getPublications({where: { id: publicationId }})