J'ai travaillé sur l'implémentation d'un nouveau modèle appartenant à l'un de nos modèles d'applications Web. Finalement, je veux construire un formulaire imbriqué. Je comprends que la forme, et les params forts peuvent avoir sa propre série de problèmes, mais je me bats actuellement pour obtenir les modèles à se comporter comme je l'attendrais dans la console rails.Rails imbriqués Attributs affectant correctement parent_id comme index, mais n'attribuant pas d'attributs supplémentaires
Rails 4.2.7, DB Postgres
MISE À JOUR - 03/10/16 - Toujours essayer de trouver la bonne solution, mais ont fait quelques changements
Notre travail est avec les écoles et, ce un cas particulier traite des enquêtes et de la manière dont une enquête est assignée à une école et à un district. Jusqu'à présent, une enquête a été assignée à un district avec un modèle SurveyAssignment, et une logique descendante supposait que toutes les écoles d'un district étaient également «affectées» à l'enquête. Maintenant, nous voulons être en mesure d'ajouter plus de granularité à la SurveyAssignment et permettre une certaine spécificité au niveau de l'école. J'ai donc créé un modèle SchoolSurveyAssignment et j'ai commencé à mettre les bits en place.
Voici les informations de modèle pertinent:
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
Code contrôleur pertinent:
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
Voici les informations de schéma correspondant:
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
Une fois que ceux-ci étaient en place, je est entré dans la console de mon rail et a tenté la chose suivante:
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 ($1, $2, $3, $4, $5) 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 ($1, $2, $3, $4, $5) 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>
Maintenant, j'ai réussi à créer deux affectations d'enquête. Je vais maintenant créer deux missions d'enquêtes scolaires hors de 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 ($1) 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 ($1) 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
Maintenant, il semble que je l'ai créé avec succès deux SchoolSurveyAssignments avec survey_assignment_id = 369 et school_ids = 5 et 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" = $1 [["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: []>]>
Comme vous pouvez le voir dans ActivRecord :: Associations :: CollectionProxy, les deux SchoolSurveyAssignments ont été créés avec survey_assignment_id: 369, mais avec un school_id nul. Ce qui est troublant car il semble être
- Ignorer les paramètres étant passés dans la fonction de création et
- ignorant la validation des school_id
Un autre élément que je ne comprends pas est la suivante :
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" = $1 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" = $1 LIMIT 1 [["id", 5]]
SurveyAssignment Load (0.4ms) SELECT "survey_assignments".* FROM "survey_assignments" WHERE "survey_assignments"."id" = $1 LIMIT 1 [["id", 369]]
=> 369
appel .survey_assignment_id doit retourner l'attribut sur le SchoolSurveyAssignment et donner 369. .survey_assignment.id est tout simplement saisir bing l'ID de l'objet parent. Je m'attendrais à ce que les deux retournent la même valeur, mais on retourne zéro.Le cas d'utilisation final consiste à créer un formulaire SurveyAssignment qui permet à l'utilisateur de définir les attributs d'un nouvel objet SurveyAssignment et de définir les attributs du nombre X de SchoolSurveyAssignments (basé sur le nombre d'écoles dans un district, variant de 2 à 15). . Une fois que j'ai mieux compris comment ces modèles interagissent, je suis confiant dans la réalisation de cet objectif, mais le comportement que je vois n'a aucun sens pour moi, et j'espérais trouver une certaine clarté dans la mise en œuvre de ces modèles. J'ai l'impression de rebondir sur la réponse, mais je manque un détail clé.
Merci,
Alex
peut vous envoyer un code de commande? Je suppose que vous utilisez des paramètres forts? – Ren
J'utilise des paramètres forts. Je vais mettre à jour avec le code du contrôleur. Cela peut exposer mon ignorance, mais j'avais l'impression que les méthodes du contrôleur ne sont frappées que par de vraies requêtes/routages HTTP, et que les commandes en cours d'exécution à partir de la console ne touchent pas ces méthodes. –