Rails 嵌套属性正确分配 parent_id 作为索引,但未分配其他属性
Rails Nested Attributes properly assigning parent_id as index, but isn't assigning additional attributes
我一直在努力实施属于我们现有网络应用模型之一的新模型。最终,我想构建一个嵌套表单。我知道表单和强参数可能有它自己的一系列问题,但我目前正在努力让模型按照我在 rails 控制台中预期的方式运行。
Rails 4.2.7,Postgres 数据库
更新 - 2016 年 10 月 3 日 - 仍在努力寻找正确的解决方案,但已进行一些更改
我们的工作是与学校和学区合作,这个特殊案例涉及调查以及如何将调查分配给学校和学区。到目前为止,已使用 SurveyAssignment 模型将调查分配给一个地区,并且一些后续逻辑假设一个地区的所有学校也 "assigned" 参与了调查。现在,我们希望能够向 SurveyAssignment 添加更多粒度,并在学校级别允许一些特殊性。
所以我创建了一个 SchoolSurveyAssignment 模型并开始准备好这些位。
相关型号信息如下:
class District < ActiveRecord::Base
...
has_many :schools, dependent: :destroy
has_many :survey_assignments, dependent: :destroy
...
end
class School
...
belongs_to :district
has_many :school_survey_assignments
has_many :survey_assignments, :through => :school_survey_assignments
...
end
class SurveyAssignment
belongs_to :district
belongs_to :survey
has_one :survey_version, through: :survey
has_many :school_survey_assignments, inverse_of: survey_assignment
has_many :schools, :through => :school_survey_assignments
accepts_nested_attributes_for :school_survey_assignments
attr_accessor :survey_group, :survey_version_type, :survey_version_id, :school_survey_assignments_attributes
validates :survey_id, presence: true
end
class SchoolSurveyAssignment
belongs_to :survey_assignment, inverse_of: :school_survey_assignments
belongs_to :school
attr_accessor :school_id, :survey_assignment_id, :grades_affected, :ulc_affected
validates_presence_of :survey_assignment
validates :school_id, presence: true, uniqueness: {scope: :survey_assignment_id}
end
相关控制器代码:
class SurveyAssignmentsController < ApplicationController
before_action :set_district
before_action :set_survey_assignment, only: [:show, :edit, :update, :destroy]
respond_to :html, :json, :js
def new
@new_survey_assignment = SurveyAssignment.new()
@district.schools.each do |school|
@new_survey_assignment.school_survey_assignments.build(school_id: school.id)
end
end
def create
@survey_assignment = SurveyAssignment.new(survey_assignment_params)
if @survey_assignment.save
flash[:notice] = "Survey successfully assigned to #{@district.name}"
else
flash[:alert] = "There was a problem assigning this survey to #{@district.name}"
end
redirect_to district_survey_assignments_path(@district)
end
def survey_assignment_params
params.require(:survey_assignment).permit(:survey_id, :status, :survey_version_id, school_survey_assignments_attributes: [:id, :survey_assignment_id, :school_id, grades_affected: [], ulc_affected: []]).tap do |p|
p[:district_id] = @district.id
p[:school_year] = session[:selected_year]
end
end
def set_district
@district = District.find(params[:district_id])
end
这里是相关的架构信息:
create_table "school_survey_assignments", force: :cascade do |t|
t.integer "survey_assignment_id"
t.integer "school_id"
t.integer "grades_affected", default: [], array: true
t.string "ulc_affected", default: [], array: true
end
add_index "school_survey_assignments", ["school_id"], name: "index_school_survey_assignments_on_school_id", using: :btree
add_index "school_survey_assignments", ["survey_assignment_id"], name: "index_school_survey_assignments_on_survey_assignment_id", using: :btree
create_table "survey_assignments", force: :cascade do |t|
t.integer "district_id"
t.integer "survey_id"
t.integer "status"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "school_year"
t.integer "last_response_status_id"
end
add_index "survey_assignments", ["district_id"], name: "index_survey_assignments_on_district_id", using: :btree
一旦这些就位,我进入我的 rails 控制台并尝试以下操作:
2.3.1 :002 > sa1 = SurveyAssignment.create(district_id: 3, survey_id: 508, school_year: 2017)
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "survey_assignments" ("district_id", "survey_id", "school_year", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["district_id", 3], ["survey_id", 508], ["school_year", 2017], ["created_at", "2016-09-30 21:30:20.205144"], ["updated_at", "2016-09-30 21:30:20.205144"]]
(7.2ms) COMMIT
=> #<SurveyAssignment id: 369, district_id: 3, survey_id: 508, status: nil, created_at: "2016-09-30 21:30:20", updated_at: "2016-09-30 21:30:20", school_year: 2017, last_response_status_id: nil>
2.3.1 :003 > sa2 = SurveyAssignment.create(district_id: 3, survey_id: 508, school_year: 2017)
(0.3ms) BEGIN
SQL (0.4ms) INSERT INTO "survey_assignments" ("district_id", "survey_id", "school_year", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["district_id", 3], ["survey_id", 508], ["school_year", 2017], ["created_at", "2016-09-30 21:30:30.701197"], ["updated_at", "2016-09-30 21:30:30.701197"]]
(0.5ms) COMMIT
=> #<SurveyAssignment id: 370, district_id: 3, survey_id: 508, status: nil, created_at: "2016-09-30 21:30:30", updated_at: "2016-09-30 21:30:30", school_year: 2017, last_response_status_id: nil>
现在,我已经成功创建了两个调查任务。我现在要根据 sa1 创建两个学校调查作业:
2.3.1 :004 > [{school_id: 5}, {school_id: 6}].each do |ssa|
2.3.1 :005 > sa1.school_survey_assignments.create(ssa)
2.3.1 :006?> end
(0.2ms) BEGIN
SchoolSurveyAssignment Exists (2.4ms) SELECT 1 AS one FROM "school_survey_assignments" WHERE ("school_survey_assignments"."school_id" = 5 AND "school_survey_assignments"."survey_assignment_id" = 369) LIMIT 1
SQL (0.4ms) INSERT INTO "school_survey_assignments" ("survey_assignment_id") VALUES () RETURNING "id" [["survey_assignment_id", 369]]
(6.4ms) COMMIT
(0.6ms) BEGIN
SchoolSurveyAssignment Exists (0.4ms) SELECT 1 AS one FROM "school_survey_assignments" WHERE ("school_survey_assignments"."school_id" = 6 AND "school_survey_assignments"."survey_assignment_id" = 369) LIMIT 1
SQL (0.3ms) INSERT INTO "school_survey_assignments" ("survey_assignment_id") VALUES () RETURNING "id" [["survey_assignment_id", 369]]
(0.4ms) COMMIT
=> [{:school_id=>5}, {:school_id=>6}]
2.3.1 :007 > sa1.save
(0.3ms) BEGIN
(0.4ms) COMMIT
=> true
现在,我似乎已经成功创建了两个 SchoolSurveyAssignments survey_assignment_id = 369 和 school_ids = 5 和 6
2.3.1 :008 > sa1.school_survey_assignments
SchoolSurveyAssignment Load (0.3ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."survey_assignment_id" = [["survey_assignment_id", 369]]
=> #<ActiveRecord::Associations::CollectionProxy [#<SchoolSurveyAssignment id: 5, survey_assignment_id: 369, school_id: nil, grades_affected: [], ulc_affected: []>, #<SchoolSurveyAssignment id: 6, survey_assignment_id: 369, school_id: nil, grades_affected: [], ulc_affected: []>]>
正如您从 ActivRecord::Associations::CollectionProxy 中看到的那样,两个 SchoolSurveyAssignments 都已创建,survey_assignment_id:369,但 school_id 为零。这似乎令人不安
- 忽略传递给创建函数的参数,并且
- 忽略 school_id
的验证
我不明白的另一项是:
2.3.1 :009 > SchoolSurveyAssignment.find(5).survey_assignment_id
SchoolSurveyAssignment Load (0.6ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."id" = LIMIT 1 [["id", 5]]
=> nil
2.3.1 :011 > SchoolSurveyAssignment.find(5).survey_assignment.id
SchoolSurveyAssignment Load (0.3ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."id" = LIMIT 1 [["id", 5]]
SurveyAssignment Load (0.4ms) SELECT "survey_assignments".* FROM "survey_assignments" WHERE "survey_assignments"."id" = LIMIT 1 [["id", 369]]
=> 369
调用 .survey_assignment_id 应该 return SchoolSurveyAssignment 上的属性并给出 369..survey_assignment.id 只是获取父对象的 ID。我希望两者 return 具有相同的值,但一个 return 为零。
最终用例是制作一个 SurveyAssignment 表单,让用户可以为新的 SurveyAssignment 设置属性,还可以为 X 数量的 SchoolSurveyAssignments(基于一个地区的学校数量;从 2 到 15 不等)设置属性).一旦我更好地掌握了这些模型的交互方式,我就有信心执行这个目标,但我所看到的行为对我来说没有意义,我希望在实施这些相关模型时能找到一些清晰的信息。我觉得我在回答这个问题时犹豫不决,但遗漏了一个关键细节。
谢谢,
亚历克斯
第一个问题,School和SurveyAssignment互不认识,school_id变为nil。在您的应用程序中,这些模型具有间接的 m-to-n 关联,那么通过模型之间的关联使用 has_many 怎么样?
在学校模型中,添加以下内容:
has_many :survey_assignments, :through => :school_survey_assignments
在 SurveyAssignments 模型中,添加以下内容:
has_many :schools, :through => :school_survey_assignments
对于最后一个问题,两个代码似乎是相同的..
尝试删除您的 attr_accessor
行代码。 attr_accessor
不应该用于保存在数据库中的属性,它可能会扰乱 ActiveRecord 默认已经提供的方法,导致这些属性无法正确保存
class SurveyAssignment
belongs_to :district
belongs_to :survey
has_one :survey_version, through: :survey
has_many :school_survey_assignments, inverse_of: survey_assignment
has_many :schools, :through => :school_survey_assignments
accepts_nested_attributes_for :school_survey_assignments
validates :survey_id, presence: true
end
class SchoolSurveyAssignment
belongs_to :survey_assignment, inverse_of: :school_survey_assignments
belongs_to :school
validates_presence_of :survey_assignment
validates :school_id, presence: true, uniqueness: {scope: :survey_assignment_id}
end
我一直在努力实施属于我们现有网络应用模型之一的新模型。最终,我想构建一个嵌套表单。我知道表单和强参数可能有它自己的一系列问题,但我目前正在努力让模型按照我在 rails 控制台中预期的方式运行。
Rails 4.2.7,Postgres 数据库
更新 - 2016 年 10 月 3 日 - 仍在努力寻找正确的解决方案,但已进行一些更改
我们的工作是与学校和学区合作,这个特殊案例涉及调查以及如何将调查分配给学校和学区。到目前为止,已使用 SurveyAssignment 模型将调查分配给一个地区,并且一些后续逻辑假设一个地区的所有学校也 "assigned" 参与了调查。现在,我们希望能够向 SurveyAssignment 添加更多粒度,并在学校级别允许一些特殊性。
所以我创建了一个 SchoolSurveyAssignment 模型并开始准备好这些位。
相关型号信息如下:
class District < ActiveRecord::Base
...
has_many :schools, dependent: :destroy
has_many :survey_assignments, dependent: :destroy
...
end
class School
...
belongs_to :district
has_many :school_survey_assignments
has_many :survey_assignments, :through => :school_survey_assignments
...
end
class SurveyAssignment
belongs_to :district
belongs_to :survey
has_one :survey_version, through: :survey
has_many :school_survey_assignments, inverse_of: survey_assignment
has_many :schools, :through => :school_survey_assignments
accepts_nested_attributes_for :school_survey_assignments
attr_accessor :survey_group, :survey_version_type, :survey_version_id, :school_survey_assignments_attributes
validates :survey_id, presence: true
end
class SchoolSurveyAssignment
belongs_to :survey_assignment, inverse_of: :school_survey_assignments
belongs_to :school
attr_accessor :school_id, :survey_assignment_id, :grades_affected, :ulc_affected
validates_presence_of :survey_assignment
validates :school_id, presence: true, uniqueness: {scope: :survey_assignment_id}
end
相关控制器代码:
class SurveyAssignmentsController < ApplicationController
before_action :set_district
before_action :set_survey_assignment, only: [:show, :edit, :update, :destroy]
respond_to :html, :json, :js
def new
@new_survey_assignment = SurveyAssignment.new()
@district.schools.each do |school|
@new_survey_assignment.school_survey_assignments.build(school_id: school.id)
end
end
def create
@survey_assignment = SurveyAssignment.new(survey_assignment_params)
if @survey_assignment.save
flash[:notice] = "Survey successfully assigned to #{@district.name}"
else
flash[:alert] = "There was a problem assigning this survey to #{@district.name}"
end
redirect_to district_survey_assignments_path(@district)
end
def survey_assignment_params
params.require(:survey_assignment).permit(:survey_id, :status, :survey_version_id, school_survey_assignments_attributes: [:id, :survey_assignment_id, :school_id, grades_affected: [], ulc_affected: []]).tap do |p|
p[:district_id] = @district.id
p[:school_year] = session[:selected_year]
end
end
def set_district
@district = District.find(params[:district_id])
end
这里是相关的架构信息:
create_table "school_survey_assignments", force: :cascade do |t|
t.integer "survey_assignment_id"
t.integer "school_id"
t.integer "grades_affected", default: [], array: true
t.string "ulc_affected", default: [], array: true
end
add_index "school_survey_assignments", ["school_id"], name: "index_school_survey_assignments_on_school_id", using: :btree
add_index "school_survey_assignments", ["survey_assignment_id"], name: "index_school_survey_assignments_on_survey_assignment_id", using: :btree
create_table "survey_assignments", force: :cascade do |t|
t.integer "district_id"
t.integer "survey_id"
t.integer "status"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "school_year"
t.integer "last_response_status_id"
end
add_index "survey_assignments", ["district_id"], name: "index_survey_assignments_on_district_id", using: :btree
一旦这些就位,我进入我的 rails 控制台并尝试以下操作:
2.3.1 :002 > sa1 = SurveyAssignment.create(district_id: 3, survey_id: 508, school_year: 2017)
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "survey_assignments" ("district_id", "survey_id", "school_year", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["district_id", 3], ["survey_id", 508], ["school_year", 2017], ["created_at", "2016-09-30 21:30:20.205144"], ["updated_at", "2016-09-30 21:30:20.205144"]]
(7.2ms) COMMIT
=> #<SurveyAssignment id: 369, district_id: 3, survey_id: 508, status: nil, created_at: "2016-09-30 21:30:20", updated_at: "2016-09-30 21:30:20", school_year: 2017, last_response_status_id: nil>
2.3.1 :003 > sa2 = SurveyAssignment.create(district_id: 3, survey_id: 508, school_year: 2017)
(0.3ms) BEGIN
SQL (0.4ms) INSERT INTO "survey_assignments" ("district_id", "survey_id", "school_year", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["district_id", 3], ["survey_id", 508], ["school_year", 2017], ["created_at", "2016-09-30 21:30:30.701197"], ["updated_at", "2016-09-30 21:30:30.701197"]]
(0.5ms) COMMIT
=> #<SurveyAssignment id: 370, district_id: 3, survey_id: 508, status: nil, created_at: "2016-09-30 21:30:30", updated_at: "2016-09-30 21:30:30", school_year: 2017, last_response_status_id: nil>
现在,我已经成功创建了两个调查任务。我现在要根据 sa1 创建两个学校调查作业:
2.3.1 :004 > [{school_id: 5}, {school_id: 6}].each do |ssa|
2.3.1 :005 > sa1.school_survey_assignments.create(ssa)
2.3.1 :006?> end
(0.2ms) BEGIN
SchoolSurveyAssignment Exists (2.4ms) SELECT 1 AS one FROM "school_survey_assignments" WHERE ("school_survey_assignments"."school_id" = 5 AND "school_survey_assignments"."survey_assignment_id" = 369) LIMIT 1
SQL (0.4ms) INSERT INTO "school_survey_assignments" ("survey_assignment_id") VALUES () RETURNING "id" [["survey_assignment_id", 369]]
(6.4ms) COMMIT
(0.6ms) BEGIN
SchoolSurveyAssignment Exists (0.4ms) SELECT 1 AS one FROM "school_survey_assignments" WHERE ("school_survey_assignments"."school_id" = 6 AND "school_survey_assignments"."survey_assignment_id" = 369) LIMIT 1
SQL (0.3ms) INSERT INTO "school_survey_assignments" ("survey_assignment_id") VALUES () RETURNING "id" [["survey_assignment_id", 369]]
(0.4ms) COMMIT
=> [{:school_id=>5}, {:school_id=>6}]
2.3.1 :007 > sa1.save
(0.3ms) BEGIN
(0.4ms) COMMIT
=> true
现在,我似乎已经成功创建了两个 SchoolSurveyAssignments survey_assignment_id = 369 和 school_ids = 5 和 6
2.3.1 :008 > sa1.school_survey_assignments
SchoolSurveyAssignment Load (0.3ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."survey_assignment_id" = [["survey_assignment_id", 369]]
=> #<ActiveRecord::Associations::CollectionProxy [#<SchoolSurveyAssignment id: 5, survey_assignment_id: 369, school_id: nil, grades_affected: [], ulc_affected: []>, #<SchoolSurveyAssignment id: 6, survey_assignment_id: 369, school_id: nil, grades_affected: [], ulc_affected: []>]>
正如您从 ActivRecord::Associations::CollectionProxy 中看到的那样,两个 SchoolSurveyAssignments 都已创建,survey_assignment_id:369,但 school_id 为零。这似乎令人不安
- 忽略传递给创建函数的参数,并且
- 忽略 school_id 的验证
我不明白的另一项是:
2.3.1 :009 > SchoolSurveyAssignment.find(5).survey_assignment_id
SchoolSurveyAssignment Load (0.6ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."id" = LIMIT 1 [["id", 5]]
=> nil
2.3.1 :011 > SchoolSurveyAssignment.find(5).survey_assignment.id
SchoolSurveyAssignment Load (0.3ms) SELECT "school_survey_assignments".* FROM "school_survey_assignments" WHERE "school_survey_assignments"."id" = LIMIT 1 [["id", 5]]
SurveyAssignment Load (0.4ms) SELECT "survey_assignments".* FROM "survey_assignments" WHERE "survey_assignments"."id" = LIMIT 1 [["id", 369]]
=> 369
调用 .survey_assignment_id 应该 return SchoolSurveyAssignment 上的属性并给出 369..survey_assignment.id 只是获取父对象的 ID。我希望两者 return 具有相同的值,但一个 return 为零。
最终用例是制作一个 SurveyAssignment 表单,让用户可以为新的 SurveyAssignment 设置属性,还可以为 X 数量的 SchoolSurveyAssignments(基于一个地区的学校数量;从 2 到 15 不等)设置属性).一旦我更好地掌握了这些模型的交互方式,我就有信心执行这个目标,但我所看到的行为对我来说没有意义,我希望在实施这些相关模型时能找到一些清晰的信息。我觉得我在回答这个问题时犹豫不决,但遗漏了一个关键细节。
谢谢,
亚历克斯
第一个问题,School和SurveyAssignment互不认识,school_id变为nil。在您的应用程序中,这些模型具有间接的 m-to-n 关联,那么通过模型之间的关联使用 has_many 怎么样?
在学校模型中,添加以下内容:
has_many :survey_assignments, :through => :school_survey_assignments
在 SurveyAssignments 模型中,添加以下内容:
has_many :schools, :through => :school_survey_assignments
对于最后一个问题,两个代码似乎是相同的..
尝试删除您的 attr_accessor
行代码。 attr_accessor
不应该用于保存在数据库中的属性,它可能会扰乱 ActiveRecord 默认已经提供的方法,导致这些属性无法正确保存
class SurveyAssignment
belongs_to :district
belongs_to :survey
has_one :survey_version, through: :survey
has_many :school_survey_assignments, inverse_of: survey_assignment
has_many :schools, :through => :school_survey_assignments
accepts_nested_attributes_for :school_survey_assignments
validates :survey_id, presence: true
end
class SchoolSurveyAssignment
belongs_to :survey_assignment, inverse_of: :school_survey_assignments
belongs_to :school
validates_presence_of :survey_assignment
validates :school_id, presence: true, uniqueness: {scope: :survey_assignment_id}
end