Newb question:


#1

Hi all,

Another newb online… trying belongs_to and has_one constructs for
the first time.

table Location defined as:
nickname, street, city, … , contact_id

the model includes “belongs_to :contact”

table Contact defined as:
name, title, … , location_id

the model includes “has_one :location”

When I build a list_contacts view, I assume
h(contact.location.nickname) will make the connection. However Rails
returns nil for contact.location. (Verified the linkage is present,
thanks to script/console.) Seems like I’m missing something very
simple, but I’m stumped. Ideas?

Best,
Brian
Austin, Texas


#2

removed_email_address@domain.invalid wrote:

When I build a list_contacts view, I assume
h(contact.location.nickname) will make the connection. However Rails
returns nil for contact.location. (Verified the linkage is present,
thanks to script/console.) Seems like I’m missing something very
simple, but I’m stumped. Ideas?

Best,
Brian
Austin, Texas

Can you post your controller code for list_contacts? If you have
‘dereferenced’ your scaffold code also make sure you have an instance
variable that holds the information for your contact in the controller
or else your view wont have anything to display.

Also, maybe you just left this out, but contact.location is nil but is
just contact nil also?

Lastly, your table should be named w/ pluralizes names.

Ex: ‘create table Contacts …’ and ‘create table Locations…’ not the
singular version you posted in your original mail. Remember models are
singular and the db tables they are tied to are pluralized.

I’ll assume that you posted just pseudo code but I had to mention it
just in case.

Hope this, at the least, kicks off a direction for a solution to your
problem.


#3

Hello Jean,
I appreciate the reply. As requested:

controller:

def list_contacts
@contact_pages = Paginator.new self, Contact.count, 60, params[:page]
@contacts = Contact.find :all, :order => params[‘order’] ||
‘lastname’,
:limit => @contact_pages.items_per_page,
:offset => @contact_pages.current.offset
end

view:

… various html …
<% for contact in @contacts
# location = Location.find(contact.location_id)
%>


<%= link_to h(contact.lastname+’,’+contact.firstname), :action
=> ‘edit_contact’, :id => contact.id %>
<%= h(contact.title) %>
<%= h(contact.email1) %>
<%= h(contact.phone1) %>
<%= h(contact.location.nickname) %>
… various html …
<% end %>

Notice the "Location.find(contact.location_id) … this works…

Tks again…
Brian

On Jul 24, 12:02 am, Jean N. removed_email_address@domain.invalid


#4

Chris wrote:

Option 1:
contacts = Contact.find(:all)

Each call to c.location triggers a DB query to delay load the

location for that contact
contacts.each { |c| c.location.nickname }

Option 2:
contacts = Contact.find(:all, :includes => [:location])

All locations are loaded upfront, so there is only ever 1 DB call

contacts.each { |c| c.location.nickname }

Cheers,
Chris

On Jul 24, 3:02 pm, Jean N. removed_email_address@domain.invalid

Adding to this, remove any DB calls in your view. this breaks the model
/ view/ controller architecture as you’re having a view interact
directly WITH the Database.

Encapsulate ALL ‘business logic / use cases’ and database access in your
controller and let your view determine how all the data ‘sent’ to it is
displayed. Even using a loop and querying the db for your location,
although a minor infraction, is considered logic and not display.


#5

You have 2 columns locations.contact_id and contacts.location_id. Of
these, only locations.contact_id is used by the associations you have
defined.

class Location < AR::Base

This will lookup locations.contact_id to find the association

belongs_to :contact
end

class Contact < AR::Base

This will also lookup locations.contact_id to find the association

has_one :location
end

Make sure that locations.contact_id is populated in your DB, and that
you haven’t mistakenly populated contacts.location_id instead.

BTW, if you need to query a list of contacts, think about the
performance of using associations.

Option 1:
contacts = Contact.find(:all)

Each call to c.location triggers a DB query to delay load the

location for that contact
contacts.each { |c| c.location.nickname }

Option 2:
contacts = Contact.find(:all, :includes => [:location])

All locations are loaded upfront, so there is only ever 1 DB call

contacts.each { |c| c.location.nickname }

Cheers,
Chris

On Jul 24, 3:02 pm, Jean N. removed_email_address@domain.invalid