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
Related
Let's say I have two tables: NormalClass Table and SpecialClass Table.
Each table contains classes for students.
I want to join tables so that I can access the normal classes of each student in that has a special class for students.
Both tables share the student_id key.
Trying to do this:
NormalClass.includes(:specialClasses)..
results in:
ActiveRecord::ConfigurationError: Association named 'specialClasses' was not found on NormalClass.includes; perhaps you misspelled it?
Should I be doing something else?
Gerbil,
Basically, you need to first set your associations properly.
Look at section 2.4 The has_many :through Association in Rails guides
Once you set it, you can pull up the data you want by pulling the student_id key
I have a problem with habtm on a single model in rails.
Example:
Let us say i have a User model with two roles "Student" and "teacher". User model is common for two roles. Now
Each student can be associated to many teachers
Each teacher can be associated to many students
In rails notation, their should be habtm between teacher and student
How this can be achieved with single table.
Thanks,
Aashish
It can't be done with a single table. In a many-to-many relationship, no matter what, you always need a table where you store the associations.
In your case, given the association seems to be parent/child, then you just need two tables instead of one.
How to implement it, it depends on your database structure and data organization. You should create an users_users table (as part of the habtm) and configure the references accordingly. If the user table, as it seems to be, is also used for STI, then the configuration may change a little bit.
I'm learning about model associations in rails. I've learned how to give the table for a model a column to hold foreign keys of another model like so:
rails generate model User account_id:integer
I would then take the primary key of an account from an Account table and assign it to the account_id for the designated user.
However, I am also told to create the association in User.rb like so:
has_one :account
I understand the difference between these two things. One creates a column in the table (the first line after the migration), and the other generates a series of helpers (the latter).
However, what I am seeing in tutorials is that sometimes both are done, while other times, only the association (has_one :account) is done. How do I go about deciding when to create a column in the table to hold foreign keys, and when to just create the association in the model .rb file?
You will always have to create the column in the Database (1)
It is not mandatory to define the relation(s) inside the models, but it is highly recommended (2)
(1) : The Database needs this column of foreign keys to be able to retrieve the corresponding record. Without the column, the DB cannot find back the related record. You can use a Migration to create this column, not only a scaffold.
(2) : You can skip the relations declaration in the models, but it is highly recommended because:
it generate methods corresponding to the relations (ex: User belongs_to :role, then you can do user.role directly instead of Role.where(id: user.role_id).first)
not every human can remember all the associations. It is better to show/list everything that is linked to your model
You asked:
How do I go about deciding when to create a column in the table to hold foreign keys, and when to just create the association in the model .rb file?
I would answer:
Always create the column (cannot work if you don't) AND define every association(s) (relation(s)) inside the model.
Your first example is a scaffold that creates the model file and the migration.
has_one helps ActiveRecord understand the relationships between the tables so that it can, among other things, generate proper SQL queries for you.
But #1 has to be in place for #2 to even work. However, creating the db column via a scaffold command isn't necessary - it's just convenient sometimes, because it creates both the model file, and the associated migration.
You could just write the migration by hand. Just because whatever thing you're following doesn't always mention adding the db column doesn't mean it's not necessary. It's probably just assumed that you've already done it because these foreign key migrations are so common after you get up and running with Rails that they sort of go without saying after a while.
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.
When working with multiple database connections and setting up models to work with those different instances, how do you setup the has_many, has_one, belongs_to, etc?
For example:
I have one database that is Read + Write, the other DB instance is used in my Rails app as Read only. The DB table that is Read only I am pulling back a list of Media items (Videos, Images and Audios). In my Read + Write DB I have a media_ratings table.
I have a model called AvMedia (The Read only DB) and a MediaRating Model (Read + Write DB). How do I setup The AvMedia model like so: has_one rating and setup the MediaRating model like so: has_many AvMedia?
Sorry if this is confusing... I tried to explain it the best I could.
In the AvMedia model you must include has_one :rating and just make sure that in the corresponding table you have a rating_id foreign key. You also need belongs_to :avmedia in your rating model.
A lot of magic goes on behind the scenes to automatically link your tables together when you define a relationship.
It sounds like you don't need the has_many for AvMedia - from what you said it appears that it's a 1-1 relationship (one AvMedia has one rating, one rating corresponds with one AvMedia). If this is incorrect, let me know.