Create and save nested data

Hi,

I am working on a patent database, where the user can first search the patent database and then save the found patent with connected patents (called familymembers). So I have a page called "patent" where the search is done and all values that I need for the database are available.

From that page I want to store the patent with his familymembers into my database, to watch them. So I have the following models

class Watchedfamily < ActiveRecord::Base   has_many :watchedmembers   accepts_nested_attributes_for :watchedmembers end

class Watchedmember < ActiveRecord::Base   belongs_to :watchedfamily end

On my patent page I tried to save the data with a form that looks like this (I had to define the controller that i want to use, because this form is not in the watchedfamiles view!):

<% form_for :watchedfamily, :url => {:controller => "watchedfamilies", :action => "create" } do |f| %>     <%= f.text_field :Prio_No, :value => @prio_no %>     <%= f.text_field :title, :value => @title %>     <%= f.fields_for :watchedmembers_attributes do |x| %>       <p>       <%= x.text_field :Pub_No, :value => "Test" %>       </p>     <%end%>     <p><%= submit_tag "Save hidden form" %></p>   <% end %>

That is not working! Error is: "can't convert Symbol into Integer"

If I create the nested data directly in the watchedfamilies by clicking new and using the form below it is working:

<%= form_for(@watchedfamily) do |f| %>   <div class="field">     <%= f.label :Prio_No %><br />     <%= f.text_field :Prio_No %>   </div>   <div class="field">     <%= f.label :title %><br />     <%= f.text_field :title %>   </div>    <%= f.fields_for :watchedmembers do |x| %>       <p>       <%= x.text_field :Pub_No, :value => "Test" %>       </p>     <%end%>   <div class="actions">     <%= f.submit %>   </div> <% end %>

The problem is that my first approach is generating the following request:

Parameters: {"watchedfamily"=>{"Prio_No"=>"123456", "title"=>"Whatever", "watchedmembers_attributes"=>{"Pub_No"=>"Test"}}, "commit"=>"Create Watchedfamily"}

Where my second approach, directly from the watchedfamilies is generating the following request:

Parameters: {"watchedfamily"=>{"Prio_No"=>"123456", "title"=>"Whatever", "watchedmembers_attributes"=>{"0"=>{"Pub_No"=>"Test"}}}, "commit"=>"Create Watchedfamily"}

First one watchedmembers_attributes is a string, second one is an array.

If I change the line in my form from <%= f.fields_for :watchedmembers_attributes do |x| %> to <%= f.fields_for :watchedmembers do |x| %>

I got the error: "Watchedmember(#26928120) expected, got Array(#5756196)"

Maybe I am on a wrong track. I just want to save a nested value (patentfamily with its family members to my database!) I already watched tutorials and asked google, but I didn't find a solution to my problem. I hope I get help here!!!

Cheers, Sebastian

OK, I don't know why but it happens all the time that after posting a problem in a forum I am able to solve the problem, nearly!!!

I think I was really on the wrong track!

Now I added the following code in my patent view:

  <%params = {:watchedfamily => {:Prio_No => "666666", :title => "Please", :watchedmembers_attributes => [{:Pub_No => "Test1"}, {:Pub_No => "Test2"}]}} %>   <%Watchedfamily.create!(params[:watchedfamily]) %>

This is creating a new record with two nested records, exactlly what I wanted. Only problem is now: HOW TO FIRE THIS WITH A BUTTON OR LINK???

Cheers

OK, I don't know why but it happens all the time that after posting a problem in a forum I am able to solve the problem, nearly!!!

I think I was really on the wrong track!

Now I added the following code in my patent view:

<%params = {:watchedfamily => {:Prio_No => "666666", :title => "Please", :watchedmembers_attributes => [{:Pub_No => "Test1"}, {:Pub_No => "Test2"}]}} %> <%Watchedfamily.create!(params[:watchedfamily]) %>

This is creating a new record with two nested records, exactlly what I wanted. Only problem is now: HOW TO FIRE THIS WITH A BUTTON OR LINK???

