Rails has_many belongs_to unique - ruby-on-rails-3

I have 2 models:
Sale:
class Sale < ActiveRecord::Base
attr_accessible :sale_item_ids, :subtotal, :tax_charge, :total
has_many :sale_items
end
SaleItem:
class SaleItem < ActiveRecord::Base
attr_accessible :discount, :price, :product, :quantity, :sale_id, :sum, :code
belongs_to :sale
end
First i create an empty sale and then i start adding sale_items to that sale.
How can i validate the uniqueness of the sale_item_ids column? i mean, how can i make it so the same sale_item cannot be created twice with the same sale_id?
i have tried:
has_many :sale_items, uniq: true
but didnt work, i have also tried:
validates_uniqueness_of :sale_item_ids
but no success.

I believe you have to scope it. http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Try this:
validates_uniqueness_of :sale_items_ids, :scope => :sale_id

I was wrong since the beginning, sale_item_ids will always be different because its a new record in the database, so it will always be unique.
So what i have done is validate some other column.
class SaleItem < ActiveRecord::Base
validate :check_presence, :on => :create
attr_accessible :code, :discount, :price, :product, :quantity, :sum, :sale_id
belongs_to :sale
def check_presence
# Get the sale where the item sale will be saved
sale = Sale.find(self.sale_id)
# Compare the code column
sale.sale_items.each do |s|
if s.code == self.code
errors.add(:base, "Item already in the sale form.")
end
end
end
end

Related

Sum returning null

I am having a problem with a Ruby SQL look up function: it always returns a null.
Here is the terminal output:
SELECT COUNT("point_store_items"."point_cost")
FROM "point_store_items"
INNER JOIN "point_store_items_point_store_orders"
ON "point_store_items"."id" = "point_store_items_point_store_orders"."point_store_item_id"
WHERE "point_store_items_point_store_orders"."point_store_order_id" IS NULL
The code causing this problem is:
def self.pointcost
self.count(:point_cost)
end
If this code is changed to say
def self.pointcost
40
end
it has no issues except the prices always listed is 40, not what should come from the database.
Relevant Model Code:
class PointStoreItem < ActiveRecord::Base
attr_accessible :product_id, :point_cost
has_and_belongs_to_many :orders, :class_name => "PointStoreOrder"
has_many :users, :through => :orders
belongs_to :product
def to_param
"#{id}-#{product.name.parameterize}"
end
def self.pointcost
self.count(:point_cost)
end
end
The model that calls for the pointcost
class PointStoreOrder < ActiveRecord::Base
include Workflow
attr_accessible :point_transaction_id, :user_id, :workflow_state , :shipping_provider, :tracking_number, :items
has_and_belongs_to_many :items, :class_name => "PointStoreItem"
belongs_to :user
belongs_to :point_transaction
accepts_nested_attributes_for :items
workflow do
state :cart do
event :purchase, :transitions_to => :purchased
event :cancel, :transitions_to => :cancelled
end
state :purchased do
event :ship, :transitions_to => :shipped
end
state :shipped
state :cancelled
end
def purchase
unless self.user.point_balance >= total
halt "Insufficient point balance"
else
self.point_transaction = user.point_transactions.new
self.point_transaction.point_count = total
self.point_transaction.save!
self.save!
end
end
def total
self.items.pointcost
end
def on_cancelled_entry(old_state, event, *args)
end
end

Uniqueness validation with a has_many relationship

My model structure is as follows:
class Client < ActiveRecord::Base
has_many :charts
end
class Chart < ActiveRecord::Base
belongs_to :client
has_many :chart_data
end
class ChartDatum < ActiveRecord::Base
belongs_to :chart
end
ChartDatum has an attribute called 'name' which needs to be unique for each client.
I tried using "validates_uniqueness_of :name, :scope => [:chart_id]" but this helped me getting a unique key for a particular chart but not for all charts for a particular client. I am looking for something like "validates_uniqueness_of :name, :scope => [:client_id]" but obviously with the current structure it will not work out.
Could someone please help me?
Since you need unique name for chart_data for each client, you can try writing a custom validation for name something like this :
class ChartDatum < ActiveRecord::Base
belongs_to :chart
validates :name, :uniqueness => true, unless => :unique_for_client?
def unique_for_client?
client = self.chart.client
client.charts.chart_data.pluck(:name).include?(self.name)
end
end

Added two "belongs_to" to a Comment model but unable to get one of the associations

