How to use unique key as foreign key - ruby

I have an N:N relationship between 2 tables, users and titles. These tables link using a third table purchases which should have the id of each of the others table. The problem is that for the titles I do not require the title_id on the purchases table, but a different column named item_id which is UNIQUE and NOT NULL on the titles table.
Currently I have the following migration, but it is not working. I might be missing something:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases, :id => false do |t|
t.belongs_to :title, foreign_key: 'item_id'
t.belongs_to :user, :null => false
end
end
end
How can I get my purchases table to reference the titles table using that item_id key on the titles table rather than title_id ?

Migration:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases, :id => false do |t|
t.integer :item_id
t.integer :user_id, :null => false
end
end
end
In your models:
# app/models/title.rb
class Title < ActiveRecord::Base
# ...
has_and_belongs_to_many :users,
class_name: "User",
foreign_key: "item_id",
association_foreign_key: "user_id",
join_table: "purchases"
# ...
end
# app/models/user.rb
class User < ActiveRecord::Base
# ...
has_and_belongs_to_many :titles,
class_name: "Title",
foreign_key: "user_id",
association_foreign_key: "item_id",
join_table: "purchases"
# ...
end
Don't forger add indexes to migration if its need.

Related

Active Admin Tag Collection with joined table

I have a problem to display the correct values in the Active Admin Tag collection.
enter image description here
To build many to many relationships I created a joined table:
create_table "profession_allocations", force: :cascade do |t|
t.integer "master_id"
t.integer "slave_id"
t.index ["master_id"], name: "index_profession_allocations_on_master_id", using: :btree
t.index ["slave_id"], name: "index_profession_allocations_on_slave_id", using: :btree
end
and a model:
class ProfessionAllocation < ApplicationRecord
has_and_belongs_to_many :master, :class_name => 'Profession'
has_and_belongs_to_many :slave, :class_name => 'Profession'
end
to build relationships with my profession table:
create_table "professions", force: :cascade do |t|
t.string "kind", null: false
t.string "list", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "summary_de", default: "summary german", null: false
t.string "summary_en", default: "summary english", null: false
end
and the model:
class Profession < ApplicationRecord
validates_presence_of :kind, :list
validates :kind, presence: true, uniqueness: true
validates :list, presence: true
validates :summary_de, presence: true
validates :summary_en, presence: true
has_many :master_profession_allocations, :class_name => 'ProfessionAllocation', :foreign_key => 'master_id'
has_many :slave_profession_allocations, :class_name => 'ProfessionAllocation', :foreign_key => 'slave_id'
end
Inside Active Admin I use the gem activeadmin_addons to use tags, but I guess this does not madder for this issue.
if f.object&.persisted?
f.input :master_profession_allocations, as: :tags, collection: Profession.where.not(id: f.object.id).order('summary_en ASC'), display_name: :summary_en
else
f.input :master_profession_allocations, as: :tags, collection: Profession.all.order('summary_en ASC'), display_name: :summary_en
end
end
While creating the ProfessionAllocation works fine with the Tag collection, the edit view makes problems, because it displays the ProfessionAllocations and not the related Professions.
My question is, how can I display the related Profession in the Tag Collection?
Best
Alex
I solved it by create a subsection. This allows me also to store addtional data in the allocation:
f.inputs do
f.has_many :master_profession_allocations, heading: 'Slaves', allow_destroy: true, new_record: true do |pa|
if f.object&.persisted?
pa.input :level, as: :select, collection: ProfessionAllocation::LEVELS
pa.input :slave_id, as: :select, collection: Profession.joins('LEFT JOIN profession_allocations pa on professions.id = pa.master_id WHERE professions.id !=' + f.object.id.to_s).map { |pa| [pa.summary_en, pa.id] }
else
pa.input :level, as: :select, collection: ProfessionAllocation::LEVELS
pa.input :slave_id, as: :select, collection: Profession.joins('LEFT JOIN profession_allocations pa on professions.id = pa.master_id').map { |pa| [pa.summary_en, pa.id] }
end
end
end

Ruby ActiveModel::MissingAttributeError

