2010-11-12 42 views
14

j'ai les modèles suivantsaccepts_nested_attributes_for pour créer un lien vers enregistrement existant, créer un nouveau pas

class Order < AR::Base 
    has_many :products 

    accepts_nested_attributes_for :products 
end 

class Product < AR::Base 
    belongs_to :order 
    has_and_belongs_to_many :stores 

    accepts_nested_attributes_for :stores 
end 

class Store < AR::Base 
    has_and_belongs_to_many :products 
end 

Maintenant, j'ai une vue de l'ordre là où je veux mettre à jour les magasins pour le produit. La chose est que je veux seulement connecter les produits aux magasins existants dans mon DB, pas en créer de nouveaux.

Ma forme dans la vue de la commande ressemble à ceci (en utilisant Formtastic):

= semantic_form_for @order do |f| 
    = f.inputs :for => :live_products do |live_products_form| 
    = live_products_form.inputs :for => :stores do |stores_form| 
     = stores_form.input :name, :as => :select, :collection => Store.all.map(&:name) 

Bien que son imbriquée il fonctionne très bien. Le problème est que, lorsque je sélectionne un magasin et essaie de mettre à jour la commande (et les produits et magasins avec elle), Rails essaie de créer un nouveau magasin avec ce nom. Je veux juste utiliser le magasin existant et connecter le produit à cela.

Toute aide appréciée!

EDIT 1:

En fin de compte, je résolu ce problème d'une manière très grossière:

# ProductsController 

def update 
    [...] 

    # Filter out stores 
    stores_attributes = params[:product].delete(:stores_attributes) 

    @product.attributes = params[:product] 

    if stores_attributes.present? 
    # Set stores 
    @product.stores = stores_attributes.map do |store_attributes| 
     # This will raise RecordNotFound exception if a store with that name doesn't exist 
     Store.find_by_name!(store_attributes[:name]) 
    end 
    end 

    @order.save 

    [...] 
end 

EDIT 2:

solution de Pablo est beaucoup plus élégant et doit être préféré à moi .

+0

Après avoir examiné les documents pour a_n_a_f (http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html) Je suis arrivé excité quand j'ai vu l'option update_only mais rapidement réalisé est impossible faire ce que je veux faire (update_only met à jour les objets existants avant d'en créer de nouveaux). –

Répondre

21

Essayez de mettre en œuvre un :reject_if que vérifier si le magasin existe déjà et l'utiliser:

class Product < AR::Base 
    belongs_to :order 
    has_and_belongs_to_many :stores 

    accepts_nested_attributes_for :stores, :reject_if => :check_store 

    protected 

    def check_store(store_attr) 
     if _store = Store.find(store_attr['id']) 
     self.store = _store 
     return true 
     end 
     return false 
    end 
end 

J'ai ce code fonctionne bien dans un projet en cours.

S'il vous plaît, laissez-moi savoir si vous avez trouvé une meilleure solution.

+0

Très intelligent! J'ai résolu le problème d'une manière beaucoup moins élégante (je vais éditer ma question pour le montrer) mais votre solution devrait fonctionner mieux. –

+8

Cela n'a aucun sens pour moi. 'self' dans' check_store' est le Product ... et un Product n'a pas de relation 'store' (c'est HABTM: stocke). Alors, que fait ce code? De plus, il ne semble pas mettre à jour le magasin trouvé. – davemyron

+0

Je l'ai ajusté pour travailler pour mon propre usage en trouvant l'enregistrement associé existant et en mettant à jour ses attributs * dans reject_if *. Semble hacky, bien sûr, mais cela a fonctionné. – davemyron

0

J'ai eu le même problème et l'ai résolu en ajoutant: id à la liste de paramètres imbriqués.

def family_params 
    params.require(:family).permit(:user_id, :address, people_attributes: [:id, :relation, :first_name, :last_name) 
end 
+0

J'ai écrit ma question longtemps avant que les paramètres forts soient sortis. –