0

J'essaie de sauvegarder un objet contenant une recette et plusieurs ingrédients. Les données proviennent de mon application angulaire 2 qui passe l'objet en JSON. L'application My Rails 5 api recevra l'objet recette et l'enregistrera directement dans ma base de données en utilisant les paramètres forts. En ce moment je peux enregistrer l'objet de recette dans la base de données mais pour une raison quelconque les ingrédients à l'intérieur ne sont pas sauvés. J'ai vérifié la documentation des rails et je n'ai trouvé aucun problème avec mon code actuel. Toute réflexion sera apprécié.Rails 5 paramètres forts avec plusieurs attributs imbriqués

recipe.rb

class Recipe < ApplicationRecord 
has_many :ingredients 
accepts_nested_attributes_for :ingredients 
end 

ingredient.rb

class Ingredient < ApplicationRecord 
belongs_to :recipe 
end 

recipe_controller.rb

def create 
    @recipe = Recipe.new(recipe_params) 
    if @recipe.save                                     
    render json: @recipe, status: :created, location: @recipe 
    else 
    render json: @recipe.errors, status: :unprocessable_entity 
    end 
end 

def recipe_params 
    params.require(:recipe).permit(:name , :description, :imagePath, ingredients_attributes: [ :id, :name, :amount]) 
end 

mon journal de la console

 Started POST "/recipes" for ::1 at 2017-01-09 11:40:44 +0900 
     ActiveRecord::SchemaMigrationLoad(0.3ms)SELECT`schema_migrations`.* FROM `schema_migrations` 

     Processing by RecipesController#create as HTML Parameters: {"name"=>"Spaghetti", "imagePath"=>"http://cdn2.tmbi.com/TOH/Images/Photos/37/300x300/exps36749_SD143203D10__25_1b.jpg", "description"=>"Delicious spaghetti", "ingredients"=>[{"name"=>"Tomato", "amount"=>1}, {"name"=>"Pasta", "amount"=>1}], "recipe"=>{"name"=>"Spaghetti", "description"=>"Delicious spaghetti", "imagePath"=>"http://cdn2.tmbi.com/TOH/Images/Photos/37/300x300/exps36749_SD143203D10__25_1b.jpg"}} 


    (0.1ms) BEGIN 
     SQL (0.2ms) INSERT INTO `recipes` (`name`, `description`, `imagePath`, `created_at`, `updated_at`) VALUES ('Spaghetti', 'Delicious spaghetti', 'http://cdn2.tmbi.com/TOH/Images/Photos/37/300x300/exps36749_SD143203D10__25_1b.jpg', '2017-01-09 02:40:44', '2017-01-09 02:40:44') 
    (0.7ms) COMMIT 
    Completed 201 Created in 9ms (Views: 1.0ms | ActiveRecord: 2.1ms) 
+0

N'est-ce pas sur les paramètres forts? Voici un bon post [comment autoriser tableau dans les paramètres forts] (http://jaketrent.com/post/permit-array-rails-strong-parameters/) – matiss

+0

J'ai déjà essayé, il ignore simplement les ingrédients à l'intérieur de l'objet de la recette. – Johji

Répondre

1

Dans Rails 5, chaque fois que nous définissons une association belongs_to, l'enregistrement associé doit être présent par défaut après cela https://github.com/rails/rails/pull/18937change. Si vous affichez les @ recipe.errors.messages, vous trouverez

{:"ingredient.recipe"=>["must exist"]} 

Il suffit d'ajouter « option: true » à votre ligne belong_to dans le modèle d'ingrédient.

class Ingredient < ApplicationRecord 
belongs_to :recipe, optional: true 
end 
+0

Désolé, je ne comprends pas ce que vous voulez dire par là. J'essaie d'enregistrer la recette et l'ingrédient à l'intérieur de mon contrôleur de recette. – Johji

+0

Il ne sera pas enregistré si vous avez une erreur de validation. Le fait que Rails 5 exige que l'association parent existe avant de pouvoir enregistrer l'enregistrement générera l'erreur de validation et ne permettra pas à votre contrôleur de fonctionner. Si vous essayez votre code dans Rails 4, cela fonctionnera, mais pas Rails 5. En utilisant optionnel: true, vous autorisez l'enfant à être sauvé sans avoir encore d'identifiant parent. – JamesAspinwall

0

Votre journal de la console montre que les params comprennent la ingredients clé. Pour les attributs imbriqués, vous devez utiliser ingredients_attributes à la place. Donc, assurez-vous que vos params ressemblent à ceci:

{ 
    "name" => "Spaghetti", 
    "description" => "Delicious spaghetti", 
    "ingredient_attributes" => [ 
    {"name" => "Tomato", "amount" => 1}, 
    {"name" => "Pasta", "amount" => 1} 
    ] 
} 

Pourquoi ne pouvons-nous utiliser simplement ingredients ici? Eh bien, cela se traduirait par le code suivant exécuté en interne:

recipe.ingredients = [{"name" => "Tomato", … }] 

Mais recipe.ingredients= est l'auteur d'attribut de la relation, et il accepte seulement un tableau de Ingredient cas. Il faudrait donc l'utiliser comme ceci:

recipe.ingredients = [Ingredient.new("name" => "tomato", …)] 

Sous le capot, en utilisant recipe.ingredients_attributes= fait essentiellement cette conversion de hash ordinaire dans les instances de Ingredient.