I don't know what is the proper manner to ensure that there is only one record with the same couple of foreign keys. Let's assume a class named LineItem, which belongs to an cart and to item. Something like:
class LineItem < ActiveRecord::Base
belongs_to :cart
belongs_to :item
The point is: I want to make unique the combination of this two "attributes". At this time, my approach was using a after_save callback but I realized thats pretty poor. How do you resolve this? I was thinking in a PORO class (something like LineItemSaver) but I'm not completely convinced.
Thanks!
If I understood your question correctly,you want the scope option of the validates_uniqueness_of. If so, this should work.
In your LineItem model:
class LineItem < ActiveRecord::Base
belongs_to :cart
belongs_to :item
validates_uniqueness_of :cart_id, scope: :item_id
end
And also, you should be generating a migration to add this:
add_index :line_items, [ :cart_id, :item_id ], :unique => true
More Info here.
Can't you just do a uniqueness validation?
validates :cart_id, uniqueness: {scope: :item_id}
Try using a validate method to ensure that item_id is unique scoped to :cart_id
Something like:
validates :item_id, uniqueness: { scope: :cart_id }
Or if you want to fix the situation and simply increment the quantity you could also:
validate :ensure_line_item_uniqueness
def ensure_line_item_uniqueness
... whatever fixes the situation here ...
end
Related
I'm creating a polling system. I would like all options to be made unique, but only within their respective Poll. I'm using a proc to validate that they are not blank:
class Poll < ActiveRecord::Base
has_many :options
accepts_nested_attributes_for :options, reject_if: proc { |attributes| attributes['option'].blank? }
end
But I'm not sure how to validate their uniqueness. I tried doing it within the Option model but it's not rejecting duplicate options created through the Poll form's f.fields_for:
class Option < ActiveRecord::Base
belongs_to :poll
validates_uniqueness_of :option, scope: :poll_id
end
Is it possible to do it with proc?
Apply uniqueness validation upon option attributes like validates_uniqueness_of :title, :other, scope: :poll_id
You can pass any condition to that proc. So you could do query for that column and see whether any results are returned and reject if it they are.
So I have this big headache trying to solve a bug that only happens sometimes... I have the following model for allowing a user to like something:
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
validates :user, uniqueness: {scope: :likeable}
end
This is a set up to allow a user to like multiple other models and has a validation that prevents a user to like the same thing multiple times.
The thing I discovered after debugging is that the SELECT query run by rails seems to check only for the uniqueness of likeable_id (and not likeable_type):
SELECT 1 AS one FROM "likes" WHERE ("likes"."user_id" = 1 AND "likes"."likeable_id" = 4) LIMIT 1
Logically, when a user had already liked a comment with id is 4, he couldn't like any other thing with the same ID.
Any ideas on how to solve this?
According to the docs, you can define uniqueness validation on both type and id:
http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of#893-Does-not-work-with-polymorphic-relations
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
validates :user, uniqueness: {scope: [:likeable_id, :likeable_type]}
end
In my rails projects I have a lot of association tables. And I have some validations. Nothing really difficult, and it works almost every times.
But from time to time (like tonight), I have to switch from
validates_presence_of :project_id
validates_presence_of :tag_id
validates_uniqueness_of :project_id, :scope => [:tag_id]
to
validates_presence_of :project
validates_presence_of :tag
validates_uniqueness_of :project, :scope => [:tag]
Do you know the difference ? Do you if one is better than the other ?
From the Rails Guides: http://guides.rubyonrails.org/active_record_validations.html#presence
2.9 presence This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either
nil or a blank string, that is, a string that is either empty or
consists of whitespace.
class Person < ActiveRecord::Base
validates :name, :login, :email, presence: true
end
If you want to be sure that an association is present, you'll need to
test whether the associated object itself is present, and not the
foreign key used to map the association.
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
So, you should use the second example you gave, which tests if the associated object itself is present, and not the first example, which only tests if the foreign key used to map the association is present.
I've got a model in which a very small percentage of the objects will have a rather large descriptive text. Trying to keep my database somewhat normalized, I wanted to extract this descriptive text to a separate model, but I'm having trouble creating a sensible workflow in ActiveAdmin.
My models look like this:
class Person < ActiveRecord::Base
has_one :long_description
end
class LongDescription < ActiveRecord::Base
attr_accessible :text, :person_id
belongs_to :person
validates :text, presence: true
end
Currently I've created a form for editing the Person model, looking somewhat like this:
form do |f|
...
f.inputs :for => [
:long_description,
f.object.long_description || LongDescription.new
] do |ld_f|
ld_f.input :text
end
f.actions
end
This works for adding/editing the LongDescription object, but I still have an issue: I'd like to avoid validating/creating the LongDescription object if no text is entered.
Anyone with better ActiveAdmin skills than me know how to achieve this?
Are you using accepts_nested_attributes_for :long_description? If so, you can add a :reject_if option:
class Person < ActiveRecord::Base
has_one :long_description
accepts_nested_attributes_for :long_description, reject_if: proc { |attrs| attrs['text'].blank? }
end
Note that this is a Rails thing, not an ActiveAdmin thing, and so it will simply skip assignment and update/create of the nested object if that attribute is missing.
More here: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I am trying to order by a field in a related model in Rails. All of the solutions I have researched have not addressed if the related model is filtered by another parameter?
Item model
class Item < ActiveRecord::Base
has_many :priorities
Related Model:
class Priority < ActiveRecord::Base
belongs_to :item
validates :item_id, presence: true
validates :company_id, presence: true
validates :position, presence: true
end
I am retrieving Items using a where clause:
#items = Item.where('company_id = ? and approved = ?', #company.id, true).all
I need to order by the 'Position' column in the related table. The trouble has been that in the Priority model, an item could be listed for multiple companies. So the positions are dependent on which company_id they have. When I display the items, it is for one company, ordered by position within the company. What is the proper way to accomplish this? Any help is appreciated.
PS - I am aware of acts_as_list however found it did not quite suit my setup here, so I am manually handling saving the sorting while still using jquery ui sortable.
You could use the includes method to include the build association then order by it. You just make sure you disambiguate the field you are ordering on and there are some things you should read up on here on eager loading. So it could be something like:
#items = Item.includes(:priorities).where('company_id = ? and approved = ?', #company.id, true).order("priorities.position ASC")
class Item < ActiveRecord::Base
has_many :priorities
belongs_to :company
def self.approved
where(approved: true)
end
end
class Priority < ActiveRecord::Base
belongs_to :item
end
class Company < ActiveRecord::Base
has_many :items
end
#company = Company.find(params[:company_id])
#items = #company.items.joins(:priorities).approved.order(priorities: :position)
If I've understood your question, that's how I'd do it. It doesn't really need much explanation but lemme know if you're not sure.
If you wanted to push more of it into the model, if it's a common requirement, you could scope the order:
class Item < ActiveRecord::Base
has_many :priorities
belongs_to :company
def self.approved
where(approved: true)
end
def self.order_by_priority_position
joins(:priorities).order(priorities: :position)
end
end
and just use: #company.items.approved.order_by_priority_position