HMT or HABTM for Orders and Items - ruby-on-rails

Fairly new to rails and trying to understand which relationships to use before going forward.
I have two models: orders and items. This is a many to many relationship, but I'm unsure of which relationship to use.
Orders might have delivery time, quantity of items, etc.
Lastly, what would you call the model joining orders and items if using HMT?

If you need to know anything else about the relationship of the item on a particular order, you need HMT.
If your items change price in the future, do you want to know how much they were sold for on orders in the past?
In this type of requirement, I've always had many "LineItem" records for an order, and the line_item instances belong_to to the item and order, and record the pricing and/or quantity for that order.
HMT vs HABTM? There are so few times that all you need is a many-to-many, that I'd almost always go with HMT for the extra ability to add more information to the association.

This seems like a classic case of HABTM, and the example given in the Rails Guides is perfect. The choice comes down to whether you need any other data or logic on the join model itself. If so, then use the HMT, where you will create a third active_record model to serve as the join table. You can name that anything you want. But it seems like HABTM will work for you, and all you need to setup is the join table with the default name (items_orders) in your migration, and rails will take care of everything else for you.
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end

Related

Referencing attributes across multiple tables in Rails

Let me try and condense my question:
I want to display data from multiple tables in a particular view. I want to list every person I have in a "People" table, and append there job on an "Affiliations" table listed with their company from an "Employers" table. Affiliations should belongs_to People and Employers, and Employers and People have_many Affiliations. What would the migration and controller look like?
I'm not entirely sure if this is the same as what you're asking, but using has_many and belongs_to may be a better solution. Using these associations would allow an employee to have many employers and then simply get the most recent one.
Please feel free to correct me if this isn't what you are asking.

Look up table, enum or a function to return value from key in Rails

I have two models, Tutor and Student. Tutor can have multiple Topics he can cover, and Student can have multiple Topics he would like to learn. There are 10 possible topics (in string).
I am thinking of creating a Topic table, which contain topic strings. But it would create unnecessary repetition of these strings (making table heavy). So I create a Topic table which contains only topic key.
However, I am undecided about how to retrieve value:
First, I can create another lookup table, which maps a key to string value. This will result in an extra merging step.
Second, I can have a class function that belongs to Topic, that returns string from value.
Which way would be more efficient in my situation? Is there a better approach that I haven't thought of?
Thank you.
It depends. IMO "topics" sounds like something that need managing, and may change.
If that's the case, there should be a topic table, with an id, name, probably a description, etc. Both tutors and students would have_many topics :through a join table. Topics would belong_to both.
There are several implementations options, including a polymorphic association of topics.
Assuming a Tutor model could be rolled into a User model with role assignments, setup a has_and_belongs_to_many relationship between Users and Topics. This sets up a join table where the foreign keys are listed to join the heavier rows together.
class User < ActiveRecord::Base
has_and_belongs_to_many :topics
end
class Topic < ActiveRecord::Base
has_and_belongs_to_many :users
end
See the Rails Guide for additional description.
The alternative is to use just a has_many association but it lacks a join table so the Topic entries will need to be duplicated for each instance.

Adding categories to a Ruby on Rails application

I've had some issues with this before when creating applications and I think I'm starting to run into it again, hence I'm asking this on StackOverflow to save me a lot of time.
I've spent the last few weeks setting up a perfected product model for my system. The model performs exactly as I want it to and has several complex features (such as search via sunspot). I wanted to setup the category to product structure before I started this heavy development - however struggling with this kind of thing was just putting me off creating the application so I got straight into the product structure.
Now I've got the product model setup - what would be the easiest way to add a category ownership to encompass the products? (All products have a category_id column which store their father category id)
My plan is to have the category index to be a list of all the categories, the category show to be a list of the products inside that category and the product show being the view of the actual product. This would eliminate the product index and so I'll have to come up with a way to port the search feature (sunspot) from my index view to the category show somehow.
As for the actual listing of the products - I assume I'll have to do some kind of partial? (I don't know a lot about it).
Most basically, my relationships are planned to be:
category:
has_many :products
product:
has_one :category
My products then have a category_id column to store the ID of it's parent category.
Any tips on how to accomplish the relationships (category show to list the products etc)?
Best Regards,
Joe
Relationships like the one you're wanting are built into ActiveRecord support. Understanding the model relationships in Rails is critical to doing anything in Rails that's non-trivial, so study up.
Also, the relationship you're looking for is something like:
product:
belongs_to :category
category:
has_many :products

Confusion over the tables for has_one and has_many

