【问题标题】:SequelizeEagerLoadingError: (parent) is not associated to (child)!SequelizeEagerLoadingError:(父)未关联到(子)!
【发布时间】:2019-08-06 01:07:34
【问题描述】:

我正在使用 sequelize 构建一个应用程序。我目前有 3 张桌子;用户、游览和位置。位置与巡回赛具有 n:1 的关系。 Tour 与用户具有 n:1 的关系。

没有用户关联,其他两个表工作正常。一旦我添加了用户关联(并且我尝试通过迁移和删除然后重新创建我的整个数据库来做到这一点),我得到一个 SequelizeEagerLoadingError: Location is not associated with Tour!

这是我的模型:

module.exports = function(sequelize, DataTypes) {
  var Location = sequelize.define("Location", {
    title: {
      type: DataTypes.STRING,
      allowNull: false
    },
    description: {
      type: DataTypes.TEXT,
      allowNull: false,
      validate: {
        len: [500]
      }
    },
    address: {
      type: DataTypes.TEXT,
      allowNull: false
    }
  });

  Location.associate = function(models) {
    Location.belongsTo(models.Tour, {
      onDelete: "cascade"
    });
  };

  return Location;
};

module.exports = function(sequelize, DataTypes) {
  var Tour = sequelize.define("Tour", {
    title: {
      type: DataTypes.STRING,
      allowNull: false
        },
    description: {
      type: DataTypes.TEXT,
      allowNull: false,
      validate: {
        len: [1, 1000]
      }
    },
    neighborhood: {
      type: DataTypes.STRING,
      allowNull: false
    },
    URL: {
      type: DataTypes.TEXT,
      allowNull: false,
      validate: {
        len: [1, 1000]
      }
    },
    numberOfStops: DataTypes.INTEGER,
    duration: {
      type: DataTypes.INTEGER,
      allowNull: false
    },
    tags: DataTypes.STRING
  });

    Tour.associate = function(models) {
    Tour.hasMany(models.Location);
  };

  Tour.associate = function(models) {
    Tour.belongsTo(models.User);
  };

  return Tour;
};
 

var bcrypt = require("bcrypt-nodejs");
module.exports = function(sequelize, DataTypes) {
  var User = sequelize.define("User", {
    name: {
      type: DataTypes.STRING,
      allowNull: false
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
      validate: {
        isEmail: true
      }
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    }
  });
  User.prototype.validPassword = function(password) {
    return bcrypt.compareSync(password, this.password);
  };

  User.hook("beforeCreate", function(user) {
    user.password = bcrypt.hashSync(
      user.password,
      bcrypt.genSaltSync(10),
      null
    );
  });

  User.associate = function(models) {
    User.hasMany(models.Tour);
  };

  return User;
};

这里是包含语句失败的地方,以及我们用 tourId 建立到该位置的链接:

app.get("/tour/:id", function(req, res) {
    db.Tour.findOne({
      where: { id: req.params.id },
      include: [db.Location]
    }).then(function(tour) {
      res.render("tour", {
        tour: tour
      });
    });
  });
  
  
  
  
   

var API = {
  saveTour: function(tour) {
    return $.ajax({
      headers: {
        "Content-Type": "application/json"
      },
      type: "POST",
      url: "api/tours",
      data: JSON.stringify(tour)
    });
  },
  saveLocations: function(locations) {
    return $.ajax({
      headers: {
        "Content-Type": "application/json"
      },
      type: "POST",
      url: "api/locations",
      data: JSON.stringify(locations)
    });
  },
  getUserId: function() {
    return $.ajax({
      type: "GET",
      url: "api/user_data"
    });
  }
};

var tour = {
    Users: thisUser.getUserId(),
    title: title,
    description: description,
    neighborhood: neighborhood,
    URL: URL,
    duration: duration,
    tags: tags
  };

  // console.log(tour);

  if (!errors.length) {
    // Post our tour to the Tours table, then reveal the form and set our local tour object.
    API.saveTour(tour).then(function(tour) {
      document.getElementById("submit-tour").remove();
      document.getElementById("tourstopssection").style.display = "block";
      thisTour.setId(tour.id);
    });
  }
}

