do join tables need to use conventional naming in rails? - ruby-on-rails

say I have company and employee tables, does my join table have to be called companies_employees? I don't see anything about this on the rails documentation.

Name of join table can be changed. Here is document of has_and_belongs_to_many.
If the default name of the join table, based on lexical ordering, is
not what you want, you can use the :join_table option to override the
default.
So, you can change it like this:
# Company model
has_and_belongs_to_many :employees, join_table: "comp_emps"
# Employee model
has_and_belongs_to_many :companies, join_table: "comp_emps"

No, in fact none of the tables have to be named a particular way -- that's just convention. You can override the table name for any model, but usually you would do because you have a legacy schema.
And similarly, if you have a model that belongs to both Company and Employee it doesn't have to be named CompanyEmployee (conventional table name would be company_employees) or EmployeeCompany (conventional table name employee_companies, but it often makes sense to.

Related

Many-to-Many: Simple vs Rich in Rails

I have a question about these two relationships as I think I'm overthinking it and have confused myself quite a bit.
So they both involve a new table that sits in between the two tables you want to join.
A simple M2M gets the foreign keys from the other two tables (so example would be "blog_posts_categories" table would get the blog_post_id and the category_id foreign keys). Then for the associations, the blog_post and category models would have has_and_belongs_to_many associations with each other but the joined table gets no model.
for a rich M2M, a third joined table is created. This table gets the foreign key from the other two tables, and those two tables get the foreign key of the joined table. Then for the rails association, the joined DOES get a model, and it belongs_to the other two corresponding models. And those two models has_many of the joined table model
Am I anywhere near close to being right? I think my issue is that I keep conflating the table with the model or at least for the simple many-to-many since I keep expecting there should be a model to go along with the table.
Thanks for any direction that can be given.
Your conceptual understanding of the two are almost there. Rather than thinking of them as simple vs. rich, I prefer to think of them as implicit vs. explicit.
Take for example, two models, Book and Author.
With a has_and_belongs_to_many, Rails will implicitly create a join table between your two models. I.e. books_authors.
You can also explicitly create the join table, say, Authorship, that belongs_to both. (Book and Author will then have has_many :authorships.)
In both cases, your domain model will look the same:
Book (1)--(n) Authorship (n)--(1) Author
Now, here is the opninionated part. I prefer to use the explicit approach, as this is easier to grasp, conceptually, and makes it easier to add additional fields to your join table. Say that for example you want the Authorship to be ordered, you can easily add an order column to your Authorship model and use that accordingly.
A third relationship table is suitable for many-to-many relations. You can do something like this:
class BlogPost < ActiveRecord::Base
has_many :blog_post_categories
has_many :categories, through: :blog_post_categories
end
class Category < ActiveRecord::Base
has_many :blog_post_categories
has_many :blog_posts, through: :blog_post_categories
end
class BlogPostCategory < ActiveRecord::Base
belongs_to :blog_post
belongs_to :category
end
The third model is very simple and its overhead is basically negligible. Also it is flexible and extensive if you want to attach more information to that relationship (for example, priority or timestamp). I personally prefer having a standalone table for relationships rather than a few more columns in the blog_posts table.
Here is a relevant blog post: Why You Don’t Need Has_and_belongs_to_many Relationships.
Perhaps you can explain the tradeoffs you're considering here?

How do I add items to join tables in Rails? Do they get their own models?

