Rails foreign_key not id - ruby-on-rails

I have two tables
class Sku < ApplicationRecord
validates :sku, :supplier_code, :name, :price, presence: true
belongs_to :supplier, class_name: 'Supplier', foreign_key: 'code'
end
and
class Supplier < ApplicationRecord
validates :code, :name, presence: true
has_many :skies, class_name: 'Sku'
end
i set foreign_key as a code field
but when i tried to create sku with supplier_code which i set to supplier
Supplier.create(code:4,name:2) => OK
i have got an error {:supplier=>[{:error=>:blank}]},
Sku.create(name:2,price:2,sku:3,supplier_code:4).errors

You must need to define on another model
class Sku < ApplicationRecord
validates :sku, :supplier_code, :name, :price, presence: true
belongs_to :supplier, class_name: 'Supplier', foreign_key: 'code'
end
and
class Supplier < ApplicationRecord
validates :code, :name, presence: true
has_many :skies, class_name: 'Sku', primary_key: 'id', foreign_key: 'code'
end

Related

Rails use model in the same namespace for belongs_to reference, how to reference model from outside

I working on a Rails application, currently we structure the app by modules. Right now we have 2 separate model for users: User and Freight::Customer::User.
I have a new model Freight::Customer::MembershipStatus looks like this:
class Freight::Customer::MembershipStatus < ActiveRecord::Base
belongs_to :customer, class_name: 'Freight::Customer'
belongs_to :created_by, class_name: 'User'
validates :from, presence: true
validates :to, presence: true
validates :customer, presence: true
validates :status, presence: true
end
In this case, the created_by is reference to User. But when the code run membership_status.created_by, rails try to look for the Freight::Customer::User, I think it because Rails try to look for model within the same module first.
Is there a way to config this model to use the outer User model class?
You can get user class using this type, try this.
class Freight::Customer::MembershipStatus < ActiveRecord::Base
belongs_to :customer, class_name: 'Freight::Customer'
belongs_to :created_by, class_name: '::User'
validates :from, presence: true
validates :to, presence: true
validates :customer, presence: true
validates :status, presence: true
end

Rails 4 - Validates uniqueness with has_many through

I have these three models:
User:
class User < ActiveRecord::Base
validates :name, presence: true
validates :surname, presence: true
validates :email, presence: true, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
has_many :permissions, dependent: :destroy
has_many :stores, through: :permissions
end
Store:
class Store < ActiveRecord::Base
validates :name, presence: true
validates :description, presence: true
has_many :permissions
has_many :users, through: :permissions
end
Permission:
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :store
end
How can i validate the uniqueness of the user.email based on the store.id?
You don't.
You should validate the uniqueness of the users email in User. And validate the uniqueness of the user_id and store_id in Permission.
class User < ApplicationRecord
# ...
validates_uniqueness_of :email
end
class Permission < ApplicationRecord
validates_uniqueness_of :user_id, scope: 'store_id'
end
This allows a user to have permissions for multiple stores - but does not allow duplicates. In general when linking records together use id's - not something like emails.

Using a self join model in a has_one relationship

I have a self join model captain on my user model. I'm having an issue using the captain in an has_one relationship with a team model, which already has a has_many relationship with the user model.
My Team Model
class Team < ActiveRecord::Base
validates :teamname, :teamcolor, presence: true
has_one :captain, :class_name => "User"
#, :through => :user
has_many :users
#after_save :set_default_captain
accepts_nested_attributes_for :users
accepts_nested_attributes_for :captain
end
My User Model
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save {self.email = email.downcase }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format:{with: VALID_EMAIL_REGEX},
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
has_one :profile, inverse_of: :user, dependent: :destroy
has_many :teammates, :class_name => "User", :foreign_key => "captain_id"
belongs_to :captain, :class => "User"
belongs_to :team
accepts_nested_attributes_for :team
end
I'm having the issue using #team.captain, as captain_id is in the User database table but it's retrieving the first user with the team_id equaling #team.id. Usinghas_one :captain, :through => :user` gives association error. Any advice is appreciated. Thanks.
Try to specify foreign_key for the association.
has_one :captain, :class_name => "User", :foreign_key => 'captain_id'

How to scope validation to specific model in polymorphic association.?

I have two model User and Investment and one polymorhic model Address
class User < ActiveRecord::Base
has_one :address, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :address
end
class Investment < ActiveRecord::Base
has_many :addresses, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :addresses, reject_if: lambda { |v| v['address'].blank? } && :address_blank, :allow_destroy => true
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
validates :address, presence: true
end
now validates :address, presence: true will applicable to both Investment as well as User
but i want it to applicable to only Investment not to User. so how do i do that.
Thanks.
in class Investment add
validates :address_id, presence: true
and remove bellow from class Address
validates :address, presence: true
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
validates :address, presence: true, if: :investment?
protected
def investment?
addressable_type == 'Investment'
end
end

Failing validations in join model when using has_many :through

My full code can be seen at https://github.com/andyw8/simpleform_examples
I have a join model ProductCategory with the following validations:
validates :product, presence: true
validates :category, presence: true
My Product model has the following associations:
has_many :product_categories
has_many :categories, through: :product_categories
When I try to create a new product with a category, the call to #product.save! in the controller fails with:
Validation failed: Product categories is invalid
When I remove the validations, everything works and the join models are saved correctly.
I'm using strong_parameters but I don't think that should be related to this issue.
This is a "racing condition" in the callback chain.
When you create a product it doesn't have any id before it is saved, therefore there is no product in the scope of ProductCategory.
Product.new(name: "modern times", category_ids:[1, 2]) #=> #<Product id: nil >
At that stage of validation (before saving), ProductCatgory cannot assign any id to it's foreign key product_id.
That's the reason you have association validations : so that the validation happens in the scope of the whole transaction
UPDATE: As said in the comment you still can't ensure presence of a product/category. There's many ways around depending on why you want do this (e.g direct access to ProductCategory through some form)
You can create a flag to have validates :product, presence: true, if: :direct_access?
or if you can only update them: validates :product, presence: true, on: "update"
create your product first (in the products_controller) and add the categories after
... But indeed these are all compromises or workarounds from the simple #product.create(params)
Specifying inverse_of on your joining models has been documented to fix this issue:
https://github.com/rails/rails/issues/6161#issuecomment-6330795
https://github.com/rails/rails/pull/7661#issuecomment-8614206
Simplified Example:
class Product < ActiveRecord::Base
has_many :product_categories, :inverse_of => :product
has_many :categories, through: :product_categories
end
class Category < ActiveRecord::Base
has_many :product_categories, inverse_of: :category
has_many :products, through: :product_categories
end
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
validates :product, presence: true
validates :category, presence: true
end
Product.new(:categories => [Category.new]).valid? # complains that the ProductCategory is invalid without inverse_of specified
Adapted from: https://github.com/rails/rails/issues/8269#issuecomment-12032536
Pretty sure you just need to define your relationships better. I still might have missed some, but hopefully you get the idea.
class Product < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
validates :name, presence: true
validates :description, presence: true
validates :color_scheme, presence: true
belongs_to :color_scheme
has_many :product_categories, inverse_of: :product
has_many :categories, through: :product_categories
end
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
validates_associated :product
validates_associated :category
# TODO work out why this causes ProductsController#create to fail
# validates :product, presence: true
# validates :category, presence: true
end
class Category < ActiveRecord::Base
has_many :product_categories, inverse_of: :category
has_many :products, through: :product_categories
end

Resources