Rails migration - many belongs_to to same table, multiply FKs - ruby-on-rails

I need some table like this one:
uid:integer
pid:integer
predesposition:integer
primary_key(uid,pid)
foreign_key(uid, user(id))
foreign_key(pid, user(id))
So, two questions:
1. How can I declare multiply FKs in migration?
2. How can I declare two (or more) columns belonged to same table in migration?
Example:
create_table :encounters, :id => false do |t|
t.belongs_to :user, :polymorphic => true
t.belongs_to :user, :polymorphic => true
t.integer :predisposition
end

If you want to refrence other table then follow the concept of foreign key . And wanna to suggest to use full table name instead of uid and pid .
create_table :table_name, {:id => false} do |t|
t.integer :user_id
t.integer :pid #suppose its procedures table
t.integer :predesposition
t.references :user
t.references :procedure
t.timestamps
end
execute "ALTER TABLE table_name ADD PRIMARY KEY (user_id,pid);"
Hope that help .

Related

Do I need a foreign key declaration in the individual table when creating join table?

I have a join table of "contents" and "roles" called content_roles and this is the join table.
class CreateContentRoles < ActiveRecord::Migration
def change
create_table :content_roles, :id => false do |t|
t.belongs_to :content, foreign_key: "content_id"
t.belongs_to :roles, foreign_key: "role_id"
end
add_index :content_roles, ["content_id", "roles_id"]
end
end
So in the individual roles and contents migration, do I need to have a foreign_key that refers back to the join table and/or the roles/contents? Sorry, I didn't explain this any better.
why cant you use references as shown below
class CreateContentRoles < ActiveRecord::Migration
def change
create_table :content_roles, :id => false do |t|
t.references :content, index: true, foreign_key: true
t.references :roles, index: true, foreign_key: true
t.timestamps null: false
end
end
end
The id columns in the content and roles tables act as foreign keys. Rails joins the content and content_roles tables using id in content table and content_id in content_roles table. So are the roles and content_roles tables.

rails database constraint for self referential association