I have two models: Reader and Magazine. I obviously want to have a join table, readers_magazines, to represent which magazines each reader is subscribed to.
So I create my Reader model (with fields like name, address and age) and my Magazine model (with fields like Title and Active?). In each model I write has_and_belongs_to_many of the other.
Then I write a migration, CreateReadersMagazinesJoin, and write:
create_join_table :readers, :magazines do |t|
t.index 'reader_id'
t.index 'magazine_id'
end
And migrate the database. All good.
My question is... what now? Do I create a model for the join table? That seems wrong, and yet I do need some model validations (I don't want to represent the same User-Subscription combo twice). So do I write a model for it and manually specify the database table to use?
What is the correct procedure in this situation?
From the rails guides:
A has_and_belongs_to_many association creates a direct many-to-many
connection with another model, with no intervening model.
That means that using a has_and_belongs_to_many association might be easier for you to setup (because there is no middle model) but you don't have any kind of control over the join table, validations, etc. HABTM are normally considered harmful and you would want to use a has_many :through relationship instead, that is the same but having a model to represent the join table, so you have control over everything.
More information here.
Do I create a model for the join table? That seems wrong, and yet I do need some model validations.
If you don't want to add an additional fields to the join table, you don't really need to create the model for it.
And in most cases the validation that are required by yours, can be applied to one or both the tables.
I don't want to represent the same User-Subscription combo twice.
What do you mean twice, I havent seen the cases for that, however since the User-Subscription is something external the for the join table, you can explain more crear.
Where you have the choice between a habtm relationship or separate model, I would consider whether you need a rich amount of data in the join. In the present case, I think you do need extra data in the join. For example, a subscription would have a start date and an end date. It may also have a billing date and billing amount. You would not normally populate this information in a join table but rather in a stand alone model.
I would call the join table subscriptions and thus create a model Subscription. Then you can do it like this:
class Reader < ActiveRecord::Base
has_many :subscriptions
has_many :magazines, through: :subscriptions
end
class Subscription < ActiveRecord::Base
beongs_to :reader
belongs_to :magazine
end
class Magazine < ActiveRecord::Base
has_many :subscriptions
has_many :readers, through: :subscriptions
end

Rails association primary_key option

I understand that foreign keys are for specifying when you have a different column name (different than parent child's class name) on the child class. I know what primary keys and foreign keys are and have read the rails documentation on associations several times, but I can't figure out what the primary key option is for.
1) But what is the primary_key option for? How does it change the sql when an association is called?
2) In what instances would you need to specify the primary_key on association?
3) In what instances would you need to specify both the primary_key and foreign_key?
below is an example of specifying the foreign_key option on associations:
class User
has_many :texts, foreign_key: :owner_id
end
class Text
belongs_to :user, foreign_key: :owner_id
end
User Table
id| name |
Text Table
id| owner_id |name
Ok so I thought more about the SQL and figured it out. You use foreign_key option when your child has a different foreign_key name then your parent's classname_id BUT on your parent table, you are still using ID as your identifier.
user table
id|name|age
text table
id|random_id|conversations
select * from user where user.id = text.random_id
select * from text where text.random_id (foreign_key) = account.id (primary key)
On the other hand, you use primary_key with foreign_key when you don't want to use id at all to link the relationship.
user table
id|userable_id|name|age
text table
id|userable_ss_id|conversations
HERE: if you wanted to link the userable_ss_id to userable_id, you would include both primary_key and foreign_key options on both relationships.
class User
has_many :texts, primary_key: :userable_ss_id, foreign_key: :userable_id
end
class Text
belongs_to :user, primary_key: :userable_ss_id, foreign_key: :userable_id
end
Basic rule of thumb:
select * from text where text.(foreign_key) = account.(primary key)
primary key concerns the main table and foreign key the associated one
U 've used the foreign key correctly. If for isntance User had another primary key than :id u'd have to specify that either.
#User
has_many :texts, primary_key: :uuid, foreign_key: owner_id
So you need this options, if you want to have another naming of the keys than rails conventions assume for the main and associated table respectivly
Rails uses convention over configuration.
By convention, all database tables in rails have a primary-key of the id column.
If, for some odd reason (eg you've got a legacy database), your table uses a different primary key to id... you use the primary_key call to tell rails what it is.
By convention, all associations use a foreign-key of <model>_id for the foreign key.
If, for some reason, your association uses a different foreign-key to find the associated model - you'd use foreign_key to tell rails what it is.
Unlike primary_key, using foreign_key can be much more common. Especially when you have more than one association using the same table but with different association names.

has_and_belongs_to_many where both models have underscore names

I'm working with Rails 3.2.1 and have the two models CookingVenue and DiningVenue with associated MySQL tables of cooking_venues and dining_venues. I have set up the has and belongs to many relationship between the two models but what's the name of the MySQL table name here to represent the join?
Is it cooking_venues_dining_venues?
Will Rails try to find habtm relationships between cooking and venues etc, or is Rails really clever enough to work all this out?
Like you said, cooking_venues_dining_venues is the name of the join table. After creating this table with cooking_venue_id and dining_venue_id field you need to define has_and_belongs_to_many association in both model.
class CookingVenue < ActiveRecord::Base
has_and_belongs_to_many :dining_venues # foreign keys in the join table
end
class DiningVenue < ActiveRecord::Base
has_and_belongs_to_many :cooking_venues # foreign keys in the join table
end
Yup. You just name the join table in alpha order like you have done and you should be good to go.

Rails relationship

I'm trying to figure out something regarding rails relationships. I already posted a question regarding a specific items not long ago but I do not really understand what's done in the underlying DB.
I have a Project model and a Client model.
A Project belongs_to :client => I need to manually add client_id in projects table (with a migration).
A Client has_many :projects => I do not need to do anything in the DB (no migration).
The project.client and client.projects methods are both available.
I have a Group model and a User model.
A Group has_and_belongs_to_many :user
A User has_and_belongs_to_many :group
I then need to create a migration to create a joint table with a user_id and a group_id pointers.
I do not really see where the border between rails and the relational database is.
Why do I need to add foreign key sometimes but not always ? How is the has_many relationship handled as I did not do anything in the underlying DB for this particuliar guy ?
I am kind of lost sometimes :)
Thanks and Regards,
Luc
For a has_many <-> belongs_to assoication, you're defining that one project is owned (belongs_to) by one client. Therefore, that client has many (has_many) projects. For a project to determine what client it belongs to it needs to have an client_id column so that it can look it up. This client_id column is used by Rails when you call the client method, much like this:
Client.find(project.client_id)
That's how you can find a project's client. The client_id column is often referred to as a foreign key, because its a unique identifier ("key") in a table not of its origin ("foreign"). Boom.
When you call the other way around, finding all the projects a client has, i.e. client.projects, Rails does the equivalent of this:
Project.find_all_by_client_id(client.id)
This then returns all Project records which are associated with a particular client, based off the client_id field in the projects table.
With a has_and_belongs_to_many association, such as your users & groups example, you're declaring that a user has_and_belongs_to_many :groups.
Now if it were simply a has_many :groups, the foreign key would go in the groups table, or if it were a belongs_to it would go in the users table. Good thing to remember: the foreign key always goes in the table of the model that has the belongs_to.
You're also declaring that a group has_and_belongs_to_many :users, and so we come across the same problem. We can't declare the key in the users table because it doesn't belong there (because a user has many groups, you would need to store all the group ids the user belongs to) or the groups table for the same reasons.
This is why for a has_and_belongs_to_many we need to create what's known as a join table. This table has two and only two fields (both of them foreign keys), one for one side of the association and another for the other. To create this table, we would put this in a migration's self.up method:
create_table :groups_users, :id => false do |t|
t.integer :group_id
t.integer :user_id
end
A couple of things to note here:
The table name is the two names of the two associations in alphabetical order. G comes before U and so the table name is groups_users.
There's the :id option here which, when given the value of false generates a table with no primary key. A join table doesn't need a primary key because its purpose is to just join other tables together.
We store the group_id and user_id as integer fields, just like we would on a belongs_to association.
This table will then keep track of what groups have what users and vice versa.
There's no need to define additional columns on either the users or groups table because the join table has got that under control.
class Customer < ActiveRecord::Base
has_many :orders, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :customer
end
#order = #customer.orders.create(order_date: Time.now)

Resources