// Function takes in the newly created tour object, grabs DOM values for each.
function addTourLocations(e) {
  e.preventDefault();
  // Grab and process all of our tour stops.
  var locationElements = document.getElementsByClassName("tourstop");
  var areStopErrors = false;
  var locations = [];

  // Loop over every location element on the DOM.
  for (var j = 0; j < locationElements.length; j++) {
    var children = locationElements[j].children;

    // Initialize this location with the tour id; we'll pass in data...
    var thisLocation = {
      TourId: thisTour.getId()
    };

    // ... by looping over the DOM children and grabbing their form values.
    for (var k = 0; k < children.length; k++) {
      if (
        children[k].classList.value.includes("stoptitle") &&
        children[k].value
      ) {
        var stopTitle = children[k].value;
        thisLocation.title = stopTitle;
      }

      if (
        children[k].classList.value.includes("stopaddress") &&
        children[k].value
      ) {
        var stopAddress = children[k].value;
        thisLocation.address = stopAddress;
      }

      if (
        children[k].classList.value.includes("stopdescription") &&
        children[k].value
      ) {
        var stopDescription = children[k].value;
        thisLocation.description = stopDescription;
      }
    }

    // Push this location into our locations array.
    locations.push(thisLocation);

最后,app/db 是这样同步的:

require("dotenv").config();
var express = require("express");
var session = require("express-session");
var exphbs = require("express-handlebars");
var helpers = require("./lib/helpers");

var db = require("./models");
var passport = require("./config/passport");

var app = express();
var PORT = process.env.PORT || 3000;

// Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static("public"));

var hbs = exphbs.create({
  defaultLayout: "main",
  helpers: helpers // Require our custom Handlebars helpers.
});

//Sessions are used to keep track of our user's login status
app.use(
  session({ secret: "keyboard cat", resave: true, saveUninitialized: true })
);
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
  res.locals.user = req.user; // Set a local variable for our user.
  next();
});

// Handlebars
app.engine("handlebars", hbs.engine);
app.set("view engine", "handlebars");

// Routes
require("./routes/apiRoutes")(app);
require("./routes/htmlRoutes")(app);

var syncOptions = { force: false };

// If running a test, set syncOptions.force to true
// clearing the `testdb`
if (process.env.NODE_ENV === "test") {
  syncOptions.force = true;
}

// Starting the server, syncing our models ------------------------------------/
db.sequelize.sync(syncOptions).then(function() {
  app.listen(PORT, function() {
    console.log(
      "==> ????  Listening on port %s. Visit http://localhost:%s/ in your browser.",
      PORT,
      PORT
    );
  });
});

module.exports = app;

我已经用谷歌搜索了四天....帮助!

【问题讨论】:

  • 异常发生在哪一行,消息是什么?您的第四个代码块似乎至少从两个位置粘贴在一起。后半部分应该是什么,它如何与 app.get 定义结合在一起?
  • 现在只是一个猜测,您可能还需要定义从 Tour 到 Location 的反向关系,即一个 Tour 有多个 Location。 “hasMany”,就像您为 User 和 Tour 所做的那样。
  • @Christoph - 谢谢!实际上,我确实在 Tour 模型中建立了 hasMany,但昨晚我在复制/粘贴工作中忽略了它。我更新了模型并为最后一个块添加了一些清晰度 - 基本上第一个(获取路径)是引发错误的地方,就在“包含:db.Location”行。第二部分是我们如何确定 API 路由,第三部分是我们如何根据客户端输入实际构建 Location 和 Tours。

标签: mysql node.js express sequelize.js sequelize-cli


【解决方案1】:

尝试将此添加到您的关联中,还有为什么您在 Tour 上定义了两倍的关联功能?

module.exports = function(sequelize, DataTypes) {
  var Location = sequelize.define("Location", {
    //
  });

  Location.associate = function(models) {
    Location.belongsTo(models.Tour, { as:'Tour', foreignKey:'tourId', onDelete: "cascade"});
  };

  return Location;
};

module.exports = function(sequelize, DataTypes) {
  var Tour = sequelize.define("Tour", {
    //
  });

  Tour.associate = function(models) {
    Tour.hasMany(models.Location, { as: 'Locations', foreignKey: 'tourId'});
    Tour.belongsTo(models.User, { as: 'User', foreignKey: 'userId' });
  };


  return Tour;
};

module.exports = function(sequelize, DataTypes) {
  var User = sequelize.define("User", {
    //
  });

  User.associate = function(models) {
    User.hasMany(models.Tour, {as: 'Tours', foreignKey: 'userId'});
  };

  return User;
};

并在查询中添加相同的内容。

db.Tour.findOne({
  where: { id: req.params.id },
  include: [{ 
    model: db.Location,
    as: 'Locations'
  }]
}).then(function(tour) {
  res.render("tour", {
    tour: tour
  });
});

【讨论】:

  • 非常感谢!为了回答你的问题,我这样做是因为我对此仍然很陌生,而且我什至没有想到我可以将这两个陈述结合起来......但现在很明显!我确实注意到外键和'as'是由sequelize自动分配的。在这种情况下,在模型中显式调用它会有所不同吗?
【解决方案2】:

我想通了——我在 tour 模型上定义了两次关联的事实打破了一切。一旦我如上所述将它们组合在一起,一切都完美无缺!

还有一点需要注意 - sequelize 会自动分配外键和别名,所以我把这部分省略了。

【讨论】:

  • 看来 ellebkey 找到了解决方案,即双重关联。然后请按照 Stack Overflow 上的惯例将她的答案标记为正确答案。
猜你喜欢
  • 2020-09-02
  • 2019-07-29
  • 1970-01-01
  • 2018-12-18
  • 2017-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多