You need to re-think this entirely. The fact that you are calling a Model method directly in a view should be a strong whiff of Code Smell, and you need to consider how to make this a link to a REST (create) method in your WatchedfamilyController rather than what you have here. Getting the result back into your Patent view is an exercise in routing and maybe Ajax, but what you have done in this view shouldn't work, or maybe shouldn't be tried, even if you can get it to work part-way.

Walter

OK again I got it:

  <%= button_to "Show", :controller => "watchedfamilies", :action => "create", :watchedfamily => {:Prio_No => "X5x5x5x5", :title => "Please", :watchedmembers_attributes => [{:Pub_No => "yyy1"}, {:Pub_No => "zzz2"}]} %>

Thanks for the reply!

I am not quite sure what you meant with 'calling a Model method directly', probably this one, or? <%Watchedfamily.create!(params[:watchedfamily]) %>

So I already changed my code as you can see below. Is that still Code Smell or is that better?

  <%= button_to "Save Patent",     :controller => "watchedfamilies",     :action => "create",     :watchedfamily => {     :Prio_No => @Prio,     :title => @title.join(","),     :watchedmembers_attributes => @fam_pubs     }%>

There is another problem I found: If I use my above code to send more data with one click to my database I get an "URI Too Large" error. How can I avoid that? Or how can I store a lot of data without a large URI??? My @fam_pubs array is very large!!!

Sebastian

Sebastian wrote in post #995486:

I am not quite sure what you meant with 'calling a Model method directly', probably this one, or?

Watchedfamily is your Model in the example below, 'create!' is the method called on it and '(params[:watchedfamily])' is what you are passing to that method.

<% Watchedfamily.create!(params[:watchedfamily]) %>

So I already changed my code as you can see below. Is that still Code Smell or is that better?

Read this article: Code Smell

Thank you!

I am still having the problem that the URI gets too large if I want to send to much data to the database. How can I avoid this?

Can I maybe call my @fam_pubs variable in the create method of my watchedfamilies controller, so that it is not needed to send it with the URI?

If yes how would that look like? My create method is still the standard one:

def create     @watchedfamily = Watchedfamily.new(params[:watchedfamily])     respond_to do |format|       if @watchedfamily.save         format.html { redirect_to(@watchedfamily, :notice => 'Watchedfamily was successfully created.') }         format.xml { render :xml => @watchedfamily, :status => :created, :location => @watchedfamily }       else         format.html { render :action => "new" }         format.xml { render :xml => @watchedfamily.errors, :status => :unprocessable_entity }       end     end   end

Cheers, Sebastian

Sebastian wrote in post #995494:

Thank you!

I am still having the problem that the URI gets too large if I want to send to much data to the database. How can I avoid this?

What do you mean by 'the URI gets too large'? If you are running Rails 3, I suggest you read more on nested form attributes when dealing with active record objects:

Yes you were right! I was definitely on the wrong track!

The URL really helped to understand how to create nested active records.

I still have a big problem. I have two methods in my controller SHOW and SAVE.

In the show method I extract a lot of data from an XML into a hash variable. In the save method I want to store that variable in the database. The problem is that my @hash variable is only available in the first method. How can I access a variable from one method to another? Is session or flash a good idea for that, I don't need that data anymore after saving to db.

Simplified methods look like this:

class PatentController < ApplicationController   def show #This method has more than 50 lines code in reality     @hash = REXML::Document.new xmlfile   end

  def create     title123 = params[:title]     pub = @hash     params = {:watchedfamily => {:Prio_No => pub, :title => title123, :watchedmembers_attributes => pub}}     @watchedfamily = Watchedfamily.new(params[:watchedfamily])   end end

Cheers, Sebastian

Or is it better to create that @hash directly in the needed method, like this:

  def create     @hash = REXML::Document.new xmlfile     title123 = params[:title]     pub = @hash     params = {:watchedfamily => {:Prio_No => pub, :title => title123, :watchedmembers_attributes => pub}}     @watchedfamily = Watchedfamily.new(params[:watchedfamily])   end

Or put all the 'extract data from XML code' into a model and just call the model where I need it?

The problem is that I don't know exactly what I should put in a model and what in a controller.

Sebastian

I create the hash as mentioned in my preivious post and that is working as intended.

Thank you for your replies, that gave me the right direction!

Sebastian