Possible to alias a belongs_to association in Rails? - ruby-on-rails

I have a model with a belongs_to association:
class Car < ActiveRecord::Base
belongs_to :vendor
end
So I can call car.vendor. But I also want to call car.company! So, I have the following:
class Car < ActiveRecord::Base
belongs_to :vendor
def company
vendor
end
end
but that doesn't solve the assignment situation car.company = 'ford', so I need to create another method for that. Is there a simple alias mechanism I can use for associations? Can I just use alias_method :company, :vendor and alias_method :company=, :vendor=?

No it doesn't look for company_id for instance change your code as follows
In Rails3
class Car < ActiveRecord::Base
belongs_to :vendor
belongs_to :company, :class_name => :Vendor,:foreign_key => "vendor_id"
end
In Rails4
We can use alias attribute.
alias_attribute :company, :vendor

In Rails 4, you should simply be able to add alias_attribute :company, :vendor to your model.

Short Version:
Generate model with migration
$ rails generate model Car vendor:references name:string ...
Add following line in Car model i.e car.rb file
class Car < ActiveRecord::Base
belongs_to :company, :class_name => 'Vendor', :foreign_key => 'vendor_id'
end
Now you have #car.company instance method.
For a Detailed explanation read ahead [Optional if you understood the above !!]
Detailed Version:
The model Car will have an association with the model Vendor (which is obvious). So there should be a vendor_id in the table cars.
In order to make sure that the field vendor_id is present in the cars table run the following on the command line. This will generate the right migration. The vendor:references is important. You can have any number of attributes after that.
$ rails generate model Car vendor:references name:string
Or else in the existing migration for create_table :cars just add the line t.references :vendor
class CreateCars < ActiveRecord::Migration
def change
create_table :cars do |t|
t.string :name
...
t.references :vendor
t.timestamps
end
end
end
The final thing that you need to do is edit the model Car. So add this code to your car.rb file
class Car < ActiveRecord::Base
belongs_to :company, :class_name => 'Vendor', :foreign_key => 'vendor_id'
end
After you do the third step you will get the following instance methods for the model Car provided by Rails Associations
#car.company
When you do #car.company it will return a #<Vendor ...> object. To find that #<Vendor ...> object it will go look for the vendor_id column in the cars table because you have mentioned :foreign_key => 'vendor_id'
You can set the company for a car instance by writing
#car.company = #vendor || Vendor.find(params[:id]) #whichever Vendor object you want
#car.save
This will save the id of that Vendor object in the vendor_id field of the cars table.
Thank You.

class Car < ActiveRecord::Base
belongs_to :vendor
belongs_to :company, :class_name => :Vendor
end

Related

how to create a record for a join table?

I have the following associations set up:
class Book < ActiveRecord::Base
belongs_to :author
belongs_to :category
has_many :users_books
has_many :users, through: :user_books
end
and
class User < ActiveRecord::Base
has_many :users_books
has_many :books, through: :users_books
end
I created a join table migration as I ought to
class CreateUsersBooks < ActiveRecord::Migration[4.2]
def change
create_table :users_books do |t|
t.integer :user_id
t.integer :book_id
end
end
end
Now I need to create a method called check_out_book, that takes in a book and a due_date as arguments. When a user checks out a book, it should create a new UserBook record (or Checkout record or whatever you want to call you join table/model). That new UserBook record should have a attribute (and therefore table column) of returned? which should default to false. How would I go about creating this method and the migrations?
Your tablenames and your associations in Rails should always be singular_plural with the exception of the odd duckling "headless" join tables used by the (pretty useless) has_and_belongs_to_many association.
class CreateUserBooks < ActiveRecord::Migration[4.2]
def change
create_table :user_books do |t|
t.references :user
t.references :book
t.boolean :returned, default: false
end
end
end
class UserBook < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
class Book < ActiveRecord::Base
belongs_to :author
belongs_to :category
has_many :user_books
has_many :users, through: :user_books
end
class User < ActiveRecord::Base
has_many :user_books
has_many :books, through: :user_books
end
But you should really use a better more descriptive name that tells other programmers what this represents in the domain and not just a amalgamation of the two models it joins such as Loan or Checkout.
I would also use t.datetime :returned_at to create a datetime column that can record when the book is actually returned instead of just a boolean.
If you want to create a join record with any additional data except the foreign keys you need to create it explicitly instead of implicitly (such as by user.books.create()).
#book_user = Book.find(params[:id]).book_users.create(user: user, returned: true)
# or
#book_user = current_user.book_users.create(book: user, returned: true)
# or
#book_user = BookUser.new(user: current_user, book: book, returned: true)

