Polymorphic Nested Form with Paperclip in Rails 3.1.3 - ruby-on-rails

My models:
class NewsItem < ActiveRecord::Base
has_many :file_uploads, :as => :uploadable
accepts_nested_attributes_for :file_uploads, :allow_destroy => true
end
class FileUpload < ActiveRecord::Base
belongs_to :uploadable, :polymorphic => true
has_attached_file :upload, :styles => {:thumb => '100x100>'}
end
Form Code (nested within NewsItem)
...
<%= f.fields_for :file_uploads do |upload| %>
<div class="file_upload">
<%= upload.file_field :upload %>
</div>
...
<% end %>
...
On submit I get the following error: "unknown attribute: upload". Here are the params:
{"utf8"=>"✓",
"authenticity_token"=>"MBfxJ4XTizCXv3Mpu971VHCm60bS3Y84Kdxfe+VJD2w=",
"news_item"=>{"title"=>"",
"body"=>"",
"published_date"=>"",
"file_uploads_attributes"=>{"0"=>{"upload"=>#<ActionDispatch::Http::UploadedFile:0x000001070112a8 #original_filename="rails-dd352fc2630e5f9aa5685ef1d7fe5997.png",
#content_type="image/png",
#headers="Content-Disposition: form-data; name=\"news_item[file_uploads_attributes][0][upload]\"; filename=\"rails-dd352fc2630e5f9aa5685ef1d7fe5997.png\"\r\nContent-Type: image/png\r\n",
#tempfile=#<File:/var/folders/hb/2bkct63171lck8d3sg0xfq0c0000gn/T/RackMultipart20111204-3216-71in7a>>,
"name"=>"",
"caption"=>""}}},
"commit"=>"Create News item"}
I'm using Rails 3.1.3 and paperclip "~> 2.4".

I'd avoid generic terms like "uploadable" because the resultant term "upload" has the potential for collision.
youavmatchulsky's suggestions are good too - if you have attr_accessible anywhere you'll need to make file_uploads_attributes accessible as well.
Also, the params don't look like the form is multipart, so I'd force it with :multipart => true in the call to form_for
EDIT: Even though it's supposed to happen automagically, you may have to explicitly accept_nested_attributes_for the join, and then on the join model accept_nested_attributes_for :uploadable -- I've found anaf to be pretty weird with things like polymorphic joins sometimes

Restarting the rails app fixed the problem. I'm guessing I installed the gem but did not restart, leading to the error above. Lesson learned: always restart after installing a gem.

Related

Nested many to many form using cocoon Rails 6

