Ruby on rails polymorphic associations - ruby-on-rails

Is there a difference between the has_many , has_and_belongs_to_many and the polymorphic association.
Why should I use polymorphic association.

has_many, has_and_belongs_to_many and the polymorphic associations are solutions to different problems.
has_many means you have a parent object that has many other children. While the children do only belong to that specific parent.
has_and_belongs_to_many means the objects are not structured in a hierarchical structure. And that the objects on both sides can be connected to mutiple others. A good example is probably a tagging system. A thing can be tagged with multiple tags. But the tags do not only belong to one thing, they can be added to other things too.
polymorphic associations are used when the association doesn't point to one specific other model. An example might be an Author model that has a created_work association, but it doesn't point to a decicated CreatedWork model. But instead, it can return different things, like an instance of a Book, a Play an Article, or a simple Comment which are all different classes and have different database tables.

Related

How does one create a `has_one` relationship between two models using a join table/model?

TL;DR: How does one create a has_one association using through join table and vice-versa a belongs_to through?
Context: I have two models, ProcessLog and Encounter. ProcessLog, as the name (somewhat) suggests, saves log of a single run (corresponding to a row in DB) of an external process (which is run multiple times). On the other hand, Encounter is a model that keeps track of some information X. Encounters can be produced either internally or as a result of a successful execution of the external process mentioned earlier. What it entails is that not all Encounters have an associated ProcessLog and not all ProcessLogs have an associated Encounter. However, If there is a ProcessLog for an Encounter, this is a 1:1 relationship. An Encounter cannot have more than one ProcessLog and a ProcessLog cannot belong to more than one Encounter. From DB design perspective, this is an optional relationship (I hope I haven't forgotten my lessons). In a database, this would be modelled using a join table with encounter_id as the primary key and process_log_id as the foreign key.
Rails: In Rails, 1:1 relationships are generally modelled without using a join table and the belongs_to table generally having a foreign key to the other table. So in my case, this would be encounter_id in process_logs table.
Problem: With traditional Rails approach of has_one and belongs_to, this will result in many rows in process_logs table with NULL values for encounter_id column. Now there are pros and cons to this approach by Rails, however, that is not my intention to discuss here. Yes, it will keep the table structure simple, however, in my case it breaks the semantics and also introduces lots of NULL values, which I don't consider a good approach. And is also the reason why a join table exists for optional relationships.
What have I done so far?: There aren't a whole lot of helpful documents I could find on this topic, except for the following two linked documents, though they have their own issues and don't solve my problem.
SO Question The approach here is using has_many for the join model, whereas I have only one
Discussion on RoR Similarly, it is using has_many and yet somehow talks about has_one
I created a join model called EncounterProcessLog (which has belongs_to for both ProcessLog and Encounter) and then a has_one ..., through: on the other two models, but Rails is looking for a many-to-many association and of course looking for encounter_id on process_logs table.
Question: How can I achieve what I intend to achieve here? Something on the lines of (non-working) code below:
class Encounter:
has_one :process_log, through: :encounter_process_logs
class ProcessLog:
belongs_to :encounter, through: :encounter_process_logs # This may be incorrect way of specifying the relationship?
class EncounterProcessLog:
belongs_to :encounter
belongs_to :process_log # May be this should be a has_one?
I hope someone is able to guide me in the right direction. Thanks for reading so far.
One way I can think of for doing this is:
class Encounter:
has_one :encounter_process_log
has_one :process_log, through: :encounter_process_log
class ProcessLog:
has_one :encounter_process_log
has_one :encounter, through: :encounter_process_log
class EncounterProcessLog:
belongs_to :encounter
belongs_to :process_log
This would return the process_log for encounter and vice versa which is what you want probably.

Create join table using migrations or models?

As a Ruby on Rails newbie, I'm going through the Rails Guides and tonight is Active Record Migrations.
After finishing the section on Join Tables (http://guides.rubyonrails.org/active_record_migrations.html#creating-a-join-table), I'm left with the impression that using create_join_table is preferred (and simpler) than creating the Join Table via rails generate model.
Is this a correct assumption? Are there nuances I should be aware of?
Using the example in the guides of categories and products:
A join table works transparently. You only work with the two existing models (Category and Product), and the join table exists only to enable the HABTM relationship between them, so you can call category.products, or product.categories, and things just work.
Generating a model, on the other hand, would only be necessary if you need to work with that association as a distinct thing in your application (e.g. if you need to do things with a Categorization directly).
Contrast the Rails Guides description of a has_and_belongs_to_many association (read more):
A has_and_belongs_to_many association creates a direct many-to-many
connection with another model, with no intervening model. For example,
if your application includes assemblies and parts, with each assembly
having many parts and each part appearing in many assemblies, you
could declare the models this way:
with that of a has_many :through association (read more):
A has_many :through association is often used
to set up a many-to-many connection with another model. This
association indicates that the declaring model can be matched with
zero or more instances of another model by proceeding through a third
model. For example, consider a medical practice where patients make
appointments to see physicians. The relevant association declarations
could look like this:
So, yes, you're correct that create_join_table would be simpler than creating a model for the association. You can also see this answer for another explanation of the difference between these approaches.
As the docs mention...
Migration method create_join_table creates a HABTM join table
--
When you create a has_and_belongs_to_many relationship, you only need a join table (no model).
has_and_belongs_to_many join tables are different from has_many :through as they don't have an id (primary_key); has_many :through are meant to represent an interim model, so each "record" should have a unique identifier.
Thus, your question about whether it's better to create a join table through rails g model is false. has_and_belongs_to_many has no binding model. If you were using has_many :through, you'd be able to use the rails g model no problem.

Rails: What do model associations tangibly do?

I have the following models setup:
clinics
has_many :occupations, through: :clinic_occupations
has_many :clinic_occupations
occupations
has_many :clinic_occupations
clinic_occupations
belongs_to :occupations
belongs_to :clinics
I think the has_many :clinic_occupations in clinics is probably unnecessary, but it's what we have right now so I wanted to include it. I am trying to only call occupations that have been associated with a clinic, or occupations that have no clinic id at all. What is the correct way to do this, and what are these model associations tangibly doing?
I think the has_many :clinic_occupations in clinics is probably unnecessary
Nope, it is. In order for any has_many _, through: _ to work, you need to supply another association. That's exactly what through stands for, "an association through an association". Under the hood, this uses JOIN-clauses for "lower" asssociated objects with "higher" ones . Since Rails 3 these can even be nested, producing a JOIN-clause through multiple tables at once.
What it does is create a bunch of methods for your model (reference) with some caching underneath.
Why should you define that association explicitly? Because:
It's safer. Association name may not follow Rails convention and thus no assumptions can be made just with its name. Let's face it, sometimes this happens. More on that below.
It offers all the features has_many associations have in general. Association may contain details that do not violate conventions (like conditions).
It's more flexible. This way you can define multiple associations that point to the same class. It's impossible to follow conventions in this case, since one name can only point to one method.
It's clearer when looking at the model why a single method call queries multiple tables at once.
In fact, you may want to access the join model (ClinicOccupation) directly when speeding up certain queries. There are cases when you already know the needed object's id and want to use it in a query without fetching the entire object first with an extra query.

Rails - one model that many others can use?

Running Rails 3.2.8 with SQLite (basic setup from current dl on rubyonrails.org)
If i have several models that I want to have an attribute of "notes" and all "notes" are String, should there only be one specific Note model, with all the other models referencing the Note model?
or
Does each model has to have its own attribute for "notes"?
If the first response, does that mean that for each relationship that references the "notes", do I need to create another class that defines that :through relationship? (ie tableOne, tableTwo, and tableThree all reference notes, then I would need to create additional classes tableOne_notes, tableTwo_notes, and tableThree_notes)
Is a polymorphic solution applicable?
If you want one 'notes' model that can serve the same purpose for many other models this would be a polymorphic relationship.
This is actually true regardless of the application framework.
In the specific case of rails implementation of polymorphic relationships for this example you would add a
note_type
column in the notes model/table and in the Note model, using database migrations plus:
belongs_to :notable, :polymorphic => true
in Note and then
has_many notes, :as => notable`
in the other models that need the notes.
See http://guides.rubyonrails.org/association_basics.html#polymorphic-associations for more.
Frequent uses for Polymorphic relationships:
notes (as you are using)
update information (timestamp, ip address, user-agent, etc)
categories that are simple key-value lookups
It depends on your needs. But basically it is possible to create a model Notice and create a 1-n relation between Notice and Other and Other2...
As I understand your question only a 1-n or multiple 1-n relationships make sense. If you add to every table a column notes, it could only be one note for each data-line. Another class which defines :through is not necessary by a 1-n relation.

has_and_belongs_to_many vs has_many through

What is the difference between the
has_and_belongs_to_many and
has_many through relationships? When and where to use which one?
As far as I can remember, has_and_belongs_to_many gives you a simple lookup table which references your two models.
For example,
Stories can belong to many categories.
Categories can have many stories.
Categories_Stories Table
story_id | category_id
has_many :through gives you a third model which can be used to store various other pieces of information which don't belong to either of the original models.
For example
Person can subscribe to many magazines.
Magazines can have many subscribers.
Thus, we can have a subscription model in the middle, which gives us a similar table to the earlier example, but with additional properties.
Subscriptions Table
person_id | magazine_id | subscription_type | subscription_length | subscription_date
And so on.
From http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
My rule of thumb is, can I get by with a list of checkboxes here? If so, then it's a has-and-belongs-to-many (HABTM) association. If I need the checkbox to capture more about the relationship than simply yes/no it belongs, then use has_many :through. HABTM is as simple as using the _ids method with a simple_form collection_check_boxes. has_many :through often involves accepts_nested_attributes_for.
From my experience it's always better to use has_many: through because you can add timestamps to the table. Many times while debugging some ActiveRecord objects that are connected through HABTM, I was missing created_at, updated_at timestamps to get the clue what actually happened.
So keep in mind that it can help you to debug, investigate an issues with the data relations in the context of the time, because without it you are "blind" when relations were created or updated.
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
Many of the answers clarify that you should use has_and_belongs_to_many vs. has_many through: if you will not need any extra data or validations on the join table.
However, beware of taking this approach. In the early stages of application development, it is nearly impossible to know what extra features or validations you may need in the far future of your project's lifecycle. If you decided to use has_and_belongs_to_many, and want to add one simple datapoint or validation 2 years down the road, migrating this change will be extremely difficult and bug-prone. To be safe, default to has_many :through
The simplest rule of thumb is that you can go with has_many :through relationship if you need to work with the relationship model as an independent entity.
If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
Rails offers two different ways to declare a many-to-many relationship between models. The first way is to use has_and_belongs_to_many, which allows you to make the association directly:
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.

Resources