I am currently building very simple Comment system on Rails. The primary models are User, Albumpost, and Comment. Users can post Albumposts. For each Albumpost, Users can add Comments to the Albumpost. As a result, a Comment belongs to a User and belongs to an Albumpost.
The problem I'm having is that even with the proper associations in my models (see below), I can't get
#comment.user.name
when I'm trying to render the comments in the albumpost 'show' page (/views/albumposts/show.html.erb). When I go to the page, I can't get #comment.user.name (doesn't understand the association) and get a
"undefined method `name' for nil:NilClass"
Oddly I can get
#comment.albumpost.content
I've double-checked my models and also added the proper foreign keys to the models. Am I doing something wrong in the controllers?
Here are my models:
class Comment < ActiveRecord::Base
attr_accessible :body, :albumpost_id, :user_id
belongs_to :albumpost
belongs_to :user
end
class Albumpost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :comments, dependent: :destroy
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :albumposts, dependent: :destroy
has_many :comments, dependent: :destroy
end
Here are the relevant parts of my Albumpost and Comments controllers:
class AlbumpostsController < ApplicationController
def show
#albumpost = Albumpost.find(params[:id])
#comments = #albumpost.comments
#comment = Comment.new
#comment.albumpost_id = #albumpost.id
#comment.user_id = current_user.id
end
end
class CommentsController < ApplicationController
def create
albumpost_id = params[:comment].delete(:albumpost_id)
#comment = Comment.new(params[:comment])
#comment.albumpost_id = albumpost_id
#comment.user_id = current_user.id
#comment.save
redirect_to albumpost_path(#comment.albumpost)
end
end
I think you should prefer setting objects to relations instead of setting their ids. For example, you should do this:
#comment.user = current_user
instead of
#comment.user_id = current_user.id
ActiveRecord will take care of setting corresponding *_id fields. I'm not sure how it handles the reverse. (it should autoload though, if I understand correctly)

How to store the 'total' count of this polymorphic model in its associated models (Rails)?

I have a model called Vote that has a polymorphic association with other two models: Microposts and Comments.
Here is their association:
micropost.rb:
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments, :dependent => :destroy
has_many :votes, :as => :votable, :dependent => :destroy
end
comment.rb:
class Comment < ActiveRecord::Base
belongs_to :micropost, :counter_cache => true
belongs_to :user
has_many :votes, :as => :votable, :dependent => :destroy
end
vote.rb:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
end
The Vote model has a column called polarity (for voting up +1 and voting down -1).
I would like to add a total (the sum of the polarity of all the votes of a post or comment) to each instances of the Post and Comment model.
What's the best way of accomplishing this?
Example:
Post (:id = 2)
Vote (:polarity = 1) Vote (:polarity = -1) Vote (:polarity = 2)
Post with ID 2 has a :total of 2 votes
You can create an instance method in Post and Comment models like:
def total
self.votes.map {|v| v.polarity }.sum
end
Then put #post.total or #comment.total
Or you can create a helper which can do the same:
def total object
object.votes.map {|v| v.polarity }.sum
end
Then put <%= total #post %> or <%= total #comment %>

Avoiding individual database calls for count

My models look like this:
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
end
class Actor < ActiveRecord::Base
attr_accessible :name
has_many :movies, :through => :jobs
has_many :jobs, :dependent => :destroy
end
class Job < ActiveRecord::Base
attr_accessible :movie_id, :actor_id
belongs_to :movie
belongs_to :actor
end
When I'm displaying my index of Actors, I'd like to show the number of movies each actor has starred in. I can do this with #actor.movies.count, however this generates an SQL query for each actor. With, say, 30 actors, this will result in 30 extra queries in addition to the initial.
Is there any way to include the count of movies each actor has participated in, in the initial Actor.all call? And thereby getting things done with only one call. Extra bonus if this was sorted by said count.
Update:
All answers provided has been helpful, and though it turned into some dirt-slinging-contest at some point, it worked out well. I did a mish-mash of all your suggestions. I added a movies_counter column to my Actor model. In my Job model I added belongs_to :actor, :counter_cache => :movies_counter. This works brilliantly, and is automatically updated when i create or destroy a movie, without me adding any further code.
As #Sam noticed, you should add new column to actors table movies_counter
rails g migration add_movies_counter_to_actor movies_counter:integer
Now you can edit your migration
class AddMoviesCounterToActor < ActiveRecord::Migration
def self.up
add_column :actors, :movies_counter, :integer, :default => 0
Actor.reset_column_information
Actor.all.each do |a|
a.update_attribute :movies_counter, a.movies.count
end
end
def self.down
remove_column :actors, :movies_counter
end
end
And run it
rake db:migrate
Then you should add two callbacks: after_save and after_destroy
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
after_save :update_movie_counter
after_destroy :update_movie_counter
private
def update_movie_counter
self.actors.each do |actor|
actor.update_attribute(:movie_count, actor.movies.count)
end
end
end
Then you can call some_actor.movies_counter
Add a column to your Actor table called 'movie_count'. Then add a call back in your Actor model that updates that column.
class Movie < ActiveRecord::Base
has_many :actors, :through => :jobs
before_save :update_movie_count
def update_movie_count
self.actor.update_attribute(:movie_count, self.movies.size)
end
end
That way your just have an integer that gets updated instead of calling all records.