SitePoint Sponsor

User Tag List

Results 1 to 14 of 14
  1. #1
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Saving data to two tables

    I am trying to save data to two tables - person and address. But first I want to do a lookup on the address table to make sure that the address entered does not already exist in the address table - if it does I want to extract the residence.id and plug that into the person table. Otherwise, I will just save the data to the respective tables. Here is the code I have tried thus far:
    Code:
     def create # this part of the code does not work because I haven't a clue what I am doing
        @address_check = Residence.find(params[:residence])  #check for an existing address
        @person = Person.new(params[:person])
        if @address_check
    	 @person.residence_id = @address_check.id
    	 if @person.save
    		 flash[:notice] = 'The Person was successfully created.'
    		redirect_to :action => 'list'
    	else
    		render :action => 'new'
    	end
        else  #this part of the code works
    	@residence = Residence.new(params[:residence])
    	if @person.save && @residence.save
    		@person.update_attribute(:residence_id, @residence.id)
    		flash[:notice] = 'The person was successfully created.'
    		redirect_to :action => 'list'
    	else
    		render :action => 'new'
    	end
        end
      end
    The second part of the code works. The first part does not work primarily because I am not sure what I am doing. Is there some kind soul who can give me a hand with this.

    Rick

  2. #2
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The plugging in of your residence id into your person model should be handled by Rails associations.

    I'm not sure how you are determining if an address already exists (Residence.find(params[:residence]) wont work) but lets say you have a house_number and a postcode field, which would be a good way of identifying a unique address.

    It seems to me you want something like:

    Code:
    # residence.rb
    class Residence
      has_one :person # could be has_many
    end
    
    # person.rb
    class Person
      belongs_to :residence
    end
    
    # your controller
    def create
      # will load an existing address if it exists, else create a new one
      residence = Residence.find_or_create_by_house_number_and_postcode(params[:residence][:house_number], params[:residence][:postcode])
      person = residence.build_person(params[:person])
      if person.save and residence.save
        flash[:notice] = 'The person was created successfully'
        redirect_to :action => 'list'
      else
        redirect_to :action => 'new'
      end
    end
    Note that the above hasn't been tested but it should be along the right lines. There isn't any validation in the models either so you'll need to add that, and deal with any errors.

    See the following API docs for more details:

    http://api.rubyonrails.com/classes/A...cord/Base.html
    http://api.rubyonrails.com/classes/A...ssMethods.html

  3. #3
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Luke,

    Thanks for that - I will give it a try. I had a look at the API references but I must admit it will take some time before I get it (that is understand just what is going on). For example, just what does this mean:
    Code:
     residence = Residence.find_or_create_by_house_number_and_postcode(params[:residence][:house_number], params[:residence][:postcode])
    , especially the _or_create_by... Lets say you wanted to add another parameter aptnum. Would the code then read
    Code:
    residence = Residence.find_or_create_by_house_number_and_postcode_and_aptnum(params[:residence][:house_number], params[:residence][:postcode] params[:residence][:aptnum])
    or is everything after "find" just a nemonic that describes the action?

    Sorry for that - I found the answer by reading the documentation - Dynamic attribute-based finders. But I am still puzzeled by the second statement.

    I believe I will have to check for the aptnum also because people in an apt all live at the same address and postalcode but different apts. Should I expect any problems with nil apt numbers.

    Finally, I found the syntax in the API docs but what does this line of code do exactly:
    Code:
    person = residence.build_person(params[:person])
    ?

    Thanks for your patience.

    Rick

  4. #4
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The first code breaks down into two parts.

    First, find_or_create will return a matching record if it exists or a new record if it doesn't.

    Rails supports dynamic name based finders so yes, you could add aptnum in exactly the way you did above.

    See "Dynamic attribute-based finders" here:
    http://api.rubyonrails.com/classes/A...cord/Base.html

    The last method, build_person is a dynamically create method available due to the association made between people and residences. residence.build_person() creates a new Person object that is automatically linked to the residence object you call it on - it creates the link for you. build_person() returns the new person object without saving it. There is also create_person() which does save the associated record.

    See the associations API and look under each association type for the methods it makes available to your objects:
    http://api.rubyonrails.com/classes/A...ssMethods.html

  5. #5
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks Luke for your time and knowledge. The more I find out about ror the more I like it.

    Rick

  6. #6
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Luke,
    I tried your example code and received the following error: undefined method `find_or_create_by_number_and_aptnum_and_postcode' for Residence:Class

    Any idea what generates this error?

    Rick

  7. #7
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Does your residences table have the columns 'number', 'aptnum' and 'postcode'?

  8. #8
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It does now - I completely missed postcode - should have been postalcode. Must have checked it a dozen or more time but couldn't see the forest. Now the following statement
    Code:
    person = residence.build_person(params[:person])
    is throwing a similar error: undefined method `build_person' for #<Residence:0x3716780>. I am using has_many :person in the residence method and belongs_to in the person method. Can you help me on this one.

    Rick

  9. #9
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It should be

    Code:
    has_many :people

  10. #10
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I tried that and it did not work either. It threw the same error. Here is what I have:
    Code:
    class Residence < ActiveRecord::Base
    	has_many :people  #I assume this has to be plural to fit with has_many
    	
    end
    class Person < ActiveRecord::Base
    	belongs_to :residence
    end
    
    # and the line that is throwing the error
    person = residence.build_person(params[:person])
    Is this correct? The pluralization rules are sometimes confusing.

    Rick

  11. #11
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Mmm, should work. What version of Rails are you using?

    Try residence.people.build() or residence.build_people()

  12. #12
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well Luke,

    I have had mixed success. I did get the record to post to the db and everything looks correct at first blush. The code I used was
    Code:
    people = residence.people.build(params[:people])

    Then when it wast to return to the list view I started getting the following error:You have a nil object when you didn't expect it!
    The error occured while evaluating nil.aptnum

    and the ofending line of code is:
    Code:
    <%=h list_stripes.residence.aptnum.nil? ? ' ' : ('Apt.' + list_stripes.residence.aptnum + ' - ')  %>
    I think this is saying if the aptnum is nil (or NULL?) leave it blank otherwise put in the aptnum.

    What would be the cause of this error?

    Rick

  13. #13
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In fact, the error means that list_stripes.residence is returning a nil object (of NilClass) rather than a Residence object, and you are trying to call the method aptnum() on nil, which of course doesn't exist.

    So you need to look at why list_stripes.residence is retruning nil. What is list_stripes?

  14. #14
    SitePoint Zealot ricklach's Avatar
    Join Date
    Nov 2004
    Location
    Victoria BC
    Posts
    116
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    list_stripes is just a helper to alternately color the rows of a table. I did find the problem - I had one row in my person table thad did not have a corresponding row in the residence table. Fixed it and problem solved.

    Finally, how do you show an email image with an address attached to it. I have tried numerous mail_to and image_tag variations but just can't seem to figure it out.

    Rick
    Last edited by ricklach; Mar 5, 2006 at 05:55.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •