I have a problem in association between two classes, so i have a class table here named Post
Class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :post_type , null: false
t.text :content , null: false
t.integer :person_id
end
add_index :posts, :person_id
add_index :posts, :group_id
end
end
and the other one is called Action
class CreateActions < ActiveRecord::Migration
def change
create_table :actions do |t|
t.string :target_type, null:false
t.integer :target_id
t.integer :upvote_count
t.timestamps
end
add_index :actions,:target_id
end
end
so the problem is i want to associate the target_is as the foreign key to the Post class so i did this
class Post < ActiveRecord::Base
has_one :action
end
class Action < ActiveRecord::Base
belongs_to :post , :class_name => 'Target', :foreign_key => 'target_id'
end
but is doesn't work, which when i assign Action object to action method in Post object this error is appeared
Mysql2::Error: Unknown column 'actions.post_id' in 'where clause': SELECT `actions`.* FROM `actions` WHERE `actions`.`post_id` = 6 LIMIT 1
so any help?
You need to set the foreign key on both sides of the association:
class Post < ActiveRecord::Base
has_one :action, :foreign_key => 'target_id'
end
class Action < ActiveRecord::Base
belongs_to :post , :class_name => 'Target', :foreign_key => 'target_id'
end
http://guides.rubyonrails.org/association_basics.html#has_one-association-reference
http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference
I suppose you are trying to apply polymorphic association. Try this out.
class Post < ActiveRecord::Base
has_one :action, :as => :target
end
class Action < ActiveRecord::Base
belongs_to :target, :polymorphic => true
end
Related
I have 2 Models with association has_many along with cascade property between them.
class ServicesBrandDetail < ApplicationRecord
has_many :services_brands, foreign_key: "brand_id", dependent: :delete_all
end
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_details, foreign_key: "brand_id",
end
Migration for both files
class CreateServicesBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :services_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServicesBrands < ActiveRecord::Migration[6.1]
def change
create_table :services_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :services_brand_details}
t.timestamps
end
end
end
Now I was able to create and save data in from ServicesBrandDetails model. but the Problem is when i create record from ServiceBrand It created record perfectly but i was not able to store data in DB.
record = ServicesBrandDetail.create(:brand => "a", :mail_list => 'abc#mail.com', :cc_list => 'def#mail.com')
record.save
Record successfully stored in DB.
child = record.services_brands.new(:warehouse => "in") <-- record was created successfully.
child.save
it give me error
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.5/lib/active_record/inheritance.rb:237:in `compute_type': uninitialized constant ServicesBrand::ServicesBrandDetails (NameError)
Please follow proper Naming convention
This article might help - https://www.bigbinary.com/learn-rubyonrails-book/summarizing-rails-naming-conventions
In ServiceBrand Model
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
belongs_to should be foreign key name i.e brand in your case
You can delete existing models and tables from your codebase and try below one. (I've tested)
class ServiceBrandDetail < ApplicationRecord
has_many :service_brands, foreign_key: :brand_id, dependent: :delete_all
end
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
Migration for both files
class CreateServiceBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :service_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServiceBrands < ActiveRecord::Migration[6.1]
def change
create_table :service_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :service_brand_details}
t.timestamps
end
end
end
Then try to create model objects which you tried in your question. It will work 👍🏽
In your model ServicesBrand you have to use singular association name for belongs_to
Change this belongs_to :services_brand_details to this belongs_to :services_brand_detail
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_detail, foreign_key: "brand_id"
end
I have 3 models:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
end
class Language < ActiveRecord::Base
has_many :user_languages
has_many :users, :through => :user_languages
end
class User < ActiveRecord::Base
has_many :user_languages, :dependent => :destroy
has_many :languages, :through => :user_languages
accepts_nested_attributes_for :user_languages, :allow_destroy => true
end
I'm using nested_form gem to help user select which language(s) they can speak in. The CRUD for that is working fine.
But, I can't validate uniqueness of the UserLanguage. I try this 2 syntax but they didn't work for me:
validates_uniqueness_of :language_id, scope: :user_id
validates :language_id, :uniqueness => {:scope => user_id}
My schema for user_languages table:
create_table "user_languages", force: :cascade do |t|
t.integer "user_id"
t.integer "language_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "user_languages", ["language_id"], name: "index_user_languages_on_language_id", using: :btree
add_index "user_languages", ["user_id"], name: "index_user_languages_on_user_id", using: :btree
What should I do to make sure one user can choose only a language once? Say, if I select English inside the dropdown, my second English will not be saved (duplicate) and rejected.
This is how I did it finally:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
def self.delete_duplicated_user_languages(user_id)
user_languages_ids = UserLanguage.where(user_id: user_id).pluck(:language_id).sort
duplicate_language_ids = user_languages_ids.select {|language| user_languages_ids.count(language) > 1}
duplicate_language_ids.uniq!
keep_this_language = []
duplicate_language_ids.each do |language_id|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language_id).id
end
single_language = user_languages_ids.select {|language| user_languages_ids.count(language) == 1}
single_language.each do |language|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language).id
end
UserLanguage.where(user_id: user_id).where.not(id: keep_this_language).destroy_all
end
end
I save all UserLanguages first and delete them (duplicate ones) later.
If User has and should only have one Language, then you could change the cardinality between the models:
class Language < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
has_one :language
end
Then by definition your users will only have one language at a time and your overall model will be simpler.
You can read more about active record associations and cardinalities in this Association Basics guide
I have the next two models
class Currency < ActiveRecord::Base
# something ...
end
class CurrencyRate < ActiveRecord::Base
attr_accessible :rate
belongs_to :currency_to_convert, :class_name => 'Currency'
belongs_to :currency_converted, :class_name => 'Currency'
end
how should I create the migration for the CurrencyRate model, only with one field for the foreign key association or two fields?
I have been thinking in something like
class CreateCurrencyRates < ActiveRecord::Migration
def change
create_table :currency_rates do |t|
t.integer :currency_id
t.timestamps
end
end
end
but I'm not sure if works for a model with two belongs_to associations.
class CurrencyRate < ActiveRecord::Base
attr_accessible :rate
belongs_to :currency_to_convert, :class_name => 'Currency', :foreign_key => 'currency_convert_id'
belongs_to :currency_converted, :class_name => 'Currency', :foreign_key => 'currency_converted_id'
end
class CreateCurrencyRates < ActiveRecord::Migration
def change
create_table :currency_rates do |t|
t.integer :currency_converted_id
t.integer :currency_convert_id
t.timestamps
end
end
end
I'm making a database:
class CreateUsers < ActiveRecord::Migration
def change
has_many :listings, :dependent => :restrict #won't delete if listings exist
has_many :transactions, :dependent => :restrict #won't del if trans exist
create_table :users do |t|
t.integer :key #it's hard to use string as primary
t.string :identifier_url
t.string :username
t.integer :rating
t.timestamps
end
end
end
and
class CreateListings < ActiveRecord::Migration
def change
has_one :book
belongs_to :transaction
belongs_to :user
create_table :listings do |t|
t.integer :key
t.integer :condition
t.decimal :price
t.timestamps
end
end
end
I can't find anything anywhere on this so I'm guessing it's something really basic.
The associations (has_many, belongs_to etc...) should be declared in the model, not in the migration.
This is a good read to start with migrations:
http://guides.rubyonrails.org/migrations.html
And this one for associations:
http://guides.rubyonrails.org/association_basics.html
Put your association in the model
class Member < ActiveRecord::Base
has_many :listings, :dependent => :restrict
has_many :transactions, :dependent => :restrict
end
You don't have to declare the associations in the migration, but in the models!
I have no idea what went wrong but I can't get belongs_to work with :class_name option. Could somebody enlighten me. Thanks a lot!
Here is a snip from my code.
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.text :name
end
end
def self.down
drop_table :users
end
end
#####################################################
class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.text :title
t.integer :author_id, :null => false
end
end
def self.down
drop_table :books
end
end
#####################################################
class User < ActiveRecord::Base
has_many: books
end
#####################################################
class Book < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :validate => true
end
#####################################################
class BooksController < ApplicationController
def create
user = User.new({:name => 'John Woo'})
user.save
#failed_book = Book.new({:title => 'Failed!', :author => #user})
#failed_book.save # missing author_id
#success_book = Book.new({:title => 'Nice day', :author_id => #user.id})
#success_book.save # no error!
end
end
environment:
ruby 1.9.1-p387
Rails 2.3.5
class User < ActiveRecord::Base
has_many :books, :foreign_key => 'author_id'
end
class Book < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id', :validate => true
end
The best thing to do is to change your migration and change author_id to user_id. Then you can remove the :foreign_key option.
It should be
belongs_to :user, :foreign_key => 'author_id'
if your foreign key is author id. Since you actually have User class, your Book must belong_to :user.
I do in this way:
Migration -
class AddAuthorToPosts < ActiveRecord::Migration
def change
add_reference :posts, :author, index: true
add_foreign_key :posts, :users, column: :author_id
end
end
class Post
belongs_to :author, class_name: "User"
migration
t.belongs_to :author, foreign_key: { to_table: :users }
working on rails 5