Complex forms often lead to complex controllers, but that doesn't have to be the case. In this episode see how you can create multiple models through a single form while keeping the controller clean.
Thanks Ryan, this episode is a great summary on how to create multiple models in one action. However, a while ago I had trouble building associated models from a custom setter method that is called during initialization (like in your case). Under some circumstances, the associated models (tasks in your example) were vanishing. The full story: http://zargony.com/2007/09/17/vanishing-records-on-creating-multiple-models-in-one-action/
@Skyblaze: Using separate actions might look unnecessary, but in fact, it makes life easier if it comes to error handling, restful routes and/or ActiveResource.
@Skyblaze:
This is the RESTful way of doing stuff.
A resource expects to have a 'new' action for entering a new object. This even extended to new.xml in edge rails.
Basically if you share controller actions for 2(or more) purposes, it can get ugly really fast. Splitting an action by using if/then/else (request.post? ) just to share a few basic lines isn't worth the cluttering. If you want to DRY up the code, use a before_filter for setting up the basic @instance variable.
@Zargony, interesting. Perhaps a better solution would be to store the contents in an instance variable and handle the association creating in a callback instead of directly in the accessor method.
@Jermaine, yes, the technique I'll show in the upcoming episodes is better then what I wrote about. I'm planning to update these articles soon.
Ok for the rest so do you advise to not use anymore the postback actions style of coding?
For Rayan:
We would love some episodes on complete deployment with capistrano 2.0 and maybe a subversion only one that explain the basics of subversion.
I am also waiting for the longer episodes on how to write basic apps
Thanks for the topic, my controllers can be more slim now.
Do you use not official way to share controllers and view among different applications? (e.g plugems, engines, gems,..)
Ryan, Thanks, I love this idea of creating and updating children in the parent model rather than the controller. So today I made fields_for person[mugshot] and person[address], even though Person only has_one of each of these. Then I made setter methods in the Person model: def address= (address_hash) and the same for mugshot. Works a treat and so much cleaner!!
@HappyCoder, the presenter pattern is interesting, but I'm skeptical. Adding another layer of abstraction and delegation is a big cost, and I have yet to see a good example of it which justifies this cost. Instead I prefer the more direct approach like the virtual attributes you see here.
However, I do think there are many patterns around this concept of presenter pattern which are useful. But, I would only apply it through refactoring to make sure it's exactly what your app needs. Otherwise, you risk adding unnecessary complexity.
I guess you could say every presenter pattern is different depending on the needs of the app.
@DAZ, the difference is that .create will save the model to the database right there, but .build will only build the model in memory and not try to save it.
Hi Ryan, great video, one question, I use a date widget called "datetime toolbocks", they dont provide a method on the formbuilder, so I have to use the normal helpers, for example I use: "toolbocks_date_select object method" syntax on it. Given this, is there a way to use fields_for to make this work?
@Joel, I'm not familiar with the plugin so I don't know. Basically you just need to be concerned about the resulting HTML. Make sure it has that same "project[task_attributes][]" prefix in the name of the fields and it should work. If you can't prefix the name of the fields then you'll have to either hack it or look for a different solution.
Ryan, have you ever tried this approach with date_select. I have a modified version of your example working (I backed off the fancy date widget) but when I add:
<%= task_form.date_select :duedate, :use_month_numbers => true,:order => [:month,:day,:year], :include_blank => true %>
I get the following cgi error:
Conflicting types for parameter containers. Expected an instance of Array, but found an instance of Hash. This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value.
/Applications/Locomotive2/Bundles/standardRailsSept2006.locobundle/i386/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/cgi_ext/cgi_methods.rb:204:in `type_conflict!'
@Joel, yeah, looking at the HTML source it generates you can see it removes the "[]" in the name of the field. Not sure why date_select does this or what the best alternative is.
Is there a way to build such complex form when the models have been generated using the scaffold resource generator, so that we could keep the generated REST code (i.e model + controllers, minus the views) ?
I'm working on a project where I have the equivalent of a Project resource with nested Task resources and I'm struggling to find a nice way to build such complex form while keeping the generated routes and controllers.
Ryan this is beautiful! You might remember me from the railsforum, we've talked about this topic.
Looks like you found it out. I'm still amazed with the amount of lines I removed from my code when I implemented these ideas.
Great screencast, thanks.
Thanks for the great screen cast Ryan. You're right on time for an issue I was tackling. Thanks a lot.
I only kind of remember a way of setting up a class so that one name encompasses several models. For example: Employees have names, addresses and phone numbers. The class Employee contains the models names, addresses and phone_numbers. The point is that you can reference each of the models by calling on the Employee class. Could this fit into your solution or is this a whole new bag of worms?
I set the prefix in the loop to be a constant + id of the task. Then when im creating the tasks I fish out the param for the date. Not the prettiest and it nullifies some of the elegance of this approach but it works.
@Rene, this should work fine with the code generated by scaffold resource. The code I start out with in the screencast is not far from that so you just need to make the appropriate changes in the view and model.
@foozilla, tasks.build will only create the model in memory where as tasks.create will save it to the database. I don't want to save it to the database yet in case validation fails (it will be saved later when the project is saved) so I use tasks.build.
@Bryce, it's hard to say without knowing more about your problem domain. I recommend making a post on railsforum.com where you can discuss the details.
does this mean that the tasks are created by the ProjectsController (as opposed to by the TasksController generated by scaffold_resource)?
I was under the assumption that in order to be RESTful, one should use the TasksController.create action for creating a new task. (and TasksController.edit in order to update).
@Rene, the tasks controller doesn't play any part here, it's all in the projects controller. Since we're going through only one form here it only uses one controller.
I suppose some REST extremists may not like this, but I don't see any problem with it. We aren't adding anymore actions to the controller. In fact there's very little code in the controller concerning tasks. It's primarily concerned with creating the project and the rest is done in the project model through the parameters which are passed to it.
Supposing every project can have upto 10 tasks but not necessarily 10 for every project and the controller and view look same as in the tutorial, how can I filter out the tasks fields that are left blank?
Hey Ryan, this approach seems to have the same problem with collection_select as it does with date_select. May be an issue with all selects. Given this im not sure this approach is valuable unless your form will only have simple text fields.
thanks
Joel
@Kyle, you can do a "blank?" check to only create tasks where the fields aren't blank.
http://pastie.caboo.se/104922
@Dennis, this is a little out of the scope of this tutorial. I recommend posting your question on railsforum.com if you haven't already.
@Joel, collection_select has worked for me with this approach. Are you certain it's not working? I was on edge rails when I tested it so maybe that's part of the issue.
I'm having an issue, the model I'm using in a similar fashion to your Task model has no id column (it's a hm:t join table). I'm getting this error when i try and run it:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.column
Great screencast Ryan!
But, is there way to build in memory say... tag, for article with :through => :tagged_article association.
Like:
def new
@article = Article.new
@article.tags.build
end
Great Railscast. However, I'm left curious to see how you will perform validation. In a current system I'm working on, I have a form for a user with an embedded address model. I post via ajax, validate the user fields then the address fields, and use RJS to highlight the specific form fields where an error occurred. Your technique promises to make that whole process much simpler. Can I request that you follow up in railscast 75? :)
ok I fugured out how to do this. first I put the form in _form.rhtml and I am using it for both new and edit. then I updated the virtual attribute to chect to se if it is getting an array(as in new tasks) or a hash(updating the tasks).
def task_attributes=(task_attributes)
if task_attributes.class == Array
task_attributes.each do |attributes|
tasks.build(attributes)
end
else
task_attributes.each do |id, attributes|
task = Task.find(id)
task.update_attributes(attributes)
end
end
end
@Yuval, I'm not sure I'll have time to get into the details of validation in the next episode, but I'll include it in the code sample online or something.
@Sintaxi, the next episode will show you how to handle editing the project with the tasks.
How do you end up displaying the task info in the index.rhtml form? I have no idea how you do this? Your projects controller index method finds all projects, but in your tutorial, you are able to display the tasks with the project. What is the rhtml code that can display a task for a project? Anyone know how to help?
Thanks for putting these together, they have been a great help!
My issue is that I would like to do this over multiple relationships. For example: what if a task had multiple requirements? How can I do this with still only calling Project.new(params[:project]) and having everything still set automatically?
One serious problem I've been running into using this pattern is validations, especially on the child model.
If, for example, the tasks model had this:
validates_presence_of :project_id
Then you will not be able to save the in-memory project. Reason is when the project is asked if it's valid? it'll cascade down and ask it's children tasks if they're all also valid? - if you're enforcing on the task level the need for an existing project_id, then that validation will fail since the parent project has not yet been saved, there's no id.
I would be very curious to see if you or anyone else has found a clean solution to this problem. Recently I've resorted to some nastiness like so:
In the loop in tasks_attributes= method, work-around the fact that .build does not assign a task's .project_id (understandable) but also does NOT assign the task's .project:
tasks.buid(attributes).project = self
Then in the validation in the task model, do this:
validates_presence_of :project_id, :if => lambda {|record| record.project.nil? }
It's not clean, it's not cohesive, but it works. I'd love to hear if someone came up with something better.
I'd also be happy to hear throries regarding why this:
t = some_project.tasks.build :a => :b, :c => :d.....
will not populate t.project - it's obvious that rails knows what the parent of the built task is, do why doesn't it assign it (object-reference wise - has nothing to do with whether the parent is saved or not....)
hi ryan
thanks for the great railscasts they are a great help!
now, im having a little prroblem trying to implement this one.
When i call the New project view which includes the code for the childs, my app seem not to recognize the child because it says it doesnot find one of the tasks fields:
undefined method `description' for #<Task:0x455e8a4>
hi ryan,
this is drivin me crazy, it just does not work in my case.
i always get that undefined method error up there in my last post.
im trying to give a user(tablename = "users", model name = "User") the possibility to have many emails(table name = "emails", model name ="Email")
im using REST, might this be the problem?
thanks
oh and i forgot:
following code gives me a set of column headers with the names of the fields which rails does not find using your code:
<% for column in user.emails.content_columns %>
<th><%= column.name %></th>
<% end %>
Can you use the technique that you described for an arbitrarily nested creation? Assume that I want to have
Projects
Tasks
People
and I wanted to be able to add multiple tasks and multiple people on a single input form, can I still get away with that? It seems like the id substitution that Rails does may be a problem.
Thanks for the great work you do, I check in every week.
One thing I noticed in the build source code for Active Record (at least in Edge Rails) is that you don't actually need to loop through task_attributes to build the tasks objects -- build will recursively do it for you.
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
becomes
def task_attributes=(task_attributes)
task_attributes.build(attributes)
end
Thanks for the example. I tried it and it worked with the project/tasks. However, when I tried to follow your example on task/requirements it didn't work.
I believe that in your example, it should have been <% for requirement in task.requirements %> instead of <% for requirement in @project.task.requirements %> as task doesn't exist.
I tried both, and the former returned an empty array for requirements, and the latter throws some error on the page.
Could you please show me a working example or correct me?
Today is the first time I actually play around with rails.
@James OKelly: and for all who are getting erros like @profile[interest_descriptions] is not allowed as an instance variable name ... it took me half day before I have found out that (probably) the 1.1.6 version I am using behaves differently and can not generate names like project[task_attributes][][text] which are needed here.
Moreover if you bypass fields_for and other helpers altohether it will NOT convert such values to the array within the params hash structure, but forget all but one item. And as the whole method depends strongly on that, you are out of luck. Do the same as me UPGRADE to 1.2.x series. On Debian it means bypasing apt-get and use gem installation of rails.
@gorn I'm not certain that's the correct solution. I am on a pretty edgy version of Rails (well past 1.2.3) and I am getting this issue as well. Maybe it's the specific version that I am on that is messed up.
Does anyone else have any answers to the dreaded instance variable not found issue?
Hi there, i have managed to implement updating the two models at once and was wondering if it is possible to do three? The third model im using (facilities) habtm the second (room_types). Ideally i would like to be able use check boxes to update the data. Any ideas?
I am having a problem with the "this.up" method in Internet Explorer. Any time it is called, there is a "object doesn't support this property or method".
Tried searching Google but it seems impossible to search for "this.up" since it instead searches for "this up".
I'm having a little problem with the "project[task_attributes][]" part. I've watched the screencast on virtual attributes, I have "def task_attributes=(attributes)" in my model, but I get the message "`@project[task_attributes]' is not allowed as an instance variable name". It works fine when I have "project[task_attributes]" in my form without the extra brackets, but I want to be able to support an array. What am I missing here?
For '... is not allowed as an instance variable name' I ended up using this for each field and removing it from the fields_for call completely. I'm running Rails 2.0.2.
Everyone getting the "is not allowed as an instance variable name" error: one possibility is that you're feeding fields_for a nil object as the second argument.
The error message is really misleading, since it has nothing to do with the format of the string you're sending. My guess is that, failing the existence of the object, it tries to transform the string into a variable.
Ryan, what a wonderful screencast, thank you.
However, when I follow your instructions, the text_fields for my child model do not appear. Playing with the model in the ruby script/console I discovered that parent.children.build creates an empty array of children the very first time it is called, and a @parent.children.size of 1, but the correct number of children the second time it is called.
My parent class is Organ (organization) which inherits from Party. Party has many Addresses.
>> o = Organ.new
=> [#<Organ:0x4a3001c @attributes={ etc.}, @new_record=true>]
>> o.addresses.build
=> [#<Address:0x4a1bf40 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> []
>> o.addresses.build
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
It seems the culprit was the call to parent.children.size right after parent.children.build. Once I removed that call in my view (where the number of children was to be displayed), the child fields appeared as they should!
So i realize this is late, but no one on railsforum seems to know how to help, or just dont care to...
Will this not work for creating a model with multiple attributes? My parameters are keep getting screwed up. Lets say we are creating or updating a task that also has a completed_on attribute, well instead of getting a task model for the ones im editing/creating, it somehow sends a seperate set of paramaters for each attribute. So say I have 2 tasks, I will have params like so...
"task_attributes => [{"task" => "foo"}, {{"task" => "bar"}, {"completed_on" => "1/2/3"}, {"completed_on" => ""}, {"should_destroy" => ""},{"should_destroy" => ""}]"
@levi I too am seeing the parameters get wonky when using multiple attributes in the child object. Let me know if you discover a solution, I will do the same.
`@project[service_attributes]' is not allowed as an instance variable name
I would simply pass locals as in the first example which works fine except that when I add link_to_function and pass in a new object I get the same error.
I've double checked everything and can't work out what the problem is. On Rails 2.02
I have a question about the build method (as do many others I see). I can't seem to find any documentation for this method but from what I gather it bubbles up through to the ActiveRecord::Associations package. I understand what build does but could you possibly point me in the right direction for documentation just so I can learn a bit more about it? I'm still a bit of a noob!
Ryan, thank-you for this informative screen cast! I have been taking a slightly different approach with the code for saving child properties in the controller, but your approach is much cleaner. Thanks again.
Ryan, I love the screencasts! They have taught me an incredible amount in a short time.
I'm having a good deal of problems with this one though. I'm trying to do pretty much exactly what you have above, with one wrinkle: multiple fields for the second model.
I have an order, and items on the order. So I need to be able to setup the order (shipping address etc.) and then add items on the same form. The catch is, I need to define price, quantity and the item_id from the form. When I use your handy for loop in the model virtual attribute, I end up getting "is invalid" because I am trying to create a new record for every variable (price, qty, id). Is there any trick around this? I haven't been able to find much on virtual attributes used in the way you're using them here.
Thanks for the great casts Ryan.
For anyone struggling to get this approach to work for date_select or datetime_select etc as mentioned above by @joel see http://dev.rubyonrails.org/ticket/10551
I have a text_field_with_auto_complete functioning normally. But when I try to use as a text_field, I can not use the <% task_form.text_field_with_auto_complete ... In what way can I get the value of text_field_with_auto_complete and send along with other parameters?
Hi Ryan! Great lesson! It has been helping me a lot on this project on school records (or so I hope it will). However, I'm stuck in this one part.
See, I'm using collection_select or a drop down list dealing with an object instead of just text_fields, and I'm stumped as to where to put the array in the parameters, or how to put it.
Secondly, I'm having some issues getting this sort of thing implemented in my Rails 2.0 project. I'm working with layers in films so that a film has many layers (instead of project has many tasks). I went ahead and tossed in a couple of extra fields for my layer parameters, including a datetime select, a system select and a material select so that my new.html.erb file has something like this in it
<% for layer in @film.layers %>
<% fields_for "...", layer do |layer_form| %>
<p>
Time: <%= layer_form.datetime_select :time%>
Material: <%= collection_select(:layer, :material_id, Material.find(:all, :order=>"name"), :id, :name)%>
System: <%= collection_select(:layer, :system_id, System.find(:all, :order=>"name"), :id, :name)%>
</p>
<% end %>
<% end %>
This was working fine until I did the bit about changing the "..." to (in my case) "film[layer_attributes][]". I also put the accompanying code in my model:
def layer_attributes=(layer_attributes)
layer_attributes.each do |attributes|
layers.build(attributes)
end
end
When I save those and try to submit a new film request I get
ndefined method `stringify_keys!' for ["time(1i)", "2008"]:Array
from the browser. If I take out the bit of the new.html.erb file that says
Time: <%= layer_form.datetime_select :time%>
everything works fine with the layer_attributes in there. Any idea why I'm getting trouble here? Does this have anything to do with the fact that I'm running Rails 2.0?
Thanks a lot for your great casts!
I'm quite new to rails.
Now I got the following problem, which belongs to this subject:
I will explain it on the Project - Task example.
I have an action called <b>addtask</b>
that action creates a form and the possibility to add one task to this project.
Now that works fine.
Now comes the tricky part:
Every project belongs to a person and in the <b>addtask</b> I can changed that person.
Now I want that to write this person_id in the task tabel. So I now which task is ment for whom.
this is a problem when you are trying to make each field autocomplete with scriptaculous because it needs a unique id for each input, and this is ignoring the fact they should be unique anyway.
First, let me say 2 thinks - 1) I'm a noob and 2) I really appreciate these!!
I tried this with rails 2.1 but have some problems and not sure if it's do to how 2.1 works or due to my noobiness. When I tried this, it doesn't actually save the tasks, only the project. Also, my screen doesn't show the 3 tasks as part of the record as your screencast does. What gives? Any ideas?
Thanks for the great tutorial. I'm new to Rails and loving it.
I'm currently working on a form that requires creating 3 models where each belongs_to the previous. Can I use the same technique to go more than 2 levels deep? For example, instead of just being able to create a Project and it's associated Tasks on the same form, can I do something like create a Category, Project, and Task on one form? Not sure exactly how fields_for works.
@Mina: I've found (what I feel) is a cleaner way to handle the validation issue. I add a dummy project_id in my build call, which is neatly overwritten with the actual project_id when the task is saved. Using the example above:
tasks.build(attributes.merge(:project_id=>999))
This works, assuming task validates :project_id, not :project.
If this makes anyone scream, please let me know. It's just the best workaround I was able to come up with in my current, time-sensitive project.
Hi, thx for the great tutorial, but i have some problems for with the request parameters the view sending to the controller:
i have a "voting" model which has a has_many "voting_timeslots". when saving the voting with one voting_timeslot it works great but adding more voting_timeslot i get "undefined method `vote_timeslotes=' for #<Voting:0x62c418c>" .
This is i think because of the request parameter sended to the controller, the hash isn't sorted:
Curious how you got your tasks to associate to the :project_id during your setter method .build code.
I have a product model which included product_photos, where i use attachment_fu to allow for the upload of the picture to the product_photos model. I can't associate the project_id in the setter method, and it isn't added during the .save method in create.
All that to say, ryanb, how did you save the project_id to each of the tasks? if you didn't how in god's name do you display three tasks to one project, if they aren't assoicated via the project_id field in the task model?
Using this method account and group are saved by user is not. If build method is not capable of handling more than 1 level then how should i create the user ensuring the group_id is populated with the new group? Any help will be greatly appreciated.
I'm hoping someone is still looking at this comment thread that might be able to help.
I'm trying to add one additional field to the task (as used in the tutorial). Let's say I want to be able to mark a task as "Active" or not with a boolean value.
when I add
<%= task_form.text_field :task %>
<%= task_form.check_box :active %>
the form passes a "active" => "0" for each unchecked item as well as the "1" for the checked items. This gets all wonky and ends up creating extra rows with only the "active" column filled. Is there a way to make check_box work correctly here?
A Pastie for some detail on my situation:
http://pastie.org/288094
Wow. This topic has a lot of comments. I started reading through them to look for an answer to my question, but I didn't make it very far.
My question is this: is there a way to clear up any confusion from using multiple attributes of the child objects? I now see the last comment (#121) from Rustin is somewhat along these lines. If I wanted to set the task name as well as whether it should be active or not, the params sent back to my controller might look something like:
What is the solution for a has_many :through association between models? I have Movie has_many Actors through Roles - do I build on the join-table Roles?
Someone enlighten me please, as I get stringify_keys error when i try to adapt this to the join-table model attributes!
Great work Ryan, have learnt so much from your work!
@pjam : The way build works is that it stores the child objects in an array in memory and it links that array to the parent object. When the parent object is saved, it saves all of the child objects at the same time with the proper parent id in place. So, for each of the Tasks, they are stored in an array and after the Project is saved, Rails recognizes the associated array of child objects and saves them as well, tacking on the Project's id in the project_id column (all auto-magically!); gotta love Rails!
I am having problems with collection_select and getting :selected to work correctly. I am trying to use it as a search filter for a model that has associations through a join table (second model) with a third model used in the collection_select. I am very new to all of this, but enjoy the casts, no doubt.
Let me be more specific(apologies). I have :multiple => true in a :collection_select helper, however can't get multiple choices to be selected after I submit. (I can however get one to be highlighted if that's all I initially choose) I am sure it's some hash/array problem with my collection_select(model, object, collection) but can't figure it out.
If I can see params[:model][:object] data inmy controller, shouldn't @model = Model.find_by_id(params[:model][:object] be ok as my instance to set the selected with an :object accessor method?
But when i use it for my project
i get an uninitialized constant error.
I have a payslip model which has many payslip adjustments
so the statement @payslip.payslip_adjustments.build raises the error "Uninitialised constant" Any help will be appreciated. Thanks
For some reason, when I submit my form it seems to create both the project and tasks, but the tasks get created with a NULL value for "project_id", so they're not being understood as the project's children. Any idea how this might be happening or how to manually correct it?
This is driving me batty. The project AND task create fine, but project_id in the task is NULL, so the task gets essentially "lost" in the db. One thing I notice in the log is that the sql "Task Create" is happening before the sql "Project Create" -- so the (parent) project doesn't HAVE an id when the (child) task is being created. Any idea how to fix?
I am having the same problem. My guess is that something has changed in the newer version of Rails since this screencast was created. I don't know what it is but you are not alone.
Firstly, thanks for all the hard work you put in to help the community. We really do appreciate it. I am having the same issue as brian and Jonathan. I have a
modelX
has_one :ownership
has_one :user, through => :ownership
I tried following the tutorial and noticed recommendations to try the ownership.build(<new instance based on passed in params>) approach.
So my call in the model looks like this:
def ownership_attr=(ownerships)
ownerships.each do |owner|
ownership.build(:o => Ownership.new(owner)
end
Then when I hit the create button I get an exception nil.build. I have tried Ownership.create!(params passed in) but that leaves the ModelX_id to nil.
I'm trying to do this same thing except that I want a form that allows a user to create multiple projects and multiple tasks all at once, then save. So I want each project in an array, similar to how each task would be in an array, but I can't seem to separate each project, it just puts all the tasks together. Any thoughts?
Hi
I did exactly what is there in the video and its working, But whenever i try to update the same i got the following error
undefined method `stringify_keys!' for "6":String
My Models are
*************
class Product < ActiveRecord::Base
has_and_belongs_to_many :members
belongs_to :category
has_many :installments
def installment_attributes=(installment_attributes)
installment_attributes.each do |attributes|
installments.build(attributes)
end
end
end
*******************************
class Installment < ActiveRecord::Base
belongs_to :product
end
_______________________________
and the products_controller looks like this->
def update
@product = Product.find(params[:id])
respond_to do |format|
if @product.update_attributes(params[:product])
flash[:notice] = 'Product was successfully updated.'
format.html { redirect_to(@product) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @product.errors, :status => :unprocessable_entity }
end
end
end
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
and the stack trace is->
Hi, I had to do something today as Duminy and I got the same mistake.
I read somewhere that it may be the fact that what we are passing to Model.new or Model.update is no longer simple hash because it contains an array inside...
I am still confused but I get as well stringify_keys! method missing.
Thanks for the help
Ryan,
im trying to follow your example in my rails setup (2.3.2 version) and my task_attributes= setter method doesn't even get invoked. did something change in 2.3.2?
Ryanb sounds familiar in different blogs.. I'm developing an accounting system but it takes so many time in creating models in one app..Im so confused...
Ok I'd like to do this but I don't want to create a new project.
Here's what I have...
a User has_many References
The applicant is already created ahead of time. But how do I create that array... I feel like this should be simple but I'm having a hard time with it.
References Controller: http://pastie.org/605600
New References view: http://pastie.org/605604
I'm pretty sure my view isn't right... still learning...
Should I be doing this from the User controller or the References controller? I didn't think I would do this out of the User controller because I'm not creating a new user just new references.
Hi, When I try adding this to my project I keep getting an error, which I've mentioned below. I've slightly changed it where I'm not dooing the 3.times. Apart from that I think its pretty much the same. Can someone please help? I'm new to rails, so please be gentle :)
When I add this to my project I get the following error:
I really appreciate your screencasts! I am fairly new to RoR and I am wondering whether or not I need to add "accepts_nested_attributes_for ..." to the parent? I currently have this functionality working but I am curious if this is a deprecated requirement or what since I do not see you using it and it appears to work successfully without it. Much of the other reading/online resources show "accepts_nested_attributes_for ..." in the examples and I do see it on the Rails API pages. If I do need to incorporate "accepts_nested_attributes_for ..." when and why is it required?
Is it possible to do this where (to use your example):
- there is another table that has a list of task categories.
- if there are n categories in the controller there would be n.build (the easy bit)
- rather than having a collection_select next to each item, the partial would loop through each of them and have the ability to provide a value. easy enough with a hidden value to do this, but I'm thinking the edit form could get ugly very quickly
Or ... is this the wrong approach completely?
The scenario I am thinking of is where I have products that have user-definable extended attributes, that each needs a value. But the extended attributes are consistent for all products in a category
Realise that this may be out of scope and the cast is old. But I'm sure this must be a common problem?
Rookie question: I've followed the tutorial through to the mid-way point where Ryan says "Let's take a look at our code...", refreshes the UI and we see 3 tasks below the Project. I refresh my project screen and my code shows a "NoMethodError" in the Controller with an undefined method for the tasks that were added. Not sure what's up since I've pretty much followed the demo.
What I don't know is that I've created a Tasks model and I don't know what that should look like since it's not covered in the demo. Could this be the issue? Or did I miss scaffolding something? Let me know what to post that might be helpful to debug. I'm using Rails 2.3.4. & am new to this...
What if i have radio_buttons on a page for each fields_for the :name param is the same and I can check just 1 value for all fields_for, how can i fix this?
I run in such problem, that i have a validation in task model of presence project_id,
Solution was: tasks.build(attributes.merge(:project_id => self), to merge attributes array with project_id.
I wonder is there a better solution to do this?
and why "tasks.build" in project model doesn't build this relation?
I am using Rails3, ruby 1.9.2
I wonder why you use two separate methods for new and create actions. Why don't you use postbacks?
Thanks Ryan, this episode is a great summary on how to create multiple models in one action. However, a while ago I had trouble building associated models from a custom setter method that is called during initialization (like in your case). Under some circumstances, the associated models (tasks in your example) were vanishing. The full story: http://zargony.com/2007/09/17/vanishing-records-on-creating-multiple-models-in-one-action/
@Skyblaze: Using separate actions might look unnecessary, but in fact, it makes life easier if it comes to error handling, restful routes and/or ActiveResource.
@Skyblaze:
This is the RESTful way of doing stuff.
A resource expects to have a 'new' action for entering a new object. This even extended to new.xml in edge rails.
Basically if you share controller actions for 2(or more) purposes, it can get ugly really fast. Splitting an action by using if/then/else (request.post? ) just to share a few basic lines isn't worth the cluttering. If you want to DRY up the code, use a before_filter for setting up the basic @instance variable.
Ryan,
I remember that you've written a nice tutorial about this on the railsforum website a year ago.
http://www.railsforum.com/viewtopic.php?id=717
Hopefully in the next part there will be some RJS goodness.
- Jermaine
@Zargony, interesting. Perhaps a better solution would be to store the contents in an instance variable and handle the association creating in a callback instead of directly in the accessor method.
@Jermaine, yes, the technique I'll show in the upcoming episodes is better then what I wrote about. I'm planning to update these articles soon.
Hi Ryan,
thank you so much, great episode.
Again, I can't wait the next episode!
Unfortunatelly have to wait another week ;-)
greetinx
Rafael
Ok for the rest so do you advise to not use anymore the postback actions style of coding?
For Rayan:
We would love some episodes on complete deployment with capistrano 2.0 and maybe a subversion only one that explain the basics of subversion.
I am also waiting for the longer episodes on how to write basic apps
Thanks for the topic, my controllers can be more slim now.
Do you use not official way to share controllers and view among different applications? (e.g plugems, engines, gems,..)
Ryan, you always make screencasts on topics that puzzle me exactly when I really need a solution :-)
My friend told me about another one interesting solution http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
You should make a screencast about it.
Ryan, Thanks, I love this idea of creating and updating children in the parent model rather than the controller. So today I made fields_for person[mugshot] and person[address], even though Person only has_one of each of these. Then I made setter methods in the Person model: def address= (address_hash) and the same for mugshot. Works a treat and so much cleaner!!
Ryan:
Thanks so much for this episode!!! I've been waiting for it for so long!!! Can't wait for the next one.
Alek
@HappyCoder, the presenter pattern is interesting, but I'm skeptical. Adding another layer of abstraction and delegation is a big cost, and I have yet to see a good example of it which justifies this cost. Instead I prefer the more direct approach like the virtual attributes you see here.
However, I do think there are many patterns around this concept of presenter pattern which are useful. But, I would only apply it through refactoring to make sure it's exactly what your app needs. Otherwise, you risk adding unnecessary complexity.
I guess you could say every presenter pattern is different depending on the needs of the app.
Awesome!
I've been waiting a long time for you to post this one.
Very much looking forward to the next one and even hoping it will turn into a 3-part series ;)
Could somebody explain the .build method? It looks similar to .create ... are there any differences and/or reasons for using it? thanks.
@DAZ, the difference is that .create will save the model to the database right there, but .build will only build the model in memory and not try to save it.
So, you could say ".build" is like ".new".
Hi Ryan, great video, one question, I use a date widget called "datetime toolbocks", they dont provide a method on the formbuilder, so I have to use the normal helpers, for example I use: "toolbocks_date_select object method" syntax on it. Given this, is there a way to use fields_for to make this work?
@Joel, I'm not familiar with the plugin so I don't know. Basically you just need to be concerned about the resulting HTML. Make sure it has that same "project[task_attributes][]" prefix in the name of the fields and it should work. If you can't prefix the name of the fields then you'll have to either hack it or look for a different solution.
thanks, how would I trick collection_select into working this way (If I can figure this out, the rest will work:
<%= collection_select "project[task_attributes][]", :resource_id, entities, :id, :nickname } %>
when I try this, I get "ActionView::TemplateError (`@project[task_attributes]' is not allowed as an instance variable name)"
I tried similar thing on check_box_tag and it works correctly (I think)
new Railscast suggestion,
how about something on Model Observer classes?
I recently discovered these in restful_auth and find them very nice for extracting all the filters and grouping them in a small file.
Although I'm not finding much discussion/documentation on their usage.
@Joel, you would call collection_select through the form builder like the other fields:
<%= task_form.collection_select :resource_id, entities, :id, :nickname %>
You can do the same for the "check_box" method and other form field helper methods.
Ryan, have you ever tried this approach with date_select. I have a modified version of your example working (I backed off the fancy date widget) but when I add:
<%= task_form.date_select :duedate, :use_month_numbers => true,:order => [:month,:day,:year], :include_blank => true %>
I get the following cgi error:
Conflicting types for parameter containers. Expected an instance of Array, but found an instance of Hash. This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value.
/Applications/Locomotive2/Bundles/standardRailsSept2006.locobundle/i386/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/cgi_ext/cgi_methods.rb:204:in `type_conflict!'
@Joel, yeah, looking at the HTML source it generates you can see it removes the "[]" in the name of the field. Not sure why date_select does this or what the best alternative is.
Ryan,
Is there a way to build such complex form when the models have been generated using the scaffold resource generator, so that we could keep the generated REST code (i.e model + controllers, minus the views) ?
I'm working on a project where I have the equivalent of a Project resource with nested Task resources and I'm struggling to find a nice way to build such complex form while keeping the generated routes and controllers.
Ryan this is beautiful! You might remember me from the railsforum, we've talked about this topic.
Looks like you found it out. I'm still amazed with the amount of lines I removed from my code when I implemented these ideas.
Great screencast, thanks.
Is @project.tasks.build same as
@project.tasks.create ???
thanks
Will you do a cast on caching on rails.
is there way to cache part of the page?
thanks much Ryan
Thanks for the great screen cast Ryan. You're right on time for an issue I was tackling. Thanks a lot.
I only kind of remember a way of setting up a class so that one name encompasses several models. For example: Employees have names, addresses and phone numbers. The class Employee contains the models names, addresses and phone_numbers. The point is that you can reference each of the models by calling on the Employee class. Could this fit into your solution or is this a whole new bag of worms?
Thanks again,
Bryce
Well I got dates to work using a hack, if anyone has this problem, not sure its the best way but I just used:
<%= select_date (task.duedate, :prefix => aprefix %>
I set the prefix in the loop to be a constant + id of the task. Then when im creating the tasks I fish out the param for the date. Not the prettiest and it nullifies some of the elegance of this approach but it works.
@Rene, this should work fine with the code generated by scaffold resource. The code I start out with in the screencast is not far from that so you just need to make the appropriate changes in the view and model.
@foozilla, tasks.build will only create the model in memory where as tasks.create will save it to the database. I don't want to save it to the database yet in case validation fails (it will be saved later when the project is saved) so I use tasks.build.
@Bryce, it's hard to say without knowing more about your problem domain. I recommend making a post on railsforum.com where you can discuss the details.
Ryan,
does this mean that the tasks are created by the ProjectsController (as opposed to by the TasksController generated by scaffold_resource)?
I was under the assumption that in order to be RESTful, one should use the TasksController.create action for creating a new task. (and TasksController.edit in order to update).
@Rene, the tasks controller doesn't play any part here, it's all in the projects controller. Since we're going through only one form here it only uses one controller.
I suppose some REST extremists may not like this, but I don't see any problem with it. We aren't adding anymore actions to the controller. In fact there's very little code in the controller concerning tasks. It's primarily concerned with creating the project and the rest is done in the project model through the parameters which are passed to it.
Thanks a lot Ryan,
Pragmatic REST is good too. I guess the TasksController could still be useful if I want to present the Projects and Tasks as ActiveResources
Thanks for the episode Ryan, although I wonder why you couldn't have done it a week earlier as it would have saved me 2 days of work!!
Great Episode - saved a ton of ugly work.
Slightly Off Topic - I thought "Task" was a reserved word.
Good point, just noticed Task is in the reserved words list on the wiki:
http://wiki.rubyonrails.org/rails/pages/ReservedWords
Anyone know why? So far I haven't had any issues with it.
That was really nice. Thank you.
I know i'm going to get destroyed for this comment: but maybe they want to ensure task is used solely for rake tasks? Doh...i'm sure thats wrong
Supposing every project can have upto 10 tasks but not necessarily 10 for every project and the controller and view look same as in the tutorial, how can I filter out the tasks fields that are left blank?
Thanks ryan :)
How do I do if I have 3 models where
agent: mas_many :orders
orders habtm :channels
channels habtm :orders
How to save the channels within agent[channel_attribut] so it get the right order_id?
Hey Ryan, this approach seems to have the same problem with collection_select as it does with date_select. May be an issue with all selects. Given this im not sure this approach is valuable unless your form will only have simple text fields.
thanks
Joel
@Kyle, you can do a "blank?" check to only create tasks where the fields aren't blank.
http://pastie.caboo.se/104922
@Dennis, this is a little out of the scope of this tutorial. I recommend posting your question on railsforum.com if you haven't already.
@Joel, collection_select has worked for me with this approach. Are you certain it's not working? I was on edge rails when I tested it so maybe that's part of the issue.
I'm having an issue, the model I'm using in a similar fashion to your Task model has no id column (it's a hm:t join table). I'm getting this error when i try and run it:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.column
any idea on how to fix this?
Thanks for the awesome screencasts
So having a form that deals with more than one model, what is the best way to handle errors?
@Tal, it's hard to say what the problem is without seeing the code and stack trace. I recommend posting on railsforum.com about it.
@Aslan, I'll be covering how to handle validation errors in the near future.
Great screencast Ryan!
But, is there way to build in memory say... tag, for article with :through => :tagged_article association.
Like:
def new
@article = Article.new
@article.tags.build
end
Thanks
@Alex, I don't think the has_many :through association gives you that "build" method. Instead you'll have to build the join model like this:
@article.tagged_articles.build(:tag => Tag.new)
Great Railscast. However, I'm left curious to see how you will perform validation. In a current system I'm working on, I have a form for a user with an embedded address model. I post via ajax, validate the user fields then the address fields, and use RJS to highlight the specific form fields where an error occurred. Your technique promises to make that whole process much simpler. Can I request that you follow up in railscast 75? :)
how about updating the project/tasks? when creating, task_attributes is an array but when you update it is a hash.
ok I fugured out how to do this. first I put the form in _form.rhtml and I am using it for both new and edit. then I updated the virtual attribute to chect to se if it is getting an array(as in new tasks) or a hash(updating the tasks).
def task_attributes=(task_attributes)
if task_attributes.class == Array
task_attributes.each do |attributes|
tasks.build(attributes)
end
else
task_attributes.each do |id, attributes|
task = Task.find(id)
task.update_attributes(attributes)
end
end
end
@Yuval, I'm not sure I'll have time to get into the details of validation in the next episode, but I'll include it in the code sample online or something.
@Sintaxi, the next episode will show you how to handle editing the project with the tasks.
Thanks Ryan, I look forward to reading it.
How do you end up displaying the task info in the index.rhtml form? I have no idea how you do this? Your projects controller index method finds all projects, but in your tutorial, you are able to display the tasks with the project. What is the rhtml code that can display a task for a project? Anyone know how to help?
Well I answered my own question.
in index.rhtml
<% for task in @project.tasks %>
Task: <%= task.name %>
<% end %>
Hi Ryan!
Thanks for putting these together, they have been a great help!
My issue is that I would like to do this over multiple relationships. For example: what if a task had multiple requirements? How can I do this with still only calling Project.new(params[:project]) and having everything still set automatically?
I have a similar issue to @Ryan Lundie, creating the same effect with a many-to-many relationship.
I just viewed the tutorial for the first time tonight and am going to tackle it... hopefully I can adapt this tutorial to my needs.
In any case, these tutorials are great. Thanks so much for putting your time into creating these for everyone.
Mark
What is the projects_path variable? I don't see any mention of how it gets set.
@Mark
Alright, I think I have the issue figured out.
For the example above, if a task had multiple requirements, your view would look something like the following for the requirements:
<% for requirement in @project.task.requirements %>
<% fields_for "project[task_attributes][requirement_attributes]", requirement do |requirement_form| %>
<p>
Requirement: <%= requirement_form.text_field :name %>
</p>
<% end %>
<% end %>
Then in the task model:
def requirement_attributes=(requirement_attributes)
requirement_attributes.each do |attributes|
self.requirements.build(attributes)
end
end
This seems to work.
Opps. I missed a set of brakets. It should look like this:
<% fields_for "project[task_attributes][requirement_attributes][]", requirement do |requirement_form| %> (Empty set at end)
Thanks for another great episode Ryan
One serious problem I've been running into using this pattern is validations, especially on the child model.
If, for example, the tasks model had this:
validates_presence_of :project_id
Then you will not be able to save the in-memory project. Reason is when the project is asked if it's valid? it'll cascade down and ask it's children tasks if they're all also valid? - if you're enforcing on the task level the need for an existing project_id, then that validation will fail since the parent project has not yet been saved, there's no id.
I would be very curious to see if you or anyone else has found a clean solution to this problem. Recently I've resorted to some nastiness like so:
In the loop in tasks_attributes= method, work-around the fact that .build does not assign a task's .project_id (understandable) but also does NOT assign the task's .project:
tasks.buid(attributes).project = self
Then in the validation in the task model, do this:
validates_presence_of :project_id, :if => lambda {|record| record.project.nil? }
It's not clean, it's not cohesive, but it works. I'd love to hear if someone came up with something better.
I'd also be happy to hear throries regarding why this:
t = some_project.tasks.build :a => :b, :c => :d.....
will not populate t.project - it's obvious that rails knows what the parent of the built task is, do why doesn't it assign it (object-reference wise - has nothing to do with whether the parent is saved or not....)
What version of rails are you using for this Ryan? I have almost the exact same needs (a set of textboxes for a has_many) and I get:
@profile[interest_descriptions] is not allowed as an instance variable name
I am running the 2.0 PR. Any light?
My code is here: http://pastie.caboo.se/110538
I don't see anything out of whack, though I did go to a birthday party last night, hopeful my hangover isn't making me miss something stupid.
hi ryan
thanks for the great railscasts they are a great help!
now, im having a little prroblem trying to implement this one.
When i call the New project view which includes the code for the childs, my app seem not to recognize the child because it says it doesnot find one of the tasks fields:
undefined method `description' for #<Task:0x455e8a4>
Extracted source (around line #11):
8: <tr><td><%= "Task:".t%></td><td><%= f.text_field :task, :size => 60 %></td></tr>
9: <% for task in @user.tasks %>
10: <%fields_for "...", task do |task_form| %>
11: <tr><td><%= "Task:".t%></td><td><%= task_form.text_field :description, :size => 60 %></td></tr>
12: <%end %>
13: <%end%>
hi ryan,
this is drivin me crazy, it just does not work in my case.
i always get that undefined method error up there in my last post.
im trying to give a user(tablename = "users", model name = "User") the possibility to have many emails(table name = "emails", model name ="Email")
im using REST, might this be the problem?
thanks
oh and i forgot:
following code gives me a set of column headers with the names of the fields which rails does not find using your code:
<% for column in user.emails.content_columns %>
<th><%= column.name %></th>
<% end %>
Hi Ryan, just as a question is the code on new.rhtml valid html? I was just getting a little confused.
Can you use the technique that you described for an arbitrarily nested creation? Assume that I want to have
Projects
Tasks
People
and I wanted to be able to add multiple tasks and multiple people on a single input form, can I still get away with that? It seems like the id substitution that Rails does may be a problem.
Thanks for the great work you do, I check in every week.
In your code you point the form url at project_path, where is that set and is there a Railscast about what it is etc?
Great stuff though, this is exactly what I need to do :) Thanks
I'm getting an "undefined local variable or method 'task'" error when trying to save the form.
Any ideas what I might have missed?
@Chris Lock: Perhaps you need "map.resources :projects" at the top of your config/routes.rb file?
Cheers Cliff, I think I found out what he's doing via another cast :) http://media.railscasts.com/videos/034_named_routes.mov
One thing I noticed in the build source code for Active Record (at least in Edge Rails) is that you don't actually need to loop through task_attributes to build the tasks objects -- build will recursively do it for you.
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
becomes
def task_attributes=(task_attributes)
task_attributes.build(attributes)
end
Try it out.
@Ryan,
Thanks for the example. I tried it and it worked with the project/tasks. However, when I tried to follow your example on task/requirements it didn't work.
I believe that in your example, it should have been <% for requirement in task.requirements %> instead of <% for requirement in @project.task.requirements %> as task doesn't exist.
I tried both, and the former returned an empty array for requirements, and the latter throws some error on the page.
Could you please show me a working example or correct me?
Today is the first time I actually play around with rails.
Thanks & regards,
voki
@James OKelly: and for all who are getting erros like @profile[interest_descriptions] is not allowed as an instance variable name ... it took me half day before I have found out that (probably) the 1.1.6 version I am using behaves differently and can not generate names like project[task_attributes][][text] which are needed here.
Moreover if you bypass fields_for and other helpers altohether it will NOT convert such values to the array within the params hash structure, but forget all but one item. And as the whole method depends strongly on that, you are out of luck. Do the same as me UPGRADE to 1.2.x series. On Debian it means bypasing apt-get and use gem installation of rails.
Hope it will same half day to others :-)
@gorn I'm not certain that's the correct solution. I am on a pretty edgy version of Rails (well past 1.2.3) and I am getting this issue as well. Maybe it's the specific version that I am on that is messed up.
Does anyone else have any answers to the dreaded instance variable not found issue?
thanks all.
Hi there, i have managed to implement updating the two models at once and was wondering if it is possible to do three? The third model im using (facilities) habtm the second (room_types). Ideally i would like to be able use check boxes to update the data. Any ideas?
I am having a problem with the "this.up" method in Internet Explorer. Any time it is called, there is a "object doesn't support this property or method".
Tried searching Google but it seems impossible to search for "this.up" since it instead searches for "this up".
Any help would be appreciated
I'm having a little problem with the "project[task_attributes][]" part. I've watched the screencast on virtual attributes, I have "def task_attributes=(attributes)" in my model, but I get the message "`@project[task_attributes]' is not allowed as an instance variable name". It works fine when I have "project[task_attributes]" in my form without the extra brackets, but I want to be able to support an array. What am I missing here?
For '... is not allowed as an instance variable name' I ended up using this for each field and removing it from the fields_for call completely. I'm running Rails 2.0.2.
f.hidden_field :id, { :name => 'foo[bar][][id]' }
Works, but is annoyingly repetitive.
Everyone getting the "is not allowed as an instance variable name" error: one possibility is that you're feeding fields_for a nil object as the second argument.
The error message is really misleading, since it has nothing to do with the format of the string you're sending. My guess is that, failing the existence of the object, it tries to transform the string into a variable.
For all the folks with "is not allowed as an instance variable name"... make sure you've got the ":object => Xxx.new" in this bit:
page.insert_html :bottom, :tasks, :partial => 'task', :object => Task.new
That was my problem, maybe its yours.
Ryan, what a wonderful screencast, thank you.
However, when I follow your instructions, the text_fields for my child model do not appear. Playing with the model in the ruby script/console I discovered that parent.children.build creates an empty array of children the very first time it is called, and a @parent.children.size of 1, but the correct number of children the second time it is called.
My parent class is Organ (organization) which inherits from Party. Party has many Addresses.
>> o = Organ.new
=> [#<Organ:0x4a3001c @attributes={ etc.}, @new_record=true>]
>> o.addresses.build
=> [#<Address:0x4a1bf40 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> []
>> o.addresses.build
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
What could be going on here?
It seems the culprit was the call to parent.children.size right after parent.children.build. Once I removed that call in my view (where the number of children was to be displayed), the child fields appeared as they should!
So i realize this is late, but no one on railsforum seems to know how to help, or just dont care to...
Will this not work for creating a model with multiple attributes? My parameters are keep getting screwed up. Lets say we are creating or updating a task that also has a completed_on attribute, well instead of getting a task model for the ones im editing/creating, it somehow sends a seperate set of paramaters for each attribute. So say I have 2 tasks, I will have params like so...
"task_attributes => [{"task" => "foo"}, {{"task" => "bar"}, {"completed_on" => "1/2/3"}, {"completed_on" => ""}, {"should_destroy" => ""},{"should_destroy" => ""}]"
@levi I too am seeing the parameters get wonky when using multiple attributes in the child object. Let me know if you discover a solution, I will do the same.
For me things are going ok until I try to re-factor into partials.
This works fine:
<% for task in @project.tasks%>
<%= render :partial => 'task', :locals => {:task => task} %>
<% end %>
but using collection...
<%= render :partial => 'task', :collection -> @project.tasks %>
results in an error:
`@project[service_attributes]' is not allowed as an instance variable name
I would simply pass locals as in the first example which works fine except that when I add link_to_function and pass in a new object I get the same error.
I've double checked everything and can't work out what the problem is. On Rails 2.02
Hi Ryan, thanks for the great ideas!
I have a question about the build method (as do many others I see). I can't seem to find any documentation for this method but from what I gather it bubbles up through to the ActiveRecord::Associations package. I understand what build does but could you possibly point me in the right direction for documentation just so I can learn a bit more about it? I'm still a bit of a noob!
Thanks
Tristan
Hi Ryan, Thx for all your railscasts, they are really great!
Do you have any idea how to build a form tis way for double nested models? For example, a Company has many Offices, and an Office has one address.
I'd like to have a form where you can add a company with one or more associated offices along with the address of each office.
I tried the following, but the attributes of address don't get passed for some reason
<% fields_for "company[office_attributes][]", office do |office_form| %>
<p>
office:<%= office_form.text_field :name %><br />
<% fields_for "company[office_attributes][][address_attributes]", Address.new do |address_form| %>
<%= address_form.text_field :street %>
<% end %>
</p>
<% end %>
<% end %>
any help would be appreciated.
thx in advance!
Ben
Hi Ryan, thx for all these great videos. I wonder if you can upload pictures to the database in this way?
Ryan, thank-you for this informative screen cast! I have been taking a slightly different approach with the code for saving child properties in the controller, but your approach is much cleaner. Thanks again.
Hello Ryan, I have the same question as Ben. Could you spend some thoughts on it please? Thanks!
Ryan, I love the screencasts! They have taught me an incredible amount in a short time.
I'm having a good deal of problems with this one though. I'm trying to do pretty much exactly what you have above, with one wrinkle: multiple fields for the second model.
I have an order, and items on the order. So I need to be able to setup the order (shipping address etc.) and then add items on the same form. The catch is, I need to define price, quantity and the item_id from the form. When I use your handy for loop in the model virtual attribute, I end up getting "is invalid" because I am trying to create a new record for every variable (price, qty, id). Is there any trick around this? I haven't been able to find much on virtual attributes used in the way you're using them here.
hey,
I like this way of saving related objects.
How can I alter this to work with
has_many :through relation(join table)?
Right now, it blows up because of " Both records must have an id in order to create the has_many :through record associating them. "
It seems that the parent object needs to be saved before the build can work correctly.
ok, got it, u have to use join model to do the build.
membership.build(:blah => Blah.new(fields))
@tommy can you elaborate or Ryan do you have any comments about this one?
Firstly excellent cast. But I have the same question as @jetmedia.
How to get this working with :through associations. Thanks.
Thanks for the great casts Ryan.
For anyone struggling to get this approach to work for date_select or datetime_select etc as mentioned above by @joel see http://dev.rubyonrails.org/ticket/10551
Is there any way to set this up so that each task is an array of data?
I have a text_field_with_auto_complete functioning normally. But when I try to use as a text_field, I can not use the <% task_form.text_field_with_auto_complete ... In what way can I get the value of text_field_with_auto_complete and send along with other parameters?
Thanks.
Hello Ryan
great tutorial ! And I look forward new tutorial like Observer and State issue.
Thank you
Daniel
Hi Ryan! Great lesson! It has been helping me a lot on this project on school records (or so I hope it will). However, I'm stuck in this one part.
See, I'm using collection_select or a drop down list dealing with an object instead of just text_fields, and I'm stumped as to where to put the array in the parameters, or how to put it.
Care to enlighten me?
eg.
collection_select("enlist_subject", "subject_id", Subject.find(:all), :id, :title)
which can also be written/written originally as:
collection_select(:enlist_subject, :subject_id, Subject.find(:all), :id, :title)
*I don't think this will need the <% fields_for ... %> part anymore.
Hope you or anyone can help. Thanks!!
Hi - firstly, great railscast - very groovey!
Secondly, I'm having some issues getting this sort of thing implemented in my Rails 2.0 project. I'm working with layers in films so that a film has many layers (instead of project has many tasks). I went ahead and tossed in a couple of extra fields for my layer parameters, including a datetime select, a system select and a material select so that my new.html.erb file has something like this in it
<% for layer in @film.layers %>
<% fields_for "...", layer do |layer_form| %>
<p>
Time: <%= layer_form.datetime_select :time%>
Material: <%= collection_select(:layer, :material_id, Material.find(:all, :order=>"name"), :id, :name)%>
System: <%= collection_select(:layer, :system_id, System.find(:all, :order=>"name"), :id, :name)%>
</p>
<% end %>
<% end %>
This was working fine until I did the bit about changing the "..." to (in my case) "film[layer_attributes][]". I also put the accompanying code in my model:
def layer_attributes=(layer_attributes)
layer_attributes.each do |attributes|
layers.build(attributes)
end
end
When I save those and try to submit a new film request I get
ndefined method `stringify_keys!' for ["time(1i)", "2008"]:Array
from the browser. If I take out the bit of the new.html.erb file that says
Time: <%= layer_form.datetime_select :time%>
everything works fine with the layer_attributes in there. Any idea why I'm getting trouble here? Does this have anything to do with the fact that I'm running Rails 2.0?
Any help is greatly appreciated.
For those with problems with collection_select, it's because the select helpers use the :index in the html_options hash. So what is normally:
collection_select :e_id, Exercise.find(:all), :id, :name, :index => nil
Becomes:
collection_select :e_id, Exercise.find(:all), :id, :name, {}, :index => nil
I spent a lot of time trying to figure this one out, too. Hope this helps someone with this problem!
Hi!
Thanks a lot for your great casts!
I'm quite new to rails.
Now I got the following problem, which belongs to this subject:
I will explain it on the Project - Task example.
I have an action called <b>addtask</b>
that action creates a form and the possibility to add one task to this project.
Now that works fine.
Now comes the tricky part:
Every project belongs to a person and in the <b>addtask</b> I can changed that person.
Now I want that to write this person_id in the task tabel. So I now which task is ment for whom.
How can I do that?
I hope you can help me Ryan...
Best regards!
Fjord
somebody?
The html it generates is not valid I don't think. Each text field has the same id... ie
<input id="project[task_attributes]__name" name="project[task_attributes][][name]" size="30" type="text" />
this is a problem when you are trying to make each field autocomplete with scriptaculous because it needs a unique id for each input, and this is ignoring the fact they should be unique anyway.
First, let me say 2 thinks - 1) I'm a noob and 2) I really appreciate these!!
I tried this with rails 2.1 but have some problems and not sure if it's do to how 2.1 works or due to my noobiness. When I tried this, it doesn't actually save the tasks, only the project. Also, my screen doesn't show the 3 tasks as part of the record as your screencast does. What gives? Any ideas?
Please disregard my previous posts, I was able to solve this.
Does anyone know how to use fields_for with :multipart => true in order to upload images?
Ok, I just added the :multipart => true to the form_field and it works. Should have tried before....
Thanks for the great tutorial. I'm new to Rails and loving it.
I'm currently working on a form that requires creating 3 models where each belongs_to the previous. Can I use the same technique to go more than 2 levels deep? For example, instead of just being able to create a Project and it's associated Tasks on the same form, can I do something like create a Category, Project, and Task on one form? Not sure exactly how fields_for works.
@Mina: I've found (what I feel) is a cleaner way to handle the validation issue. I add a dummy project_id in my build call, which is neatly overwritten with the actual project_id when the task is saved. Using the example above:
tasks.build(attributes.merge(:project_id=>999))
This works, assuming task validates :project_id, not :project.
If this makes anyone scream, please let me know. It's just the best workaround I was able to come up with in my current, time-sensitive project.
Hi, thx for the great tutorial, but i have some problems for with the request parameters the view sending to the controller:
i have a "voting" model which has a has_many "voting_timeslots". when saving the voting with one voting_timeslot it works great but adding more voting_timeslot i get "undefined method `vote_timeslotes=' for #<Voting:0x62c418c>" .
This is i think because of the request parameter sended to the controller, the hash isn't sorted:
{"voting"=>{"vote_timeslots"=>[{"time"=>"3",
"day"=>"2008-10-22"},
{"day"=>"2008-10-22"}],
"title"=>"f",
"description"=>"f",
"deadline"=>"October 10,
2008 12:23 AM",
"vote_timeslotes"=>[{"time"=>"3"}],
"location"=>"f"},
"commit"=>"Create Voting",
"authenticity_token"=>"f1d7720b3d68672e20948265a12af397ae6a6097"}
Here i send two voting_timeslots with the voting but they get splitted up by the other voting attributes.
I would be very greatful for every help
Curious how you got your tasks to associate to the :project_id during your setter method .build code.
I have a product model which included product_photos, where i use attachment_fu to allow for the upload of the picture to the product_photos model. I can't associate the project_id in the setter method, and it isn't added during the .save method in create.
All that to say, ryanb, how did you save the project_id to each of the tasks? if you didn't how in god's name do you display three tasks to one project, if they aren't assoicated via the project_id field in the task model?
your help is always appreciate.
oh, and one last thing, i needed to use create instead of build, for some reason?
i kept getting a no method found error. which was odd. using rails 2.0.2 too.
thoughts?
for some reason .build now works. still would like an answer on my first comment post.
I am trying to use this technique to update 3 separate classes, as opposed to 2 with a parent>child>grandchild relationship.
account has_many groups
group has_many users
Building the form is pretty straightforward. And here is what I have for the create action in the controller:
@account = Account.new(params[:account])
@group = @account.groups.build(params[:group])
@user = @group.users.build(params[:user])
@account.save
Using this method account and group are saved by user is not. If build method is not capable of handling more than 1 level then how should i create the user ensuring the group_id is populated with the new group? Any help will be greatly appreciated.
I'm hoping someone is still looking at this comment thread that might be able to help.
I'm trying to add one additional field to the task (as used in the tutorial). Let's say I want to be able to mark a task as "Active" or not with a boolean value.
when I add
<%= task_form.text_field :task %>
<%= task_form.check_box :active %>
the form passes a "active" => "0" for each unchecked item as well as the "1" for the checked items. This gets all wonky and ends up creating extra rows with only the "active" column filled. Is there a way to make check_box work correctly here?
A Pastie for some detail on my situation:
http://pastie.org/288094
Wow. This topic has a lot of comments. I started reading through them to look for an answer to my question, but I didn't make it very far.
My question is this: is there a way to clear up any confusion from using multiple attributes of the child objects? I now see the last comment (#121) from Rustin is somewhat along these lines. If I wanted to set the task name as well as whether it should be active or not, the params sent back to my controller might look something like:
{"project":
{"name": "my project",
"tasks":
[ {"name": "foo"},
{"name": "bar"},
{"active": "t", "name": "blah"},
{"active": "f"},
{"active": "t"} ]
}
}
Which is not very useful. Advice?
What is the solution for a has_many :through association between models? I have Movie has_many Actors through Roles - do I build on the join-table Roles?
Someone enlighten me please, as I get stringify_keys error when i try to adapt this to the join-table model attributes!
Great work Ryan, have learnt so much from your work!
@pjam : The way build works is that it stores the child objects in an array in memory and it links that array to the parent object. When the parent object is saved, it saves all of the child objects at the same time with the proper parent id in place. So, for each of the Tasks, they are stored in an array and after the Project is saved, Rails recognizes the associated array of child objects and saves them as well, tacking on the Project's id in the project_id column (all auto-magically!); gotta love Rails!
I am having problems with collection_select and getting :selected to work correctly. I am trying to use it as a search filter for a model that has associations through a join table (second model) with a third model used in the collection_select. I am very new to all of this, but enjoy the casts, no doubt.
Let me be more specific(apologies). I have :multiple => true in a :collection_select helper, however can't get multiple choices to be selected after I submit. (I can however get one to be highlighted if that's all I initially choose) I am sure it's some hash/array problem with my collection_select(model, object, collection) but can't figure it out.
If I can see params[:model][:object] data inmy controller, shouldn't @model = Model.find_by_id(params[:model][:object] be ok as my instance to set the selected with an :object accessor method?
Great cast Ryan,
I would like to know what you think of this approach:
http://www.ghostonthird.com/2007/11/25/a-rails-form-that-creates-multiple-models-at-once/
Its a little different than yours and I trying to figure out the advantages/disadvantages of using one or the other.
Great Tutorial
But when i use it for my project
i get an uninitialized constant error.
I have a payslip model which has many payslip adjustments
so the statement @payslip.payslip_adjustments.build raises the error "Uninitialised constant" Any help will be appreciated. Thanks
For some reason, when I submit my form it seems to create both the project and tasks, but the tasks get created with a NULL value for "project_id", so they're not being understood as the project's children. Any idea how this might be happening or how to manually correct it?
This is driving me batty. The project AND task create fine, but project_id in the task is NULL, so the task gets essentially "lost" in the db. One thing I notice in the log is that the sql "Task Create" is happening before the sql "Project Create" -- so the (parent) project doesn't HAVE an id when the (child) task is being created. Any idea how to fix?
hey brian,
I am having the same problem. My guess is that something has changed in the newer version of Rails since this screencast was created. I don't know what it is but you are not alone.
hey Ryan,
Firstly, thanks for all the hard work you put in to help the community. We really do appreciate it. I am having the same issue as brian and Jonathan. I have a
modelX
has_one :ownership
has_one :user, through => :ownership
I tried following the tutorial and noticed recommendations to try the ownership.build(<new instance based on passed in params>) approach.
So my call in the model looks like this:
def ownership_attr=(ownerships)
ownerships.each do |owner|
ownership.build(:o => Ownership.new(owner)
end
Then when I hit the create button I get an exception nil.build. I have tried Ownership.create!(params passed in) but that leaves the ModelX_id to nil.
Help is greatly appreciated.
Thanks again
FYI I posted a modified version of Ryan's complex form sample app that includes auto_complete behavior; for more info see:
http://patshaughnessy.net/repeated_auto_complete
I browsed the comments, and don't think anyone has had a similar problem.
A record is created for the virtual attributes even when it's blank.
That is I have Profile.emails.build. I am not validating the presence of email, but I don't want a record to be created when no email is entered.
In this case, I get:
{"commit"=>"Create your profile",
"profile"=>{"new_email_attributes"=>[{"email"=>""}],
...
An object is created in the emails model.
In the case of your tutorial, it would be similar to an empty task object being created for the project model.
The only difference I can think of is my profile model belongs to the user model.
Anyone have any ideas why the email object is being created in my database?
Thanks.
How would you post this data to a Rails controller using XML?
Is this format correct?
http://pastie.org/387041
I'm trying to do this same thing except that I want a form that allows a user to create multiple projects and multiple tasks all at once, then save. So I want each project in an array, similar to how each task would be in an array, but I can't seem to separate each project, it just puts all the tasks together. Any thoughts?
What is projects_path?. Thanks!
Hi
I did exactly what is there in the video and its working, But whenever i try to update the same i got the following error
undefined method `stringify_keys!' for "6":String
My Models are
*************
class Product < ActiveRecord::Base
has_and_belongs_to_many :members
belongs_to :category
has_many :installments
def installment_attributes=(installment_attributes)
installment_attributes.each do |attributes|
installments.build(attributes)
end
end
end
*******************************
class Installment < ActiveRecord::Base
belongs_to :product
end
_______________________________
and the products_controller looks like this->
def update
@product = Product.find(params[:id])
respond_to do |format|
if @product.update_attributes(params[:product])
flash[:notice] = 'Product was successfully updated.'
format.html { redirect_to(@product) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @product.errors, :status => :unprocessable_entity }
end
end
end
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
and the stack trace is->
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2736:in `attributes='
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2439:in `initialize'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/reflection.rb:162:in `new'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/reflection.rb:162:in `build_association'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:418:in `build_record'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:102:in `build'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:100:in `build'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:100:in `collect'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:100:in `build'
/home/dwija/matrix/app/models/product.rb:8:in `installment_attributes='
/home/dwija/matrix/app/models/product.rb:7:in `each'
/home/dwija/matrix/app/models/product.rb:7:in `installment_attributes='
/home/dwija/matrix/app/controllers/products_controller.rb:65:in `update'
/home/dwija/matrix/app/controllers/products_controller.rb:64:in `update'
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
I could not understand why the error is coming ?
Any advice?
Duminy
Hi, I had to do something today as Duminy and I got the same mistake.
I read somewhere that it may be the fact that what we are passing to Model.new or Model.update is no longer simple hash because it contains an array inside...
I am still confused but I get as well stringify_keys! method missing.
Thanks for the help
Ryan,
im trying to follow your example in my rails setup (2.3.2 version) and my task_attributes= setter method doesn't even get invoked. did something change in 2.3.2?
thanks
Ryanb sounds familiar in different blogs.. I'm developing an accounting system but it takes so many time in creating models in one app..Im so confused...
Ryan i had
You have a nil object error
is this parameter right?
category_attributes"=>[{"id"=>"83"}]}
i want to bind category or categories to a product.
why don't these mov files play in vlc ? I hate quicktime
ya yayınla siktirtme belanı..
Ok I'd like to do this but I don't want to create a new project.
Here's what I have...
a User has_many References
The applicant is already created ahead of time. But how do I create that array... I feel like this should be simple but I'm having a hard time with it.
References Controller: http://pastie.org/605600
New References view: http://pastie.org/605604
I'm pretty sure my view isn't right... still learning...
Should I be doing this from the User controller or the References controller? I didn't think I would do this out of the User controller because I'm not creating a new user just new references.
Any thoughts? Thanks.
This is exactly what I was looking for! Thanks for explaining it.
Hi, When I try adding this to my project I keep getting an error, which I've mentioned below. I've slightly changed it where I'm not dooing the 3.times. Apart from that I think its pretty much the same. Can someone please help? I'm new to rails, so please be gentle :)
When I add this to my project I get the following error:
NoMethodError (undefined method `build' for ["name", "peter"]:Array):
app/models/look.rb:6:in `business_card_attributes='
app/models/look.rb:5:in `each'
app/models/look.rb:5:in `business_card_attributes='
app/controllers/looks_controller.rb:17:in `new'
app/controllers/looks_controller.rb:17:in `create'
this is what I get from my params:
Processing LooksController#create (for 127.0.0.1 at 2009-09-16 22:04:34) [POST]
Parameters: {"commit"=>"Submit", "look"=>{"business_card_attributes"=>{"name"=>"peter", "title"=>"director"}, "name"=>"test", "intro"=>"new"}, "authenticity_token"=>
"OpvpZZMCmEGYt/MOC21xXiyeYAb+0WIZ090giSNn9Rg="}
this is my form:
<% fields_for "look[business_card_attributes]", @business_card do |business_form| %>
<p>
<%= business_form.label :name %><br />
<%= business_form.text_field :name %>
</p>
<p>
<%= business_form.label :title %><br />
<%= business_form.text_field :title %>
</p>
<% end %>
this is my model:
class Look < ActiveRecord::Base
has_one :business_card
def business_card_attributes=(business_card_attributes)
business_card_attributes.each do |attributes|
attributes.build(attributes)
end
end
end
Ryan,
I really appreciate your screencasts! I am fairly new to RoR and I am wondering whether or not I need to add "accepts_nested_attributes_for ..." to the parent? I currently have this functionality working but I am curious if this is a deprecated requirement or what since I do not see you using it and it appears to work successfully without it. Much of the other reading/online resources show "accepts_nested_attributes_for ..." in the examples and I do see it on the Rails API pages. If I do need to incorporate "accepts_nested_attributes_for ..." when and why is it required?
Thanks ... Jeremiah
Is it possible to do this where (to use your example):
- there is another table that has a list of task categories.
- if there are n categories in the controller there would be n.build (the easy bit)
- rather than having a collection_select next to each item, the partial would loop through each of them and have the ability to provide a value. easy enough with a hidden value to do this, but I'm thinking the edit form could get ugly very quickly
Or ... is this the wrong approach completely?
The scenario I am thinking of is where I have products that have user-definable extended attributes, that each needs a value. But the extended attributes are consistent for all products in a category
Realise that this may be out of scope and the cast is old. But I'm sure this must be a common problem?
Any help would be eternally appreciated
Michael
Rookie question: I've followed the tutorial through to the mid-way point where Ryan says "Let's take a look at our code...", refreshes the UI and we see 3 tasks below the Project. I refresh my project screen and my code shows a "NoMethodError" in the Controller with an undefined method for the tasks that were added. Not sure what's up since I've pretty much followed the demo.
What I don't know is that I've created a Tasks model and I don't know what that should look like since it's not covered in the demo. Could this be the issue? Or did I miss scaffolding something? Let me know what to post that might be helpful to debug. I'm using Rails 2.3.4. & am new to this...
Ryan B has a set of sample code for this on github:
http://github.com/ryanb/complex-form-examples
This goes into more detail including show, index, edit, etc.
Since this screen cast was made 2 years ago, I suspect a lot of this is out of date.
Now we would use
accepts_nested_attributes_for :tasks
and then do something like
form_for @project do |project_form|
project_form.fields for :tasks ...
Right? Or is that different funcionality?
What if i have radio_buttons on a page for each fields_for the :name param is the same and I can check just 1 value for all fields_for, how can i fix this?
Does anyone know if there's a way to include values from multiple models in a single collection_select?
Thanks!
I think to merge arrays of different model values
I run in such problem, that i have a validation in task model of presence project_id,
Solution was: tasks.build(attributes.merge(:project_id => self), to merge attributes array with project_id.
I wonder is there a better solution to do this?
and why "tasks.build" in project model doesn't build this relation?
I am using Rails3, ruby 1.9.2
FYI if you're using Rails 3 you need to change the line:
to:
This episode has been updated to Rails 5 as a blog post Complex Forms in Rails 5.