Wednesday, September 8, 2010

Sequel Models one_to_one Associations

We looked in the past at both one to many/many to one and many to many/many to many associations, but we haven't looked at one to one associations yet. I was looking at this for addresses where a couple of different classes would use them so I wouldn't want to embed them in each table. The documentation here is good, but I needed a bit of help (and straightening out) from Jeremy Evans and the Sequel group, so I thought this might be a good topic. Here's the code we'll be using ...


require 'rubygems'
require 'sequel'

# Create an in-memory database.
DB = Sequel.sqlite

# Create a businesses table with a name an address.
DB.create_table :businesses do
primary_key :id
String :name, :text=>true, :unique=>true
foreign_key :address_id
end

# Create a people table with a name and an address.
DB.create_table :people do
primary_key :id
String :name, :text=>true, :unique=>true
foreign_key :address_id
end

# Create a table of addresses that can be used by both people
# and businesses. This is just an example and real addresses would
# be done with street addresses, cities, states, countries, etc.
DB.create_table :addresses do
primary_key :id
String :address
end

# From the documentation ...
# Differences Between many_to_one and one_to_one
# If you want to setup a 1-1 relationship between two models, you have to use
# many_to_one in one model, and one_to_one in the other model. How do you know
# which to use in which model? The simplest way to remember is that the model
# whose table has the foreign key uses many_to_one, and the other model uses
# one_to_one:
#
# For our case, the people and businesses have the foreign key and use the many_to_one and
# addresses will use one_to_one for both.

# The business model backed by the businesses table. Note the
# many to one for addresses.
class Business < Sequel::Model
many_to_one :address # Note this is singular.
end

# The person model backed by the people table. Note the
# many to one for addresses.
class Person < Sequel::Model
many_to_one :address # Note this is singular.
end

# The address model backed by the addresses table. Note the
# one to one for both business and person.
class Address < Sequel::Model
one_to_one :business # Note this is singular.
one_to_one :person # Note this is singular.
end


# Create a new business and person.
business = Business.create(:name => "The Business")
person = Person.create(:name => "Bob")

# Create new addresses for each of the above.
address_1 = Address.create(:address => "123 W. Lane, Anytown, CA 91110")
address_2 = Address.create(:address => "124 W. Lane, Anytown, CA 91110")

# Add the addresses for each of the above. Note that these use setter methods and not the
# add_ that you would see for many_to_one and many_to_many relationships.
business.address = address_1
person.address = address_2

# Print them out to show we got them correct.
puts "Business: #{business.name} at #{business.address.address}"
puts "Person: #{person.name} at #{person.address.address}"


We start out creating the database and the tables we'll use. The businesses and people tables both contain foreign keys to the addresses table and a name (in real life, these would be much more complex). The addresses table simply contains the address. Next we have the models that represent the tables. Be sure to read the comments that come directly from the documentation and that will help your understanding. Basically, in the table that has the foreign key (in this case businesses and people), the corresponding models will have a many_to_one association with the other model (in this case addresses). Note that both the many_to_one and one_to_one associations should take a singular identifier. After this, we'll create a business and a person, a couple of addresses, and then link them together. Here, we need to use the setter method (.address and not add_address() as I first tried (once again, it's in the documentation, but I obviously missed it)). Finally, just to make sure we print everything out.

Let me know if you have questions or comments.

No comments:

Post a Comment