Has_one() in AR kind of weak?

Hi,

suppose I’m doing a rails system for the DMV.

The rules say:

A person may have 0 or 1 Drivers License.

So, I’d have these 2 tables:

class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.column :name, :string
end
end

def self.down
drop_table :people
end
end

class CreateDriverslicenses < ActiveRecord::Migration
def self.up
create_table :driverslicenses do |t|
t.column :person_id, :integer
end
end

def self.down
drop_table :driverslicenses
end
end

And these models:

class Person < ActiveRecord::Base
has_one :driverslicense
end

class DriversLicense < ActiveRecord::Base
belongs_to :person
end

What is the best way to enforce this rule:

A person may have 0 or 1 Drivers License.
??

I ran a simple test with console:

pete = Person.new
=> #<Person:0x2364d9c @attributes={“name”=>nil}, @new_record=true>
pete.name = ‘Pete’
=> “Pete”

dl1 = Driverslicense.new
=> #<Driverslicense:0x235ac0c @attributes={“person_id”=>nil},
@new_record=true>

dl1.person_id = pete
=> #<Person:0x2364d9c @errors=#<ActiveRecord::Errors:0x23511ac
@errors={}, @base=#<Person:0x2364d9c …>>,
@attributes={“name”=>“Pete”, “id”=>1}, @new_record=false>

dl1.person.name
=> “Pete”

So, now Pete has a drivers license.

Let’s try to give him another drivers license:

dl2 = Driverslicense.new
=> #<Driverslicense:0x233de2c @attributes={“person_id”=>nil},
@new_record=true>
dl2.person_id = pete
=> #<Person:0x2364d9c @errors=#<ActiveRecord::Errors:0x23511ac
@errors={}, @base=#<Person:0x2364d9c …>>,
@attributes={“name”=>“Pete”, “id”=>1}, @new_record=false>

dl2.person.name
=> “Pete”

It appears that dmv has assigned 2 drivers licenses to Pete.

Is it up to me to ‘strengthen’ the has_one() method in AR?

Here is info about my rails:

$ script/about
About your application’s environment
Ruby version 1.8.4 (powerpc-darwin7.9.0)
RubyGems version 0.9.0
Rails version 1.1.6
Active Record version 1.14.4
Action Pack version 1.12.5
Action Web Service version 1.1.6
Action Mailer version 1.2.5
Active Support version 1.3.1

-Pete
http://GoodJobFastCar.com
[email protected]

You could add a uniqueness validation on the Driverslicense model

class Driverslicense < ActiveRecord::Base
validates_uniqueness_of :person_id
end

-Jonathan.

In addition to validates_uniqueness_of your database probably has an
option to enforce this kind of thing (eg add a unique index to the
person_id column of driver_licenses

Fred

Peter S. [gjfc] wrote:

It appears that dmv has assigned 2 drivers licenses to Pete.

Is it up to me to ‘strengthen’ the has_one() method in AR?

Hi Pete

The associations methods (has_one, belongs_to, etc) do not do any
validation. They simply create meta-programmed accessor methods on your
model classes that know how to query the DB for associated objects.

Considering has_one vs. has_many, the name of the accessor is affected
by the association method as well. In your case, Person.has_one
:drivers_license will create an accessor “drivers_license” on your
Person class (in memory). If you had used has_many, an accessor named
“drivers_licenses” would have been created instead. Another difference
is that, though the same finder SQL will be used regardless of whether
you use has_one or has_many, the has_one finder will only return the
first matching record, whereas the has_many will return all matching
results.

As for ensuring that a has_one association is enforced, a good practice
is to define a unique index or key on your DB:

If A has_one B, then a unique index should be defined on B’s foreign key
into A (eg. B.A_id ).

Hope this helps.