I'm trying to learn on my own ruby database relations.
I have a relation of 1 "Category" to many "Products" and I'm trying to add a product to the remote database (heroku server).
TIMESTAMP_create_products.rb
class CreateProducts < ActiveRecord::Migration
def up
create_table :products, primary_key: 'product_id' do |p|
p.index :product_id
p.string :name
p.decimal :price
p.references :categories, index: true
end
end
def down
drop_table :products
end
end
TIMETSTAMP_create_categories.rb
class CreateCategories < ActiveRecord::Migration
def up
create_table :categories, primary_key: 'category_id' do |c|
c.index :category_id
c.string :name
c.integer :parentId
end
end
def down
drop_table :categories
end
end
model.rb
class Products < ActiveRecord::Base
self.primary_key = "product_id"
validates :name, presence: true, uniqueness: true
belongs_to :categories, class_name: "Categories", foreign_key: 'category_id'
end
class Categories < ActiveRecord::Base
self.primary_key = "category_id"
validates :name, presence: true, uniqueness: true
has_many :products, class_name: "Products"
end
I add manually a category to the database and every time I try to execute the code:
Products.create(name: "name1", price: "1.1", categories: Categories.find(1))
It gives me the output:
ActiveModel::MissingAttributeError can't write unknown attribute category_id
Is there anything missing here? I don't understand why this is not working.
You may have a problem with singular / plural.
In your migration, to create the table products, you have the line:
p.references :categories, index: true
This should add to your table the column categories_id.
However, in the Products model, the foreign key is set to category_id. So when you try to attach a category to a production, it's trying to write the ID of the category to the column category_id of the table categories, which doesn't exists.
By changing the reference name in the products migration, everything should work fine:
create_table :products, primary_key: 'product_id' do |p|
p.index :product_id
p.string :name
p.decimal :price
p.references :category, index: true
end

Validate uniqueness nested model params

I have three models:
class User < ActiveRecord::Base
has_many :user_countries
accepts_nested_attributes_for :user_countries, reject_if: :all_blank
validate :user_countries
end
class UserCountry < ActiveRecord::Base
belongs_to :user
belongs_to :country
end
class Country < ActiveRecord::Base
has_many :user_countries
has_many :users, :through => :user_countries
end
In a the user creation form I can create user_countries too. How I can validate in server the uniqueness of country_id in user_countries. A user has many countries: France, United State... but not for example: France and France.
I've added this to user_countries.. but it don't work:
validates :user_id, uniqueness: {scope: :country_id, allow_blank: false}
Try this in the user_country model.
class UserCountry < ActiveRecord::Base
belongs_to :user
belongs_to :country
validates_uniqueness_of :user_id, scope: :country_id, allow_blank: false
end
Also, you need to create a migration like below to add unique indexes on database level.
add_index :user_countries, [ :user_id, :country_id ], :unique => true

Rails migrations: Drop table and change references

Help me please create a correct migration for next needs
Drop table "hiring_question_groups"
Change references (:group) in "hiring_questions" to references(:company)
I have this migration before:
class CreateHiringQuestionsAndGroups < ActiveRecord::Migration
def change
create_table :hiring_questions do |t|
t.references :group
t.string :title
t.text :description
t.boolean :is_active, default: true
t.timestamps
end
create_table :hiring_question_groups do |t|
t.references :company
t.string :title
t.boolean :is_active, default: true
t.timestamps
end
end
end
Table which should be dropped:
class Hiring::QuestionGroup < ActiveRecord::Base
belongs_to :company
has_many :questions, class_name: 'Hiring::Question', dependent: :destroy, foreign_key: 'group_id'
This table should be changed:
class Hiring::Question < ActiveRecord::Base
belongs_to :group, class_name: 'Hiring::QuestionGroup'
to:
class Hiring::Question < ActiveRecord::Base
belongs_to :company, class_name: ???
How I can do this with One migration?
That's very simple!
def change
remove_reference :hiring_questions, :group
drop_table :hiring_question_groups
add_reference :hiring_questions, :company
end
Sorry!)

rails belong_to which class to choose

There is a model relation like this.
class A
belongs_to :ref_config,:class_name => 'User'
end
My question is :
the A has a attribute named flag, now i want to create a function like this:
if flag == 1, I want the class A like this belongs_to :ref_config,:class_name => 'Department and if flag == 2, i want the class A like this belongs_to :ref_config,:class_name => 'User'
How can I implement the function
Thank you!
Have a look at polymorphic associations, which will let you use the same belongs_to relation to refer to different models.
You could configure your models something like this:
class A < ActiveRecord::Base
belongs_to :ref_config, :polymorphic => true
end
class Department < ActiveRecord::Base
has_many :as, :as => :ref_config
end
class User < ActiveRecord::Base
has_many :as, :as => :ref_config
end
To set up the needed columns in the A table, use a migration like this:
class CreateAs < ActiveRecord::Migration
def self.up
create_table :as do |t|
t.string :name # or whatever other attributes A should have
t.references :ref_config, :polymorphic => true
end
end
def self.down
drop_table :as
end
end
From the little i got your question following may help you.
class A
belongs_to :department_config, :class_name => 'Department', :conditions=> flag= 1
belongs_to :user_config, :class_name => 'User', :conditions=> flag= 2
end

Resources