Following a guide, I ran the following command:
rails g migration CreateSnippetsUsers snippet:belongs_to user:belongs_to
This created the following migration:
class CreateSnippetsUsers < ActiveRecord::Migration[5.0]
def change
create_table :snippets_users do |t|
t.belongs_to :snippet, foreign_key: true
t.belongs_to :user, foreign_key: true
end
end
end
In the past I've seen the same thing, but with index: true instead of foreign_key: true. What's the difference between the two?
Indexes, foreign keys and foreign keys constraints are strictly related concepts in databases that are often confused or misunderstood.
REFERENCES
When you declare a reference, you're simply saying to include a column whose values should match those of another table (and in Rails you also get some useful methods to navigate through the associated models). In the example:
create_table :appointments do |t|
t.references :student
end
the appointments table will have a column named student_id whose values should be in the pool of students' id values.
INDEXES
Since when you add a reference you will probably use that column often, you may (and probably should!) also tell you database to boost the look up speed using the reference column. You can do this with the option index: true (which by the way is a default option in the reference method since Rails 5). Indexes have few drawbacks, the main being a larger memory consumption.
FOREIGN KEY CONSTRAINTS
From what said so far, reference column and foreign column are synonyms. But do you remember when I said that a reference column's values should match those of another table? If you simply declare a reference, it's your responsibility to ensure that a matching row on the referenced table exists, or someone will end up doing nonsense actions like creating appointments for non-existing students. This is an example of database integrity, and fortunately there are some mechanisms that will grant a stronger level of integrity. These mechanisms are called ' database constraints'. What the option foreign_key: true does is exactly to add this kind of constraint on the reference column, to reject any entry whose foreign key values are not in the referenced table.
Database integrity is a complex task, growing in difficulty with the database's complexity. You probably should add also other kind of constraints, like using they keywords dependent: :destroy in your class to ensure that when you delete a student, all of its existing appointments are also destroyed.
As usual, here's a RTFM link: https://guides.rubyonrails.org/association_basics.html
Index improve speed of data retrieval operations on database tables. When we write index: true to any column, it adds a database index to this column. For example I was creating a table:
create_table :appointments do |t|
t.references :student, index: true
end
It will create student_id column in appointments table.
A foreign key have different use case, it is a relationship between tables. It allow us to declare an index in one table that is related to an index in another table and also some constraints are placed.The database enforces the rules of this relationship to maintain referential integrity. For example we have two table profiles and educations, and a profile may have many educations.
create_table :educations do |t|
t.belongs_to :profile, index: true, foreign_key: true
end
Now we have profile_id column in educations table which is foreign key of profiles table. It prevents a record from being entered into the educations table unless it contains a profile_id value that exists in the profiles table. So referential integrity will be maintained.
Related
Im working on learning Rails, and have found that the details are where you can really end up sinking yourself in the future. As I'm creating a very simple store, doing migrations I have a few belongs_to's that are optional object relationships. As I was reading trying to better understand null here:
Understanding rails migration statement (:null => false)
It occurred to me that using null: true would allow that column to be null, and I was couldn't find any information / questions addressing the difference between:
create_table :items do |t|
...
t.belongs_to :shopping_cart, null: true
...
end
and
create_table :items do |t|
...
t.belongs_to :shopping_cart, optional: true
...
end
What should I be doing for this kind of optional relationship, I'm new to Ruby/Rails but "convention over configuration" has me wanting to understand the right way to do this. Thanks so much!
I think you may be confused about how optional works in a belongs_to relationship.
In a migration file, you're telling the database how a table is going to be structured. What columns are called, what kind of data they hold, whether or not NULL is an acceptable value, etc. This relates directly to your model code through the ActiveRecord ORM that Rails uses.
So in a migration, null: true means at the database level that NULL is an acceptable value for the given column. null: false would mean that NULL is not acceptable and that you always expect something to be stored there. Generally, null: false is used in conjunction with a default so that there's always a value in the column even if it's not specified when the record is created.
optional is used in the model code itself to tell ActiveRecord that a belongs_to relationship is expected, but it may not exist and that's ok. In your case, that would look something like this:
class Item < ApplicationRecord
belongs_to :shopping_cart, optional: true
end
That means that an item may have a shopping_cart_id present in the database, but it doesn't have to. Items can exist with a shopping_cart_id column that contains NULL.
Really null: true and optional aren't directly related. Nullability at the database level is determined by what's in a migration file. The presence of a foreign_key in a belongs_to relationship like items belonging to shopping carts is more about the model code than it is about the migration code.
I have a question about rails 6 and model association with or without migrations.
Let me be more specific.
Let's say we want to place a foreign key inside users table which links to the id column of the courses table.
Most of the tutorials and courses i run into follow this route:
create a migration with add_column :users, :course_id, :integer
place has_one or has_many and belongs_to to the appropriate models.
And that's it.
My thought is, how about this:
create a migration with add_reference :users, :course, foreign_key: true
place the appropriate has_one/has_many and belongs_to to the apropriate models.
or is it the same?
Doesn't the second option create indexes as well whereas the first does not?
Which is a best practice?
add_reference is meant to be a shortcut as part of the rails convention, under the hood, it will call add_column and other options that you specify [you can check the code for rails-6 here]
while foreign keys are not required, they are considered a best practice because they guarantee referential integrity. you can read more about it here https://edgeguides.rubyonrails.org/active_record_migrations.html#foreign-keys
or is it the same? Doesn't the second option create indexes as well whereas the first does not?
having said that, your 2 migrations are not entirely the same even though they work the same (they achieve the same goal) - the second option will add an index by default unless you specify the option not to - I definitely agree with you about using add_reference as this is an easier and more foolproof shortcut
of course, you can also achieve that manually by using your first migration by adding an index and a foreign key as well
add_column :users, :course_id, :integer
add_index :users, :course_id # uniq or not
add_foreign_key :courses, :users
I'm very new to Rails, and currently for our class, we have to create an application with databases.
I have a bunch of tables that have foreign keys to other tables, and I've defined these associations in the migration files (using execute("ALTER TABLE...")), but not in my models (I only have self.primary_key defined in my models).
My question is, since I've set these associations in my migrations, do I also have to set the associations in my models (i.e. has_may, belongs_to, validates_uniqueness_of, etc)?
I'll be creating an app that would allow user input, and these input would be inserted to the database.
Hoping that my confusion can be cleared up. Thank you!
Example
Migration file:
class CreateSchools < ActiveRecord::Migration[5.0]
def up
create_table :schools, {:id => false} do |t|
t.integer :school_id
t.string :school_name
t.string :contact_number
t.integer :a_id
t.timestamps
end
execute "ALTER TABLE schools ADD PRIMARY KEY(school_id);"
execute "ALTER TABLE schools ADD CONSTRAINT a_id UNIQUE(a_id);"
execute "ALTER TABLE schools ADD CONSTRAINT school_references_location FOREIGN KEY (a_id) REFERENCES locations(a_id) ON DELETE CASCADE ON UPDATE CASCADE;"
change_column_null :schools, :school_name, false
change_column_null :schools, :a_id, false
end
def down
execute "ALTER TABLE schools DROP PRIMARY KEY;"
execute "ALTER TABLE schools DROP FOREIGN KEY school_references_location;"
execute "ALTER TABLE schools DROP INDEX a_id;"
drop_table :schools
end
end
Model:
class School < ApplicationRecord
self.primary_key = :school_id
end
Yes you'll need to define the associations in Model as well.
For all the rails applications,
Migrating and changing db according to associations is the first step.
Then add association statements in the models so that the associations from db can be accessed using simple methods. Like user.posts etc.
Adding association statements in Models provide you methods that generates queries according to the method called to retrieve data from db easily. You can also query db using join and select statements if you don't want to include association statements in Models.
So, in short, designing schema to manage associations is necessary where as mentioning them in Model is not. But we do it to make our life and code simpler.
There are different possibilities in Rails to link models and tables together.
add_column :books, :user_id, :integer, index: true
add_reference :books, :user, index: true, foreign_key: true
As far as I have read, I see that some Databases have different behaviors concerning this options.
I'm interested in finding what is the best practice to adopt for performances and for code readability.
I was using integer indexed column for a long time and I want to know if I should use reference with foreign_keys instead.
UPDATE: Exemple
In a brand new test app I've run:
rails g model book author_id:integer:index user:references
The migration file looks like:
class CreateBooks < ActiveRecord::Migration
def change
create_table :books do |t|
t.integer :author_id
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :books, :author_id
end
end
The ddl for the table
-- Table: public.books
-- DROP TABLE public.books;
CREATE TABLE public.books
(
id integer NOT NULL DEFAULT nextval('books_id_seq'::regclass),
author_id integer,
user_id integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
CONSTRAINT books_pkey PRIMARY KEY (id),
CONSTRAINT fk_rails_bc582ddd02 FOREIGN KEY (user_id)
REFERENCES public.users (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.books
OWNER TO my_user_name;
-- Index: public.index_books_on_author_id
-- DROP INDEX public.index_books_on_author_id;
CREATE INDEX index_books_on_author_id
ON public.books
USING btree
(author_id);
-- Index: public.index_books_on_user_id
-- DROP INDEX public.index_books_on_user_id;
CREATE INDEX index_books_on_user_id
ON public.books
USING btree
(user_id);
EDIT 2: Here is a link that describe the problem and points out the foreign_keys importance
Link
Expanding on my comment a little: yes, the primary function of foreign keys is to maintain referential integrity (https://en.wikipedia.org/wiki/Referential_integrity).
As a side benefit, they provide a little more explicit information about your schema: query and design tools can make use of those relationships, rather than rely on shared column names.
It's also easy to check that all columns references by foreign keys are indexed, whereas otherwise you may only discover a missing index at query time.
http://www.postgresql.org/docs/current/static/ddl-constraints.html#DDL-CONSTRAINTS-FK
The performance implications are pretty easy to reason about: you're going to be doing an index lookup against the parent table whenever the data in a child table changes (insert/delete/update). For most small-medium tables you never have to worry about it.
I've been reading about the add_index method in Rails.
In a tutorial, I found this example:
class AddTitleToMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.string :title
t.string :content
t.integer :user_id
t.timestamps
end
add_index :microposts, [:user_id, :created_at]
end
end
I have no idea why [:user_id, :created_at] are indexed and not the others.
How do I know which fields to index in a Rails application?
Well it depends on your application, you mostly index foreign keys to improve Database performance. For instance next time you are going to search all the micropost that belong to a user, it will use the user_id index, and whenever you are searching all post created between certain times i'll use the created_at index. Indexes basically speed queries, you index based on what queries you are going to be doing constantly.
I would say that everywhere there is a belongs_to relationship, including join tables, there should be a corresponding index. Some might call this overboard, but the performance difference between an indexed relationship and a non-indexed relationship is profound.
So, if a User has_many Books, then your migration should have a corresponding line add_index: :books, :user_id. If a Book has_many Authors, and an Author also has_many Books through a join table BookAuthorings, then there should be two indexes on BookAuthoring - one that covers author_id, and one that covers book_id.
I'd say that a good, solid 90% of Rails performance problems that I've run into stem from a missing index somewhere.
I mostly index (as others said) the foreign keys and all other fields I intend to use for searching or sorting. So in you case I would add a key to at least the title field if you want to show the microposts in alphabetical order.
In case of the content field it may depend. If it contains a lot of text, you will most likely not do a simple sort or search but would want to do fulltext search. In this case a normal database index would not be of great help and you would have to use a fulltext search engine instead.