首页 > 解决方案 > accept_nested_attributes_for 与 Rails 中的关系数据库逻辑相矛盾

问题描述

Ruby on Rails 模型逻辑与关系数据库逻辑冲突。

在 Ruby on Rails 中,拥有 , 的模型belongs_to将在数据库中拥有外键。所以,模型:

class Student < ApplicationRecord
    belongs_to :university
end

class University < ApplicationRecord
    has_many :university
end

将有迁移文件:

class CreateStudents < ActiveRecord::Migration[6.0]
    t.string :name
    t.references :university, foreign_key: true
end

class CreateUniversities < ActiveRecord::Migration[6.0]
    t.string :name
end

而且,从逻辑上讲,学生将在数据库中拥有 University foreign_key,如下所示:

Student
-id int (PK)
-name varchar
-University_id int (FK)

University
-id int (PK)
-name varchar

到目前为止这么好

当我想accepts_nested_attributes_for在学生模型中引用_form.html.erb. 为了使用它,宿​​主模型(在本例中为 Student)必须具有用于引用表的 ahas_one而不是 a 。belongs_to所以它会变成:

class Student < ApplicationRecord
    has_one :university
    accepts_nested_attributes_for :university
end

请注意,为了accepts_nested_attributes_for用于引用大学,学生模型必须将其替换belongs_to :university为 a has_one :university,而大学将有 a belongs_to :student,而不是 a has_many :student

因此,在迁移文件中,Student 将失去对 University 的引用,而后者将引用 Student,如下所示:

class CreateStudents < ActiveRecord::Migration[6.0]
    t.string :name
end

class CreateUniversities < ActiveRecord::Migration[6.0]
    t.string :name
    t.references :student, foreign_key: true
end

并且数据库将强制为:

Student
-id int (PK)
-name varchar

University
-id int (PK)
-name varchar
-Student_id int (FK)

这是错误的,因为大学与学生之间应该存在一对多的关系,而不是相反。

那么,有没有一种方法可以使用accepts_nested_attributes_for,而不必通过强制反转模型中的关系来搞乱关系数据库逻辑?

标签: ruby-on-railsrubyrelational-database

解决方案


accepts_nested_attributes_for嵌套属性允许您通过父项将属性保存在关联记录上。

因此,您可能对您想成为哪个父母略有混淆。如果您使学生成为具有大学嵌套属性的父级,则您不会引用大学,而是实质上创建新的大学记录或使用您在学生表单中使用的嵌套属性更新现有记录。

One-to-one

Consider a Member model that has one Avatar:

class Member < ActiveRecord::Base
  has_one :avatar
  accepts_nested_attributes_for :avatar
end

Enabling nested attributes on a one-to-one association allows you to create the member and avatar in one go:

params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }
member = Member.create(params[:member])
member.avatar.id # => 2
member.avatar.icon # => 'smiling'

因此,您只会为使用这些嵌套属性的每个学生更新/创建新的大学记录。

这是一对多:

One-to-many

Consider a member that has a number of posts:

class Member < ActiveRecord::Base
  has_many :posts
  accepts_nested_attributes_for :posts
end

You can now set or update attributes on the associated posts through an attribute hash for a member: include the key :posts_attributes with an array of hashes of post attributes as a value.

For each hash that does not have an id key a new record will be instantiated, unless the hash also contains a _destroy key that evaluates to true.

params = { member: {
  name: 'joe', posts_attributes: [
    { title: 'Kari, the awesome Ruby documentation browser!' },
    { title: 'The egalitarian assumption of the modern citizen' },
    { title: '', _destroy: '1' } # this will be ignored
  ]
}}

member = Member.create(params[:member])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'

您还可以在上面的一对多示例中看​​到这一点。您是否希望在发布学生记录时更改/创建大学记录?

如果您只是在寻找参考资料,那么您在第一个示例中基本上已经有了它,并且可以稍微改变您的路线以让学生被大学参考

然后,一旦嵌套了路由,您就可以对表单部分做一些工作。这是一个正在这样做的人

因此,也许如果我离开这里,请描述为什么在创建/更新学生时要创建/更新大学。如果那是你想要做的。

这将有助于其他人了解您的上下文,并可能帮助其他人了解您使用嵌套属性的意图。

例如,添加您的部分表单并解释您的目标。

编辑: 作为一个平底船,你也许可以看看有并且属于许多协会。 本教程讨论具有多对多关联的嵌套属性。但是,由于我不确定您在它之后可能会或可能不会有帮助。

另一个平底船听起来像是您希望在更新学生的大学记录时更改它们。所以你会有学生属于大学和大学有很多学生但也有很多大学记录并且学生有很多/有一个大学记录和记录属于两者。又名: 这个

然后你可以让学生接受大学记录的嵌套属性。


推荐阅读