I'm building a rails website, which involves a directed friendship relation. I know in model level, it is a self referential association. And there are methods like has_and_belongs_to for that association.
My question is: how can I set up the database level constraints for this relation. I guess the migration would be something like this, which uses foreign keys to guarantee the referential integrity:
class CreateFriendships < ActiveRecord::Migration
def change
create_table :friendships do |t|
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :user, null: false, foreign_key: true
t.integer :accepted, null: false, default: 0
end
end
But when I run rake db:migrate, it has error:
PG::DuplicateObject: ERROR: constraint "fk_friendships_user_id" for relation "friendships" already exists
As a matter of fact, I'm not even sure whether it is necessary for me to set up the database constraint in this case, since I've seen some people's implementation of friendship relation has no database constraint like this:
create_table :friendships do |t|
t.integer :user_id
t.integer :friend_id
t.timestamps
end
According to Rails Guide
The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or constraints, which push some of that intelligence back into the database, are not heavily used.
I'm not sure whether in this case, the database constraints are heavily used.
So is it really necessary for me to set up database level constraints (using foreign keys) in this case? Or I just need to realize the constraints in model level? Thanks!!
You have declared user relation twice:
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :user, null: false, foreign_key: true
Seems that it should be like this:
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :friend, null: false, foreign_key: true
To answer your question: how can I set up the database level constraints for this relation?
Answer: Just like you already have.
Often developers go the rails way and set these constraints in model, but it's perfectly reasonable to set them up in database.
EDIT:
This will let you create a table with friend_id
class CreateFriendships < ActiveRecord::Migration
def change
create_table :friendships do |t|
t.belongs_to :user, null: false, foreign_key: true
t.integer :friend_id, null: false
t.integer :accepted, null: false, default: 0
end
add_foreign_key :friendships, :users, column: :friend_id
end
end
I think you're getting confused about the role of foreign_keys in your database architecture.
ActiveRecord is just a "coating" for SQL.
It's able to form queries etc which allow you to build associated objects, thus the most important thing you can do is associate those objects properly.
The way to do this - in SQL - is to use a foreign_key, which essentially shows the likes of ActiveRecord (and SQL if you use a join query) which data is associated:
Foreign keys are a standard element of relational database structures, which you probably know.
The reason why your data structure is failing is due to the fact you've replicated the user_id foreign key in your friendships table.
You'll want to refer to the following:
Rails: self join scheme with has_and_belongs_to_many?
This shows you that if you want to create a self referential join table (such as you're doing), you need to use the following:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :friends,
class_name: "User",
join_table: :friendships,
foreign_key: :user_id,
association_foreign_key: :friend_user_id
end
#db/migrate/______.rb
class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships, id: false do |t|
t.integer :user_id
t.integer :friend_user_id
end
add_index(:friendships, [:user_id, :friend_user_id], :unique => true)
add_index(:friendships, [:friend_user_id, :user_id], :unique => true)
end
def self.down
remove_index(:friendships, [:friend_user_id, :user_id])
remove_index(:friendships, [:user_id, :friend_user_id])
drop_table :friendships
end
end
Notice how the references are for user_id and friend_user_id?
These are the two foreign keys you need to make sure your has_and_belongs_to_many is able to associate two objects of the same model.

How to write foreign keys in many-to-many rich join?

I am trying to create many-to-many rich join between AdminUser and Section with join table as SectionEdit. That is created by generating model. Inside create_section_edits, we write
create_table :section_edits do |t|
t.integer :admin_user_id
t.integer :section_id
t.timestamps
t.string :summary
end
Is there any difference in between using :admin_user_id and "admin_user_id"? Same goes for the other primary keys. admin_user_id is foreign key.
No, but it is better practice to use the symbol :admin_user_id
As an alternative consider:
create_table :section_edits do |t|
t.references :admin_user, index: true
t.references :section, index: true
t.string :summary
t.timestamps
end
Notice this way that you can index the foreign keys by adding index: true
Reference:
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-references
It should be same in this scenario.

find by reference

so i have 2 models:
create_table "holders", :force => true do |t|
t.string "faceid"
t.integer "badges_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
i need 2 things:
to get all the badges of a certain faceid holder
to get all the holders of a certain badge.
i know its really noobs question but until now i didnt work with references so i dont really understood from the literature how to make the connection.
You actually need a many to many association on your holder and badge models. So you have to options either use has many :through or use has_and_belongs_to_many. The difference between the two can be found here. I am taking the example for has_many :through.
You need to create three models.
class Holder < ActiveRecord:Base
has_many :badges_holders
has_many :badges, :through => :badges_holders
end
class Badge < ActiveRecord:Base
has_many :badges_holders
has_many :holders, :through => :badges_holders
end
class BadgesHolder < ActiveRecord:Base
belongs :badge
belongs :holder
end
And your migration files needs to be:
create_table "holders", :force => true do |t|
t.string "faceid"
t.timestamps
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.timestamps
end
create_table "badges_holders", :force => true do |t|
t.integer "holder_id"
t.integer "badge_id"
t.timestamps
end
Now you can easily use Holder.find_by_faceid('xyz').badges to find the all hedges held by the holder whose faced is xyz. And Badge.first.holders to get all the holders for the first bedge.
For your question HABTM will be a good option as you do not need any extra field in the join table, so you can just use has_and_belongs_to_many in both of your models and you don't need BadgesHolder model in that case. And for the migration of the join table, replace first line with create_table "badges_holders", :id => false, :force => true do |t| a and remove t.timestamps as the join table for HABTM should not have any other column than the foreign keys.
If it's some Ruby on Rails, you must have 2 models :
class Holder < ActiveRecord:Base
has_many :badges
end
class Badge < ActiveRecord:Base
belongs_to :holder
end
Your entry called badges_id should not be in your holders table ; you should have a holder_id on your "badges" table.
Then, you can simply call
Holder.find_by_faceid('foobar').badges
and
Badge.find(1337).holder
If your badge can belongs to many holders, then you have to write a has_and_belongs_to_many relation.

Rails: can I use polymorphic references with non-integer primary keys?

I have a database that uses UUIDs as primary keys, like this:
create_table "my_table", :id => false, :force => true do |t|
t.string "id", :limit => 36
end
However, when I try to use :references for foreign keys to that table, it generates integer columns for the ID. Can :references be instructed to deal with a non-integer ID? My migration for the referring table is like this:
create_table "child_table" :id => false, :force => true do |t|
t.string "id", :limit => 36
t.references :my_table
end
I know that I could just manually create :my_table_id and :my_table_type columns, but I'm wondering whether :references can be made to do its magic under these circumstances so that I don't have to handle the id+type explicitly throughout my code.
A :type option has been added when referencing since Rails 4.2
t.references :car, type: :uuid, index: true
For example:
def change
enable_extension 'uuid-ossp'
create_table :cars, id: :uuid do |t|
t.integer :seats
# And other car-specific things
end
create_table :wheels do |t|
t.references :car, type: :uuid, index: true
t.integer :radius
# And other wheel-specific things
end
end
source: https://github.com/rails/rails/pull/16231
Nope, references only creates integer columns as of this writing.
I'm sure you could override the references method to do what you want. But IMO you'd be better off specifying your UUID columns and type columns explicitly. That way the code is clear about what is going on behind the scenes.

Resources