1

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

  1. Ignorer les paramètres étant passés dans la fonction de création et
  2. 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

+0

peut vous envoyer un code de commande? Je suppose que vous utilisez des paramètres forts? – Ren

+0

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. –

Répondre

1

Essayez enlever vos lignes de code attr_accessor. attr_accessor ne doit pas être utilisé pour les attributs qui sont conservés dans la base de données et il est probablement déconner les méthodes qui ActiveRecord fournit déjà par défaut, ce qui provoque ces attributs ne sont pas enregistrés correctement

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 
+0

Salut, merci pour l'aide! J'ai essayé le créer selon votre suggestion et il crée le SurveyAssignment mais pas le school_survey_assignments. J'ai aussi essayé ce que disent les docteurs api.rubyonrails.org pour l'exemple un-à-plusieurs http://api.rubyonrails.org/v4.2/classes/ActiveRecord/NestedAttributes/ClassMethods.html#label -Validation + la + présence + de + a + parent + modèle Cela a également entraîné la création de l'assignation d'enquête, mais pas la création de SchoolSurveyAssignments. –

+0

Sur la base de quelques conseils d'un collègue, je me suis orienté vers l'utilisation d'une classe d'objets Form personnalisée pour gérer la création de l'assignation Survey et des SchoolSurveyAssignments. Il est approprié de gérer les paramètres, et je sais que je reçois tous les paramètres nécessaires à travers le SurveyAssignmentController et dans cet objet. Toutefois, il ne conserve pas les attributs du modèle enfant (school_survey_assignment). Il semblerait que j'ai une sorte d'autre problème, plutôt que n'importe quel problème avec les formes/objets imbriqués –

+0

fait une modification à la publication originale.essayez de commenter/supprimer les lignes 'attr_accessor' – Ren

1

Pour la première question, l'école et SurveyAssignment ne se connaissent pas, school_id devient nulle. Dans votre application, ces modèles ont une association m-à-n indirecte, alors comment utiliser has_many via l'association entre modèles?

Dans le modèle scolaire, ajouter ci-dessous:

has_many :survey_assignments, :through => :school_survey_assignments 

Dans le modèle de SurveyAssignments, ajouter ci-dessous:

has_many :schools, :through => :school_survey_assignments 

Pour la dernière question, les deux codes semblent être les mêmes ..

+0

merci de souligner l'association nécessaire. J'ai ajouté cela, et je suis toujours confronté à des problèmes. J'ai mis à jour le code pour refléter les changements. En ce qui concerne la dernière question, la différence est dans .id vs _id –

+0

Désolé, j'ai négligé cela, mais à la fois .id et _id fonctionne sur mon PC. Votre problème est toujours .id vs _id? BTW, dans la classe SurveyAssignment, reverse_of: survey_assignment manque ":" avant "survey_assignment", je suppose. – YTorii

+0

Merci pour le suivi. Ce manquant: était une faute de frappe en allant de mon éditeur à Stackoverflow, mais bon attraper. –