In the Rails ActiveRecord Associations guide, I'm confused over why the tables for has_one and has_many are identical:
Example tables for has_many:
customers(id,name)
orders(id,customer_id,order_date)
Example tables for has_one:
these tables will, at the database level, also allow a supplier to have many accounts, but we just want one account per supplier
suppliers(id,name)
accounts(id,supplier_id,account_number) #Foreign Key to supplier here??
Shouldn't the tables for has_one be like this instead:
suppliers(id,name,account_id) #Foreign Key to account here
accounts(id,account_number)
Now because the account_id is in the suppliers table, a supplier can never have more than one account.
Is the example in the Rails Guide incorrect?
Or, does Rails use the has_many kind of approach but restricts the many part from happening?
If you think about this way -- they are all the same:
1 customer can have many orders, so each order record points back to customer.
1 supplier can have one account, and it is a special case of "has many", so it equally works with account pointing back to supplier.
and it is the same case with many-to-many, with junction table pointing back to the individual records... (if a student can take many classes, and one class can have many students, then the enrollment table points back to the student and class records).
as to why account points back to supplier vs account points to supplier, that one I am not entirely sure whether we can have it either way, or one form is better than the other.
I believe it has to do with the constraints. With has_one rails will try to enforce that there is only one account per supplier. However, with a has_many, there will be no constraint enforced, so a supplier with has_many would be allowed to exist with multiple accounts.
It does take some getting used to when thinking about the relationships and their creation in rails. If you want to enforce foreign keys on the database side (since rails doesn't do this outside of the application layer), take a look at Mathew Higgins' foreigner
If I understand your question correctly, you believe there is a 1:1 relationship bi-directionally in a has_one/belongs_to relationship. That's not exactly true. You could have:
Class Account
belongs_to :supplier
belongs_to :wholesaler
belongs_to :shipper
# ...
end
account = supplier.account # Get supplier's account
wholesaler = Wholesaler.new
wholesaler.accounts << account # Tell wholesaler this is one of their suppliers
wholesaler.save
I'm not saying your app actually behaves this way, but you can see how a table -- no, let's say a model -- that "belongs to" another model is not precluded from belonging to any number of models. Right? So the relationship is really infinity:1.
I should add that has_one is really a degenerate case of has_many and just adds syntactic sugar of singularizing the association and a few other nits. Otherwise, it's pretty much the same thing and it's pretty much why they look alike.

Rails ActiveRecord Relationships

How do the relationships magically function when only the models are altered?
If I want a "has__and___belongs___to__many" relationship, what should I name the table (so Rails can use it) that contains the two foreign keys?
Short answer: You can't just tell the models that they're related; there have to be columns in the database for it too.
When you set up related models, Rails assumes you've followed a convention which allows it to find the things you wrote. Here's what happens:
You set up the tables.
Following conventions in Rails, you name the table in a particular, predictable way (a plural noun, e.g. people). In this table, when you have a relationship to another table, you have to create that column and name it in another predictable way (e.g. bank_account_id, if you're relating to the bank_accounts table).
You write a model class inheriting from ActiveRecord::Base
class Person < ActiveRecord::Base
When you instantiate one of these models, the ActiveRecord::Base constructor looks at the name of the class, converts it to lowercase and pluralizes it. Here, by reading Person, it yields people, the name of the table we created earlier. Now ActiveRecord knows where to get all the information about a person, and it can read the SQL output to figure out what the columns are.
You add relationships to the model: has_many, belongs_to or has_one.
When you type something like, has_many :bank_accounts, it assumes a few things:
The name of the model that you relate to is BankAccount (from camel-casing :bank_accounts).
The name of the column in the people table which refers to a bank account is bank_account_id (from singularizing :bank_accounts).
Since the relationship is has_many, ActiveRecord knows to give you methods like john.bank_accounts, using plural names for things.
Putting all of that together, ActiveRecord knows how to make SQL queries that will give you a person's bank accounts. It knows where to find everything, because you followed a naming convention that it understands when you created the table and its colums.
One of the neat things about Ruby is that you can run methods on a whole class, and those methods can add other methods to a class. That's exactly what has_many and friends are doing.
This works because you are following "Convention over Configuration".
If you state that a customer model has many orders then rails expects there to be a customer_id field on the orders table.
If you have followed these conventions then rails will use them and will be able to build the necessary SQL to find all the orders for a given customer.
If you look at the development.log file when you are developing your application you will be able to see the necessary SQL being built to select all orders for a given customer.
Rails does not create tables without you asking it to. The creation of tables is achieved by generating a migration which will create/alter tables for you. The fact that you create a customer model and then state within it that it has_many :orders will not create you an orders table. You will need to do that for yourself within a migration to create an orders table. Within that migration you will need to either add a customer_id column or use the belongs_to: customer statement to get the customer_id field added to the orders table.
The rails guide for this is pretty useful

Resources