Rails - relation between activeadmin model and custom model

First I would like to know if there is any possibilities to associate one of my model with the ActiveAdmin::Comment and the AdminUser models
this is my model
class AdminAction < ActiveRecord::Base
has_one :comment, :class_name => "ActiveAdmin::Comment", :foreign_key => "admin_action_id"
belongs_to :admin_user
end
thoses associations don't raise any errors, just returning `nil``
I have added a field in thoses two models :
add_column :admin_users, :admin_action_id, :integer
add_column :active_admin_comments, :admin_action_id, :integer
The goal here is to fetch the AdminUser and the Comment associate to my new model AdminAction
and when I do
a = AdminAction
a.admin_user
# and
a.comment
it works
any ideas ?
You need to have a admin_user_id in the admin_actions table to make this belongs_to association work.
class AdminAction < ActiveRecord::Base
belongs_to :admin_user
end
Also, the foreign_key param is unneeded because it will be inferred from the AdminAction class name.
class AdminAction < ActiveRecord::Base
has_one :comment, :class_name => "ActiveAdmin::Comment", :foreign_key => "admin_action_id"
end
Other than that, what you have should work as expected. If it is not, please provide more detail as to what you are seeing, or not seeing as the case may be.
I have this working, albiet with a User model rather than AdminUser. Here is my code:
Migrations
class CreateAdminAction < ActiveRecord::Migration
def change
create_table :admin_actions do |t|
t.references :user, index: true
t.timestamps
end
end
end
class AddFieldsForAdminAction < ActiveRecord::Migration
def change
add_column :active_admin_comments, :admin_action_id, :integer
end
end
AdminAction class
class AdminAction < ActiveRecord::Base
has_one :comment, class_name: 'ActiveAdmin::Comment'
belongs_to :user
end
Another thought: if you are looking to get the ActiveAdmin::Comment records for a single AdminUser, I think you can fetch them directly like this:
admin_comments = ActiveAdmin::Comment.find_for_resource_in_namespace(AdminUser.find(some_id), :admin)

Understanding Polymorphic Associations in Rails

I have a parent model called Quote. which has an attribute called final_quote and has a child model called QuoteBoms, which has attributes called quote_final_quote and quantity and total_quote (=quote_final_quote * quantity)
class Quote < ActiveRecord::Base
has_many :quote_boms, dependent: :destroy
accepts_nested_attributes_for :quote_boms, :reject_if => :all_blank, :allow_destroy => true
class QuoteBom < ActiveRecord::Base
belongs_to :quote
has_many :quotes
end
Now in the nested model, I am selecting the quote with the association "belongs_to :quote" but has_many :quotes does not work as I have only one quote_id column (I suppose this is the problem). I see that i need to define a third class as quotebom_quote_id but cannot figure out how exactly!
Any help will be greatly appreciated!
class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
class Profile < ActiveRecord::Base
has_many :images, :as => :imageable
end
class Article < ActiveRecord::Base
has_many :images, :as => :imageable
end
This is how we have made a single Image model and it is accessed by one or more than one model
Please refer this
Link
From what I can tell, you wish to create a database structure containing the models Quote and QuoteBom where a Quote has many QuoteBom and QuoteBom belongs to many Quotes.
That being the case, you will want to use a has_and_belongs_to_many association.
This will require adding to your models
class Quote < ActiveRecord::Base
has_and_belongs_to_many :quote_boms
end
class QuoteBom < ActiveRecord::Base
has_and_belongs_to_many :quotes
end
...and the following migration (assuming Quote and QuoteBom already exist)
class CreateQuotesAndQuoteBoms < ActiveRecord::Migration
def change
create_table :quote_quote_boms, id: false do |t|
t.belongs_to :quote, index: true
t.belongs_to :quote_bom, index: true
end
end
end
By having the associations above in the model and this table in your database, rails will automagically handle the associations between quote and quote_doms. As a result, you will also be able to access quote_dom.quotes which you said you weren't able to do in your question.
This is NOT a polymorphic association. A polymorphic association allows a model to belong to more than one type of other model in a single association.

