How to set foreign key in Rails - ruby-on-rails

I Have a model named employee. The following is my migration file.
class CreateEmployees < ActiveRecord::Migration
def change
create_table :employees, id: false do |t|
t.string :name
t.string :password
t.string :role
t.primary_key :name
end
end
end
Now, I want to create a model named "teamplayer" with the columns as 'name' which needs to refers 'name' column in employee model. And 'tl' column
which is independent to this model. The following is my "teamplayer" migration file.
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.string :tl
t.string :name
end
end
end
In the above file, how to reference 'name' column to the model employee? So how to achieve foreign key in rails.

I think you want to look into Active Record Associations (http://guides.rubyonrails.org/association_basics.html)
I know you've asked to create a foreign key on name but unless you plan to ensure that name is unique, then this is possibly not the best plan (depending on the actual relationship you are trying to model - one to many / one to one etc).
I would be tempted to set up the foreign key relationship on employees.id. To do this, you can use the has_many and belongs_to associations.
You could change your teamplayers migration as follows:
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.belongs_to :employee
t.string :tl
end
end
end
Then in your Employee model, you can add the has_many side of things:
class Employee < ActiveRecord::Base
has_many :teamplayers
end
You can still easily get the Employee name given a Team Player record with a simple join.
Edit - to get the Employee, you can do something like this, assuming #tis a teamplayer instance:
#t.employee.name
(the code is untested and from memory so....)

You can do it in the teamplayer model, you just need to add index in your migration
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.string :tl
t.string :name
end
add_index :teamplayers, :name
end
end
You can set name as Primary key inside the employee model like this
class Employee < ActiveRecord::Base
self.primary_key = "name"
has_many :teamplayers
end
Now inside the model Teamplayer you can set the foreign key
class Teamplayer < ActiveRecord::Base
belongs_to :employee, foreign_key: 'name'
end
This should reference 'name' to employee model

Related

Rails 5 - Creating two new Models Inheriting from a base Model

Lets say that I have an User model with its attributes (first_name, last_name, etc) and I want to create two new models Teacher and Student.
They will inherit the User model attributes, and also, they will have specific attributes. For instance, the Student model will have a file attribute, and the Teacher model will have subject attribute.
I was reading about STI (Single Table Inheritance) and Polymorphic relationships.
What should I look for to accomplish this? Do you have any example to show?
If you create an attribute called "type" on your users table, Rails will automatically assume you want to implement STI. Then, creating Teacher and Student models is as simple as extending the User class. The name of the child class will automatically be inserted into the type column and used to filter queries as you would expect.
user.rb
class User < ApplicationRecord
end
teacher.rb
class Teacher < User
end
student.rb
class Student < User
end
With STI, you place all of the columns that either model will use in the same table and simply ignore (default to null) the ones that don't apply in any give situation.
A polymorphic relationship allows two or more tables to fill the same association. If you want to use three different tables but ensure that a User has either a Teacher or a Student, that could be modeled as a polymorphic belongs_to. The downside is that you would need to get back to the User model to access the shared information, i.e. teacher.user.first_name.
I have found this gem that looks like what I am looking for. I have played with it a little and it does work for me.
https://github.com/krautcomputing/active_record-acts_as
So, for my case I have added to the Gemfile:
gem 'active_record-acts_as'
Then:
$ bundle
These are my migrations:
# 20171202142824_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.date :birth_date
t.string :dni
t.string :cuil
t.string :email
t.string :phone
t.string :address
t.string :postal_code
t.string :city
t.string :state
t.string :country
t.actable # This is important!
t.timestamps
end
end
end
# 20171202142833_create_students.rb
class CreateStudents < ActiveRecord::Migration[5.1]
def change
create_table :students do |t|
t.string :file
# Look, there is no timestamp.
# The gem ask for it to be removed as it uses the User's timestamp
end
end
end
# 20171202142842_create_teachers.rb
class CreateTeachers < ActiveRecord::Migration[5.1]
def change
create_table :teachers do |t|
# Look, there is no timestamp.
# The gem ask for it to be removed as it uses the User's timestamp
end
end
end
These are my models:
# user.rb
class User < ApplicationRecord
actable
validates_presence_of :first_name, :last_name
def full_name
[last_name.upcase, first_name].join(', ')
end
end
# student.rb
class Student < ApplicationRecord
acts_as :user
validates_presence_of :file
end
# teacher.rb
class Teacher < ApplicationRecord
acts_as :user
end
Now, with all that set, you can simply create a new Student and a new Teacher doing:
Student.create!(first_name: 'John', last_name: 'Doe', file: 'A125')
=> #<Student id: 3, file: "A125">
Teacher.create!(first_name: 'Max', last_name: 'Power')
=> #<Teacher id: 1>
You have access to all the methods and attributes of the User. For instance:
Teacher.last.full_name
=> "POWER, Max"

Relation one to many with 2 foreigns keys of the same table Ruby on rails

I have a table called customers. These table has two addresses . One address of work and One direccion of house
Those 2 addresses belong to a table called addresses
I don't know how to relation those 2 tables
Migrations
class CreateCustomers < ActiveRecord::Migration
def change
create_table :customers do |t|
t.string :name
t.integer :address_id #Address of work
t.integer :address_id_1 #Address of home
t.timestamps
end
end
end
class CreateAdresses < ActiveRecord::Migration
def change
create_table :adresses do |t|
t.string :street
t.timestamps
end
end
end
I do not believe this is a good approach or database design. If you want to proceed this way and not get out of the rails convention just create two columns address_id and address_two_id
and in customer.rb
belongs_to :address, class_name: "Address"
belongs_to :address_two, class_name: "Address"
By default rails takes the name of the foreign key and stores it in a column called "name"+"_id"
The better way is two have a column customer_id in your Address model and create a relation in your customer class
customer.rb
has_many :addresses
And you can also validate that a customer has no more than two addresses by adding this validation to
address.rb
validate :validate_two_addresses
def validate_two_addresses
address_count = Address.where(customer_id: self.customer_id).count
errors.add(:base, "You cannot have more than 2 addresses.") unless address_count < 3
end

rails nested resource unknown attribute error

I have a Contract and a Task_Order model. I keep getting an unknown attribute error for contract_id Each Contract has many Task Orders. I have read other nested models unknown attribute error questions but they haven't been able to help me. Please keep in mind I am pretty new to Rails and would greatly appreciate any help I can get. I am using Rails 4.0
Contract Model:
has_many :task_orders
Contract schema:
create_table "contracts", force: true do |t|
t.string "contractId"
t.string "contractName"
end
Task Order Model:
belongs_to :contracts
Task Order Schema:
create_table "task_orders", force: true do |t|
t.string "contract_Id"
t.string "task_orderId"
t.string "task_orderName"
end
When I click Show Contract, I get the error:
unknown attribute: contract_id
This is the line that gets highlighted:
<%= form_for([#contract, #contract.task_orders.new]) do |f| %>
I can tell that Rails is trying to print out contract_id, which is not in my Contract model... so how can I get it to print out contractId instead - which is in my Contract model?
Thanks!!
Task Order Model should have this line belongs_to contract
belongs_to association should be declared as a singular of corresponding model
Also there should be contract_id column within task_orders table.
Diagram below explains default behavior of belongs_to in Rails
Something you need to be aware of is the foreign_key of Rails (and relational databases in general):
Foreign Key
Rails' standard foreign_key is to use snake_case (contract_id), however, you can use non-conventional foreign_keys like this:
#app/models/order.rb
belongs_to :contract, foreign_key: "contract_Id"
#schema SHOULD be:
create_table "orders", force: true do |t|
t.integer "contract_id" #-> should
t.string "contract_Id" #-> your current
end
Primary Key
create_table "contracts", force: true do |t|
t.string "contractId" #-> don't need
t.string "contractName" #-> your current
t.string "name" #-> should be
end
Your primary_key is almost always going to be the id column. You should remove your contractId column from the contracts db!
Task Orders
You'll need to do this:
#app/models/order.rb
belongs_to :contracts
has_many :task_orders
You'll then need another model at app/models/task_order.rb
Form
Your form is showing the error. This is because you're trying to create an ActiveRecord in the view itself. You'll be much better using the standard accepts_nested_attributes_for method of passing nested model data through a form:
#app/models/contract.rb
def new
#contract = Contract.new
#contract.task_orders.build
end
#app/views/contracts/new.html.erb
<%= form_for #contract do |f| %>
Firstly,use singular names for belongs_to
Class TaskOrder < ActiveRecord::Base
belongs_to :contract
end
Secondly,try changing your contract_Id in your task_orders table to contract_id.
Rails by default look for model_name_id(in your case contract_id) foreign key unless if any of the custom foreign keys defined in your model.
And finally,specify the data type integer for default foreign key.In your case it should be t.integer contract_id
However if you want contract_Id as foreign key,you should define it as custom foreign key in the Contract model itself like this
Class Contract < ActiveRecord:Base
has_many :task_orders,:foreign_key => "contract_Id"
end

Rails Relational Database not working how I want

In the console
a = Reported.new
This works. After tinkering.
a.profile = Profile.first
But it's not what I want! I want a.profile to even exist. I want a.reported_by to be a profile! And I want a.reported to be a profile!
Again what I want is
a.reported_by = Profile.last #or any such profile
a.reported = Profile.first #or any such profile
Model
class Profile < ActiveRecord::Base
has_many :reported, dependent: :destroy
Migration
It doesn't have a reported column, I am not sure about the right way to implement that either.
class CreateReporteds < ActiveRecord::Migration
def change
create_table :reporteds do |t|
t.belongs_to :profile
t.integer :reported_by
t.string :reason
t.timestamps
end
end
end
Your migration seems... off. I've never seen t.belongs_to :something in a migration - shouldn't it be t.integer :profile_id? (I couldn't find documentation supporting the belongs_to syntax there).
If you want Reported#reported_by to return a Profile, then you need a reported_by_id integer on it, NOT a reported_by integer. Rails has a convention where you should make your referenced objects (in this case, a belongs_to :reported_by relationship) use the relationship_id format for it's foreign key.
Then you should have this in your class:
class Reported < ActiveRecord::Base
belongs_to :reported_by, class_name: "Profile"
end
This will make it so it uses reported_by_id as the foreign key for a Profile object, but return it as Reported#reported_by.
Then:
class Profile < ActiveRecord::Base
has_many :reporteds, foreign_key: 'reported_by_id'
end
Should let you do Profile#reporteds
And your migration would look like this:
class CreateReporteds < ActiveRecord::Migration
def change
create_table :reporteds do |t|
t.integer :reported_by_id
t.string :reason
t.timestamps
end
end
end

Database structure - associations in a multiuser system

I am in the process of building a multiclient system in ROR. (I am looking at http://guides.rubyonrails.org/association_basics.html#polymorphic-associations)
The structure is that a client has a contract, so when he logs in with his username, password and contract, he will have access to the system.
We have the contract id as a “master key”, which has to be in every table in the system.
class CreateContracts < ActiveRecord::Migration
def change
create_table :contracts do |t|
t.integer :contract_id
end
end
end
(chart of accounts)
class CreateCoas < ActiveRecord::Migration
def change
create_table :coas do |t|
t.integer :account_id
t.string :account_name
end
end
end
class CreateCustGroups < ActiveRecord::Migration
def change
create_table :custgroups do |t|
t.integer :account_id1
t.integer :account_id2
t.integer :account_id3
end
end
end
Q1: How do I define the contract with belongs_to? There has to be a relation in every table in the system to the contract table. Do I have to have a relation to all tables? (I think so)
class Contracts < ActiveRecord::Base
has_and_belongs_to_many :Coas
has_many:xxx
belongs:to
end
Q2: How do I define the association on the custgroup? Here we have a record where I have 3 or more fields that link to the same table (COA).
As Jesper said, it's quite hard to follow what you're trying to achieve, but I'll try to reply to your questions :
Q1 : If you want all your tables to reference a contract, you'll need to add to all those tables a foreign_key such as contract_id
so each create_table call will have the contract_id key
create_table :new_models do |t|
t.belongs_to :contract # this will create a contract_id field
end
you can also add an index on the column
add_index :new_models, :contract_id
then in all you models you'll add the belongs_to association :
class NewModel
...
belongs_to :contract
...
end
so if your Coas & CustGroups needs to reference the contract table, you'll have to change both migrations to include the contract_id key and then the models to add the belongs_to association
If a contract needs to have access to all Coas that references it, then you need to use the has_many association
class Contracts < ActiveRecord::Base
...
has_many :coas
...
end
It doesn't look like you need a has_and_belongs_to_many here, but i might be wrong about that.
if a contract also needs to access to CustGroups, you'll add :
has_many :cust_groups in the Contract model.
Q2 : I really didn't get understand what you want to do. Please explain what is the relation between Coas and Custgroups and I'll try to help you

Resources