I am new with rails and trying to understand its :has_many and :belongs_to functionalities.
If I have 3 tables: Customers
, Physicians
, Addresses
Each customer and each physician will have one address. So it is a one-to-one relationship.
customer_id and physician_id will match address_id
So if I want 开发者_运维知识库address of a customer with id 3. I'd say
select * from customer, addresses
where customer_id = 3 and customer.customer_id = addresses.address_id
How will I translate this into rails code?
I'll have 3 models Customer
, Physician
, Address
But I am not sure as to what the relationship be?
How will i translate the above query to rails find
function?
Customer.find (:all, ......?
I wouldn't share the PK between the tables. What if you have Physician (id = 1), Customer (id = 1). Then who gets Address (id = 1). This is the schema I'd propose
Address
-------
id
Customer
--------
id
address_id
Physician
---------
id
address_id
Then, to bind in your class.
class Physician < ActiveRecord::base
belongs_to :address
end
class Customer < ActiveRecord::base
belongs_to :address
end
class Address < ActiveRecord::base
has_one :physician
has_one :customer
def customer_address?
!customer.nil?
end
def physician_address?
!physician.nil?
end
end
It may look a little backwards, but look at the FK relationships. Address is referenced by Physician and Customer. Not the other way around.
The docs will say that you almost never want a has_one relationship. I use them, but only when I reinforce them with unique IDs in the database. So make sure that you add a migration like the following.
self.up
add_index :customers, :address_id, :unique => true
add_index :physicians, :address_id, :unique => true
end
Now, you know that a customer will only ever match to a single address.
Just quickly... you meant in your SQL query "customer.address_id = addresses.address_id" right?
When you put
has_one :address
into the Customer model and
belongs_to :customer
in the Address model, you do the same find as usual, but you can refer to it like:
@cust = Customer.find(params[:id])
street = @cust.address.street
city = @cust.address.city
and so on... check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html where it says "Is it a belongs_to or has_one association?" for more info.
Let's recap some OO principles on this one, as it will help with these answers. Consider:
- What is the benefit of having all of the addresses in one table?
- What is the benefit of having customers and physicians separated? Depending on what you're trying to do, these answers may vary.
In the most likely case, you're trying to model a customer's relationship to their (one? many?) physicians. The customer has a home address and the physician has an office address. And this illustrates one of the first outcomes - that the customer addresses and physician addresses are different. Consider the case where you dump the addresses, or sort them, or do something with them - do you care that both types of addresses are mixed together? If so, they are really two separate entities in your system. Separate them into two tables and you're done.
In a less-likely case, you don't really care about the difference between customers and physicians, only their addresses. Perhaps the association between these classes and their addresses is meant to help to scope the address? You explicitly want the ability to dump the addresses and do something with them, regardless of whether they're for the customer or physician. In any case, the Customer and Physician classes can be combined, since you don't care about the difference.
If you want to make all of thise more object oriented, consider inheritance. You might consider a base class (Person?) for the Customer/Physician which would bring together the common attributes and behaviours (including the association to your Address class) into one entity. And/or consider a base class for the CustomerAddress/PhysicianAddress classes, which implements the common attributes and behaviours (including the association to your Person class) into one entity. You might even consider Rails STI as a nice lightweight and ActiveRecord-supported way to get this done nice and clearly.
精彩评论