Has many :through association not found

I have two models that can have tags added to them.
Player
Ticket
and I have a Tag model which belongs to both so I have two join models
tag_ticket
tag_player
I am getting a Could not find the association :tag_tickets in model Ticket error but my association is in there.
class Ticket < ActiveRecord::Base
has_many :tag_tickets
has_many :tags, :through => :tag_tickets
end
I'm just focusing on the Ticket model but the player model should look similar.
this is my migration for TagTicket
class CreateTagTickets < ActiveRecord::Migration
def change
create_table :tag_tickets do |t|
t.integer :ticket_id
t.integer :tag_id
t.timestamps
end
end
end
You need to specify the :tag_tickets join first like this:
class Ticket < ActiveRecord::Base
has_many :tag_tickets
has_many :tags, :through => :tag_tickets
end
You would also need to specify the joins in your TagTicket model:
class TagTicket < ActiveRecored::Base
belongs_to :ticket
belongs_to :tag
end
Alternatively, you can skip all this and use a habtm join (only recommended if the tag_tickets join is truly only used as a join and has no primary key for itself). In this case you would have no TagTicket model (just a tag_tickets table) and the Ticket model would look like this:
class Ticket < ActiveRecord::Base
has_and_belongs_to_many :tags
end

Create join table with no primary key

I have two tables with a many to many relationship that I am using has_and_belongs_to_many to define the association.
class Foo < ActiveRecord::Base
...
has_and_belongs_to_many :bar
...
end
class Bar < ActiveRecord::Base
...
has_and_belongs_to_many :foo
...
end
I also have the class defined to represent the join table
class BarFoo < ActiveRecord::Base
...
belongs_to :foo
belongs_to :bar
...
end
When I run rake db:seed I get the following error:
Primary key is not allowed in a has_and_belongs_to_many join table (bar_foo)
If I edit the database and remove the primary key field (ID) from the bar_foo table and then rerun rake db:seed everything works as desired.
Given the above, what is the preferred means of creating join tables in rails with no primary key?
I also tried using "has_many :bars, :through => :foo" and vise versa but got an error message something like "undefined method 'klass' for nil:NilClass".
Yes, primary key is not allowed for has_and_belongs_to_many.
You have 2 ways to solve this:
Remove the primary key on that table. In your migration class:
create_table :bar_foo, :id => false do |t|
t.integer :bar_id
t.integer :foo_id
end
Apart from this, you will have to delete the file bar_foo.rb from app/models and also remove any fixture and test files that might have been generated. A good idea is to call the script/destroy (or rails destroy) to destroy the files and then regenerating the migration.
Or convert to has_many :through
class Foo < ActiveRecord::Base
...
has_many :bar_foos
has_many :bars, :through => :bar_foos
...
end
class Bar < ActiveRecord::Base
...
has_many :bar_foos
has_many :foos, :through => :bar_foos
...
end
class BarFoo < ActiveRecord::Base
...
belongs_to :foo
belongs_to :bar
...
end
You don't need the model
class BarFoo < ActiveRecord::Base
...
belongs_to :foo
belongs_to :bar
...
end
the has_and_belongs_to_many association will search for a table called bar_foo in your database what you need to do is generate a migration to create this table.
rails generate migration add_table_bar_foo_for_association
then you edit your migration and it should look like this
class AddTableBarFooForAssociation < ActiveRecord::Migration
def up
create_table :bar_foo, :id => false do |t|
t.references :bar
t.references :foo
end
end
def down
drop_table :bar_foo
end
end
Now your association should work and also if you need the association to have extra attributes on the join you can use the has_many :through way and create a model associated to this.
If you want to use a HABTM association you shouldn't create a model for it - just a table bars_foos with bar_id and foo_id integer columns.
If you need the model in between (e.g. if you want to keep track of created_at or some other attributes of the relation) you can add additional model e.g. Barred and then you'd have:
class Foo < ActiveRecord::Base
...
has_many :bars, :through => :barred
...
end
class Bar < ActiveRecord::Base
...
has_many :foos, :through => :barred
...
end
class Barred < ActiveRecord::Base
has_many :bars
has_many :foos
end

Resources