I'm trying to make it possible to add multiple crops to a spray_program through program_crops in a nested form using cocoon. I'm able to add one but the link_to_add_association button just adds # to the url. Ill add the bits of code I think will be relevant let me know if there is something else...
nested form in spray_program form
<div id="programCrops">
<%= form.fields_for :program_crops do |program_crop_form| %>
<%= render 'program_crop_fields', f: program_crop_form%>
<% end %>
</div>
<div class="links">
<%= link_to_add_association "Add crop", form, :program_crops %>
</div>
_program_crop_fields.html.erb
<div class="nested-fields">
<div class="field">
<%= f.label :name %>
<%= f.collection_select :crop_id, Crop.all, :id, :name %>
</div>
<%= link_to_remove_association "Remove crop", f %>
</div>
I suspect building the model only once here may be the issue and this needs to be passed elsewhere
def new
#spray_program = SprayProgram.new
#spray_program.build_program_sprayer
#spray_program.program_crops.build
end
the model
class ProgramCrop < ApplicationRecord
belongs_to :crop
belongs_to :spray_program inverse_of: :program_crops
validates :spray_program, uniqueness: { scope: :crop }
end
No JavaScript errors being thrown in console. The dropdown renders correctly and creates one many to many association as expected. Sorry if I've missed something important it's getting late!
--Additional models
class Product < ApplicationRecord
belongs_to :farm
has_one :user, through: :farm, dependent: :destroy
has_many :program_doses, dependent: :destroy
end
```ruby
class SprayProgram < ApplicationRecord
has_one :program_sprayer, dependent: :destroy
has_many :program_crops, inverse_of: :spray_program, dependent: :destroy
has_one :sprayer, through: :program_sprayer
has_many :crops, through: :program_crops
accepts_nested_attributes_for :program_sprayer
accepts_nested_attributes_for :program_crops, reject_if: :all_blank, allow_destroy: true
# validates_associated :program_sprayer
end
params produced look like this
{"authenticity_token"=>"[FILTERED]",
"spray_program"=>
{"date(1i)"=>"2022",
"date(2i)"=>"8",
"date(3i)"=>"1",
"farm_id"=>"1",
"program_sprayer_attributes"=>{"sprayer_id"=>"1", "litres_per_min"=>"4", "speed"=>"7"},
"program_crops_attributes"=>{"0"=>{"crop_id"=>"8", "_destroy"=>"false"}}},
"commit"=>"Create Spray program"}
without the other model I can't answer for sure, but I suspect that you are missing either accepter_nested_attributes_for or inverse_of, check the note under Basic Usage in here, https://www.rubydoc.info/gems/cocoon/1.2.12
Rails 5 Note: since rails 5 a belongs_to relation is by default required. While this absolutely makes sense, this also means associations have to be declared more explicitly. When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :project
This basically allows to have a reference to an object on the new action, without having that objects id(because it hasn't been created yet), by building the object you are giving the data to the form, but the form doesn't know how to link them, so you end up sending back to the backend two objects that don't reference each other
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Please review this, you need to tell the modal that you are accepting nested attributes, cocoon works this way, if you still not finding the solution, please share other modal too
I got it working eventually, I tried testing whether jquery was being loaded and it wasn't, cocoon didn't. I think something was wrong in my webpacker settings, after running rake assets:clobber I hit a webpack missing entry error so followed this guide after which my local server worked again.
At this point I noticed there was an error locating the cocoon module in application js after webpack compiled for my local server (I messed up the install or import somehow).
In the end i used this vanilla js version of coccoon following the instructions and it's playing nice now.

Can I get/access a form_builder.object's ATTRIBUTE in Rails?

I have resorted to ask a question here since this is something that's puzzling me:
I am trying to create a nested attributes form with the following models:
class Recipe < ApplicationRecord
has_many :ingredient_records, dependent: :destroy, inverse_of: :recipe
accepts_nested_attributes_for :ingredient_records, allow_destroy: true
class IngredientRecord < ApplicationRecord
belongs_to :food, inverse_of: :ingredient_records
belongs_to :recipe, inverse_of: :ingredient_records
I've ommited some parts of the models that are non related. (At least I hope so).
The main problem comes here: When I do this on the :edit view:
<div class="nested-fields">
<div class="field">
<%= f.label :food_id %>
<%= f.select :food_id, [[ f.object.food, f.object.food_id ]]%>
It outputs a select field with an option like <#Food:x0203400291> (the form_builder object, verified it's the correct object with a breakpoint with byebug)
BUT when I do (it's the .name part):
<%= f.select :food_id, [[ f.object.food.name, f.object.food_id ]]%>
Rails raises an error screen with: undefined method ´name' for nil:NilClass (name is an attribute on the Food.model).
What do you guys think? Am I actually tryin something that's not allowed inside a rails view? (Would be strange since putting a byebug clause just before the select builder and calling f.object.food.name returns the value just fine). Thanks in advance!

Rails5, nested form, undefined param

Huston, we have a problem:
class FirstModel
has_many :merged_models
has_many :second_models, :through => :merged_models
end
class SecondModel
has_many :merged_models
has_many :first_models, :through => :merged_models
end
class MergedModel
belongs_to :first_model
belongs_to :second_model
end
Form:
<%= form_for(first_model) do |f| %>
<%= f.fields_for :merged_model do |ff| %>
<%= ff.label :date %>
<%= ff.date_select :start_date %>
Problem:
Processing by FirstModelsController#create as HTML Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"f+D8AaVzM6ahrUyo/nwxISFEleVrXGxo8m30sIiLIe7gvG8J9KfONjuT09j6z3M4Rvw+n3Hm6PMddOtfbgjt5g==",
"first_model"=>{"first_name"=>"yyyy", "last_name"=>"yyy",
"merged_model"=>{"start_date(1i)"=>"2017", "start_date(2i)"=>"2",
"start_date(3i)"=>"28", "second_model_id"=>"1"}}, "commit"=>"Create"}
Unpermitted parameter: merged_model Unpermitted parameter:
merged_model
First model's controller's strong params:
params.require(:first_model).permit(:first_name, :last_name, merged_models_attributes: [:id, :start_date])
First model acccepts nested attributes of merged model:
accepts_nested_attributes_for :merged_models
However, after creating a FirstModel, MergedModel does not get created. Tried to create it in form:
<%= f.fields_for :merged_model [first_model.merged_models.build] do |ff| %>
But got:
no implicit conversion of MergedModel into Integer
Not completely understand what that means..
Also tried creating a new MergedModel from a FirstModel's create action, with a bang:
#merge_model = MergedModel.create!
And got the same error - no implicit conversion...
Could anyone explain more about this? I feel its about passing an Array of my MergedModel's params into MergedModel's params...? I am totally lost here...
Your form should have fields_for :merged_models instead of just merged_model.
On fresh installs of Rails5 applications, belongs_to implies optional: false by default (previously called required: true).
You need to create a MergedModel both with FirstModel AND SecondModel associated...
It looks like you are trying create a MergedModel only with a FirstModel associated, if SecondModel is optional, you need to say that for belongs_to with...
belongs_to :second_model, optional: true

Rails 3, many-to-many form using accepts_nested_attributes_for, how do I set up correctly?

I have a many-to-many relationship between Recipes and Ingredients. I am trying to build a form that allows me to add an ingredient to a recipe.
(Variants of this question have been asked repeatedly, I have spent hours on this, but am fundamentally confused by what accepts_nested_attributes_for does.)
Before you get scared by all the code below I hope you'll see it's really a basic question. Here are the non-scary details...
Errors
When I display a form to create a recipe, I am getting the error "uninitialized constant Recipe::IngredientsRecipe", pointing to a line in my form partial
18: <%= f.fields_for :ingredients do |i| %>
If I change this line to make "ingredients" singular
<%= f.fields_for :ingredient do |i| %>
then the form displays, but when I save I get a mass assignment error Can't mass-assign protected attributes: ingredient.
Models (in 3 files, named accordingly)
class Recipe < ActiveRecord::Base
attr_accessible :name, :ingredient_id
has_many :ingredients, :through => :ingredients_recipes
has_many :ingredients_recipes
accepts_nested_attributes_for :ingredients
accepts_nested_attributes_for :ingredients_recipes
end
class Ingredient < ActiveRecord::Base
attr_accessible :name, :recipe_id
has_many :ingredients_recipes
has_many :recipes, :through => :ingredients_recipes
accepts_nested_attributes_for :recipes
accepts_nested_attributes_for :ingredients_recipes
end
class IngredientsRecipes < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
attr_accessible :ingredient_id, :recipe_id
accepts_nested_attributes_for :recipes
accepts_nested_attributes_for :ingredients
end
Controllers
As RESTful resources generated by rails generate scaffold
And, because the plural of "recipe" is irregular, inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'recipe', 'recipes'
end
View (recipes/_form.html.erb)
<%= form_for(#recipe) do |f| %>
<div class="field">
<%= f.label :name, "Recipe" %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :ingredients do |i| %>
<div class="field">
<%= i.label :name, "Ingredient" %><br />
<%= i.collection_select :ingredient_id, Ingredient.all, :id, :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Environment
Rails 3.2.9
ruby 1.9.3
Some things tried
If I change the view f.fields_for :ingredient then the form loads (it finds Recipe::IngredientRecipe correctly, but then when I save, I get a mass-assignment error as noted above. Here's the log
Started POST "/recipes" for 127.0.0.1 at 2012-11-20 16:50:37 -0500
Processing by RecipesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"/fMS6ua0atk7qcXwGy7NHQtuOnJqDzoW5P3uN9oHWT4=", "recipe"=>{"name"=>"Stewed Tomatoes", "ingredient"=>{"ingredient_id"=>"1"}}, "commit"=>"Create Recipe"}
Completed 500 Internal Server Error in 2ms
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: ingredient):
app/controllers/recipes_controller.rb:43:in `new'
app/controllers/recipes_controller.rb:43:in `create'
and the failing lines in the controller is simply
#recipe = Recipe.new(params[:recipe])
So the params being passed, including the nested attributes, are incorrect in some way. But I have tried lots of variants that fix-one-break-another. What am I failing to understand?
Thanks to clues from all, I have found what was wrong with my approach. Here's how I solved it.
I had originally tried with a simple HABTM many-to-many relationship, where the join table was named following standard Rails convention: ingredients_recipes. Then I realized that in a way, accepts_nested_attributes_for is designed for a 1-to-many relationship. So I converted to using has_many_through, creating a model IngredientsRecipes.
That name was the core problem, because Rails needs to be able to convert from plural to singular when using build to create form elements. This caused it to look for the non-existant class Recipe::IngredientsRecipe. When I changed my form so it used fields_for :ingredient the form displayed, but still failed to save with a mass assignment error. It even failed when I added :ingredients_attributes to attr_accessible. It still failed when I added #recipe.ingredients.build to RecipesController#new.
Changing the model to a singular form was the final key to resolve the problem. IngredientsRecipe would have worked, but I chose RecipeIngredients, as it makes more sense.
So to summarize:
can't use accepts_nested_attributes_for with has_and_belongs_to_many; need has_many with through option. (Thanks #kien_thanh)
adding accepts_nested_attributes_for creates a accessor that must be added to attr_accessible in the form <plural-foreign-model>_attributes, e.g. in Recipe I added attr_accessible :name, :ingredients_attributes (Thanks #beerlington)
before displaying the form in the new method of the controller, must call build on the foreign model after creating a new instance, as in 3.times { #recipe.ingredients.build }. This results in HTML having names like recipe[ingredients_attributes][0][name] (Thanks #bravenewweb)
join model must be singular, as with all models. (All me :-).
If you inspect the form that is generated, you'll notice that the nested fields have a name like "ingredients_attributes". The reason you're getting the mass-assignment error is because you need to add these fields to the attr_accessible declaration.
Something like this should fix it (you'll need to doublecheck the field names):
class Recipe < ActiveRecord::Base
attr_accessible :name, :ingredients_attributes
#...
end
Update: There's a similar answer here
Leave the call as
<%= f.fields_for :ingredients do |i| %>
But before that do
<% #recipe.ingredients.build %>
Im guessing that will allow your form to be created the right way, but there are likely other errors with your models, I can look # it more in detail when I have more time if its still not working, but:
As far as what accepts_nested_attributes_for does, when you pass in a correctly formatted params hash to the Model.new or Model.create or Model.update, it allows those attributes on the related model to be saved if they are in the params hash. In addition though, you do need to make the attributes accessible if they are unaccessible in the parent model as stated by beerlington.
I think you just need set up a one-to-many association, one recipe has many ingredients and one ingredient belongs to one recipe, so your model look like:
class Recipe < ActiveRecord::Base
attr_accessible :name, :ingredients_attributes
has_many :ingredients
accepts_nested_attributes_for :ingredients
end
class Ingredient < ActiveRecord::Base
attr_accessible :name, :recipe_id
belongs_to :recipe
end
You are built right form, so I don't write it again here. Now in your new and create controller will be like this:
def new
#recipe = Recipe.new
# This is create just one select field on form
#recipe.ingredients.build
# Create two select field on form
2.times { #recipe.ingredients.build }
# If you keep code above for new method, now you create 3 select field
end
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save
...
else
...
end
end
How does params[:recipe] look like? If you just have one select field, maybe like this:
params = { recipe: { name: "Stewed Tomatoes", ingredients_attributes: [ { id: 1 } ] } }
If you have 2 ingredient select field:
params = { recipe: { name: "Stewed Tomatoes", ingredients_attributes: [ { id: 1 }, { id: 2 } ] } }

Using fields from an association (has_many) model with formtastic in rails

I searched and tried a lot, but I can't accomplish it as I want.. so here's my problem.
class Moving < ActiveRecord::Base
has_many :movingresources, :dependent => :destroy
has_many :resources, :through => :movingresources
end
class Movingresource < ActiveRecord::Base
belongs_to :moving
belongs_to :resource
end
class Resource < ActiveRecord::Base
has_many :movingresources
has_many :movings, :through => :movingresources
end
Movingresources contains additional fields, like quantity. We're working on the views for 'bill'. Thanks to formtastic to simplify the whole relationship thing by just writing
<%= form.input :workers, :as => :check_boxes %>
and i get a real nice checkbox list. But what I haven't found out so far is: How can i use the additional fields from 'movingresource', next or under each checkbox my desired fields from that model?
I saw different approaches, mainly with manually looping through an array of objects and creating the appropriate forms, using :for in a form.inputs part, or not. But none of those solutions were clean (e.g. worked for the edit view but not for new because the required objects were not built or generated and generating them caused a mess).
I want to know your solutions for this!
Okay, I missed the revolution of accepts_nested_attributes_for, this explains why it's not really working.
This got me a big step further, but I think somewhere I will still have some complications with my complex relations ^_^
class Moving < ActiveRecord::Base
has_many :movingworkers, :dependent => :destroy
has_many :workers, :through => :movingworkers
accepts_nested_attributes_for :movingworkers
end
<% form.inputs :for => :movingworkers do |movingworker| %>
<%= movingworker.inputs :worker, :quantity %>
<% end %>
Formtastic's :label_method option might help. E.g.
<%= form.input :movingworkers, :label_method => :worker %>
or
<%= form.input :movingworkers, :label_method => Proc.new { |x| "#{x.worker} #{x.quantity}" } %>
If the fields don't exist in the new view, you can just test if it is new (new_record?) and present a different set of fields (if you wrap into a partial in can be quite a clean approach).

Resources