I was going through Rails association link. But I am not able to understand how the association works. I am using mysql db.
These are the files I generated:
user.rb
class User < ActiveRecord::Base
has_many :orders
end
order.rb
class Order < ActiveRecord::Base
belongs_to :user
end
20150911181301_create_orders.rb
class CreateOrders < ActiveRecord::Migration
def change
create_table :orders do |t|
t.string :content
t.integer :user_id
t.timestamps null: false
end
end
end
20150911181351_create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps null: false
end
end
end
First I was expecting db:migrate will generate some foreign key relationship itself but it did not happen. Then I thought may be rails manages it internally but when I deleted a user through rails c , it did not delete corresponding orders from the order table.
Where is my understanding incorrect? Also give me some links which explains how does this works?
It's debatable but the traditional 'Rails way' is to manage model-related things like defaults, foreign keys, and triggers is at the ActiveRecord level instead of in the database.
That said, you're free to add a foreign key for referential integrity in a migration using the following:
add_foreign_key :orders, :users
More information is available in the Rails Guides.
The 'Rails way' to automatically destroy child objects when the parent is destroyed is to specify the :dependent strategy on the child collection. There is a great Stackoverflow discussion here going into detail about the two :dependentoptions: :destroy vs :delete_all.
Everything is in the docs
-You should note that "In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations."
-You should also note that when it comes to associations, Rails needs you to tell it that asides two models being related with belongs_to and has_many, you want the associated model to be deleted when it's parent model is deleted. That is where the dependent: :destroy comes in.
Now all you need to do in your code for orders related to a user to get deleted when the user is:
class User < ActiveRecord::Base
has_many :orders, dependent: :destroy
end
Source: Rails Guides
Related
I have a model which has a non-rails conventional primary key.
class Guoid < ActiveRecord::Base
self.primary_key = :guoid
end
and related migration
class CreateGuoids < ActiveRecord::Migration
def change
create_table :guoids, id: false do |t|
t.integer :guoid, limit: 8, auto_increment: true, primary_key: true
t.integer :parent_guoid, limit: 8
t.string :resource_type, limit: 60
end
end
end
Now I want to reference this model in another model and trying to create migration using references which doesn't to work.
class ContentUnit < ActiveRecord::Base
self.primary_key = :guoid
end
class Content < ActiveRecord::Base
self.primary_key = :guoid
belongs_to :user
belongs_to :content_unit
end
and related migration
class CreateContents < ActiveRecord::Migration
def change
create_table :contents, id: false do |t|
t.references :content_unit, index: true, foreign_key: true
t.references :user, index: true, foreign_key: true
end
end
end
When I run the migration, I am getting following error.
Mysql2::Error: Can't create table `myapp_development_cr1`.`#sql-54a_308` (errno: 150 "Foreign key constraint is incorrectly formed"): ALTER TABLE `contents` ADD CONSTRAINT `fk_rails_823443bd0d`
FOREIGN KEY (`content_unit_id`)
REFERENCES `content_units` (`id`)
I am expecting to create content_unit_guoid foreign key in contents table referencing guoid in guoids table.
I used activerecord-mysql-awesome gem to work well with non-rails convention primary keys.
Here is a trigger which first creates a record in guids table and use it's pk as pk of the target table.
DELIMITER $$
CREATE TRIGGER `content_before_insert` BEFORE INSERT ON `content`
FOR EACH ROW BEGIN
IF NEW.guoid = 0 THEN
INSERT INTO `content`.guoids (resource_type)
VALUES('Content');
SET NEW.guoid = LAST_INSERT_ID();
END IF;
END
$$
DELIMITER ;
That is not a viable database design for ActiveRecord or even in general.
ActiveRecord (and any decent ORM) requires each table to have a primary key. This is what enables relations and lets Rails differentiate records.
class Content < ActiveRecord::Base
self.primary_key = :guoid
belongs_to :user
end
This will never work since self.primary_key = :guoid references contents.guoid not guoids.guoid. You cannot use relations in ActiveRecord as primary keys. Even if you could this would really be a performance killer since every query would need to join the guoids table - even recursively!
Rails is strongly convention driven and really smiles on you if you take a little time to learn the Rails way vs fighting the framework to make it work like framework X. The rails guides are a good place to start.
Stick with id for primary keys and _id for foreign key columns. You will have less fuss and not be treated like idiots if you have to collaborate with other developers.
There are valid cases where you would want to use unique identifiers (UUIDs) instead of auto-incrementing values. For example if you have several databases auto-incrementing values can cause race conditions. But in that case you still need to use a primary key on each table - the difference is simply the contents of the primary key.
This is either done by generating a hashes with a algorithm that has a low chance of collision on the application level or more recently by using binary UUID types in the database. The later being preferable today.
Not by using relations. AR just does not work that way.
http://geekhmer.github.io/blog/2014/12/06/using-uuid-as-primary-key-in-ruby-on-rails-with-mysql-guide/
http://blog.arkency.com/2014/10/how-to-start-using-uuid-in-activerecord-with-postgresql/
Using non-standard foreign keys.
The belongs_to and reference macros are an example of things that just work if you follow the conventions.
For foreign key constrainst that does not match the conventions you need to manually create it in the migration:
class CreateStores < ActiveRecord::Migration
def change
create_table :contents do |t|
t.references :manger, index: true, foreign_key: false
end
add_foreign_key :stores, :users, column: 'manager_id', primary_key: 'uuid'
end
end
Note that this will not fix your issue since your general approach is not viable!
So, your trying to make a foreign key from the contents table to the guoids table using this correct?
t.references :content_unit, index: true, foreign_key: true
references takes a table name as the argument and tries to find a column called id on it to make the foreign key between the tables. So you can see in your error message its trying to find the column id on content_units table. This is in no way referencing your guoids.
Its perfectly reasonable to want globally unique identifiers (typically GUID's or UUID's, however i'm not sure why you are storing them on a separate table and then (i'm assuming) going to make everything foreign key to it creating some massive many to many table that connects every table in your database? Seems really unscalable. Postgress handles uuid's for you nicely, but as I have in the past, it looks like your using mysql. Here is how I have done it.
Models
Class Teacher < ActiveRecord::Base
has_many :students
before_validation :generate_id
self.primary_key = :id
private
def generate_id
write_attribute(:id, SecureRandom.uuid) unless read_attribute(:id)
end
end
Class Student < ActiveRecord::Base
belongs_to :teacher
end
Migrations
create_table :teachers, id: false do |t|
t.string :id, null: false, unique: true, limit: 36
end
create_table :students do |t|
t.string :teacher_id, limit: 36
end
add_foreign_key :model_a, :model_b
I am making a simple junction / relational table in my mysql database on my rails app.
I run:
bin/rails generate model personplace person:references place:references
I get a very large error that begins with:
Index name 'index_place_on_place_id' on table 'places' already exists/home/myname/.rvm/gems/ruby-2.1.7/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract/schema_statements.rb:939:in `add_index_options'
It is saying that I am indexing twice and that's not allowed.
I am not trying to index twice, I am just trying to make a couple foreign keys.
My goal is to make a good model/migration of a junction table with 2 foreign keys pointing to 2 separate tables.
EDIT 1:
The table places was made like this:
bin/rails generate model places person:references name:string creator:string size:integer
It already references the person table. That person the person that owns the place
The new table is for keeping track of all the people at the place.
EDIT 2:
class CreatePlaces < ActiveRecord::Migration
def change
create_table :places do |t|
t.references :user, index: true, foreign_key: true
t.string :name
t.string :creator
t.integer :size
t.timestamps null: false
end
end
end
I am creating a primary -> foreign key relationship among my columns while doing the db design. my db design should be:
Users table: userid (primary key), name
Shoppinglist table: shoppingid (primary key), userid(foreign key)
Here is my migration file:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.integer :userid, :primary_key
has_many :shoppinglists, dependent: :destroy
t.timestamps null: false
end
end
end
class CreateShoppinglists < ActiveRecord::Migration
def change
create_table :shoppinglists do |t|
t.integer :shoppingid, :primary_key
t.belongs_to :userid
t.timestamps null: false
end
end
end
Isnt there a way to create the above while creating the table instead of creating a new migration file?
I use this command to check my relationships and I dont see any relations created:
User.reflect_on_all_associations
Since your migration fails to apply, you may have concluded that it contains errors. You seem to not understand the purpose of migrations and their difference with models. Plus, you're violating plenty of conventions, so it does a bit of what you're not expecting.
has_many and belongs_to are not migrations' scope. These methods are from models and all they add is some query methods. They can even be used on an existing database without the migration mechanism at all, as horrible as that may be.
Rails already adds a primary key column id to every table, unless told otherwise with an option id: false in create_table. So add another one and violate conventions at your own peril.
Rails' convention of column naming is that of method naming: snake_case, not likethis. It uses this to properly pluralize and singularize the given terms to infer configuration through convention. Consequently, foreign key columns are specified in form <ASSOCIATION>_id, i. e. user_id.
Foreign key support has come quite recently, in Rails 4.2 only, and since it doesn't (as guides claim) work with sqlite, Rails don't add these by default. When following conventions, you only need to specify affected tables, Rails will infer the rest. Of course, make sure you're running Rails 4.2 (or newer) before trying. Before that, the notion of primary-foreign keys belonged not to the database, but to the models that were housing their own queries generated via has_many and the company.
You can create a migration something like:
rails g migration addShoppingListAssociationToUsers
And have something similar to following as the content of the migration:
class AddShoppingListAssociationToUser < ActiveRecord::Migration
def change
add_reference :shoppinglists, :user, index: true, foreign_key: true
end
end
I am assuming that you do not need to create primary keys explicitly and can use the ones that are automatically generated with each table by Rails.
# User.rb
has_many :shopping_lists
# ShoppingList.rb
belongs_to :user
# Migration Files
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreateShoppinglists < ActiveRecord::Migration
def change
create_table :shoppinglists do |t|
t.references :user, index: true
t.timestamps null: false
end
end
end
I used a migration in ruby to create these tables together with a model.
I have a Table Information and a Table Detail, each entry in the information table can have one association in the detail table.
So i created them with:
create_table :details do |t|
t.integer :id
t.string :Bezeichnung
t.binary :Koordinaten
end
create_table :informations do |t|
t.integer :id
t.integer :DetailID
t.integer :Longitude
t.integer :Latitude
end
In my Information Table i got a DetailID which should Reference to the id of the Detail table.
Now i did:
ALTER TABLE informations
ADD CONSTRAINT fk_informations_details
FOREIGN KEY (DetailID)
REFERENCES details(id)
Is this correct? Have i set the FOREIGN KEY correct? Or do i have to put the foreign key in the other table!?
Because i want to use in my Information Model following:
has_one :detail, :foreign_key => 'DetailID'
And in the Detail Model following:
belongs_to :main
Some notes:
Perform all kinds of alterations on the database using migrations, never do them by hand.
Use Ruby on Rails naming convention whenever possible. This means using detail_id instead of DetailID
Your associations are wrong.
If Information has_one :detail, Ruby on Rails will look for an entity with a matching information_id in the details table.
In your model, it is the other way around - the Information contains a detail_id. In other words Ruby on Rails would specify this as Information belongs_to :detail and Detail has_one :information.
I have three models clients, client_categories and clients_category_merge.
I want to store clients_id and client_categories_id into clients_category_merge table, as a single client can have multiple client categories.
How do I add the record to 2 tables (clients and clients_category_merge) when I only have one model (clients) when submitting the form?
I am sure there is a good way of doing this. But I am pretty new to Rails and lost on this one.
The has_many :through association will add the proper records for you.
class Client < ActiveRecord::Base
has_many :client_categories_merges
has_many :client_categories, :through => :clients_categories_merges
end
class ClientCategories < ActiveRecord::Base
has_many :client_categories_merges
has_many :clients, :through => :clients_categories_merges
end
class ClientCategoryMerges < ActiveRecord::Base
belongs_to :client_category
belongs_to :client
end
Check out this guide
Edit: And this one for the corresponding forms