Rails 4 + Postgresql - Joins table with 2 conditions - ruby-on-rails

I need to list Inputs that have Translations with language_id = 1 and dont have Translations with language_id = 2
My current scope is:
scope :language, -> (id){joins(:translations).where('translations.language_id = 1)}
Models:
class Input < ActiveRecord::Base
has_many :translations
has_many :languages, through: :translations
scope :language, -> (id) {joins(:translations).where('translations.language_id = 1')}
def translation_from_language(language)
self.translations.where(language: language).take
end
end
class Translation < ActiveRecord::Base
belongs_to :input
belongs_to :language
before_save :default_values
#Apenas para testar o scope de search
scope :search, -> (part) { where("LOWER(value) LIKE ?", "%#{part.downcase}%") }
# Se nao setar um input manualmente, irá criar um novo
def default_values
self.input ||= Input.create
end
end
class Language < ActiveRecord::Base
has_many :translations
has_many :inputs, through: :translations
end

So my solution is just create a query like
Input.where("id IN ( SELECT input_id
FROM translations
WHERE language_id = 1
)
AND id NOT IN (
SELECT input_id
FROM translations
WHERE language_id = 2
)")

Related

Get id, name from master table - Ruby

The association is has follows
Company has_many company_commodities
CompanyCommodity belongs to Company
CompanyCommodity belongs to Commodity
Consider that company1 has an entry in the company_commodities table.
Now in the decorator file, i need to get the commodity name and id of that record.
I have implemented as follows.
company1 = Company.find(1)
arr = co.company_commodities.map(&:commodity).pluck(:name, :id)
arr.map { |a| { name: a[0], id: a[1] } }
This produces the output as
[{:name=>"Pharmaceuticals", :id=>25},
{:name=>"Medical Devices", :id=>26}]
Is there a cleaner way to do this? 
class Company < ApplicationRecord
has_many :company_commodities
has_many :commodities, through: :company_commodities
end
class CompanyCommodity < ApplicationRecord
belongs_to :commodity
belongs_to :company
end
company = Company.find(1)
# this returns the object you can access by arr.first.id, arr.first.name
arr = company.commodities.select(:name, :id)
# if you want to access as hash, usage: arr.first['id'], arr.first['name']
arr = company.commodities.select(:name, :id).as_json
You can get the commodities association by using through, then you can use select to filter the attributes.
Change the associations as below
class Company
has_many :company_commodities
has_many :commodities, through: :company_commodities
end
class CompanyCommodity
belongs_to :company
belongs_to :commodity
end
And query the records as
company1 = Company.find(1)
arr = co.commodities.pluck(:name, :id)
arr.reduce([]) { |array, el| array << [[:name, :id], el].to_h}

How to write a :within scope for a polymorphic relationship?

I have a Location model, and other models have a belongs_to :locatable, polymorphic: true
So, something like a PlaceOfInterest might look like this:
class PlaceOfInterest < ApplicationRecord
belongs_to :user
has_one :location, as: :locatable
# how to do this part?
scope :within,
-> (latitude, longitude, radius_meters = 0) {
where(%{ST_Distance(lonlat, 'POINT(%f %f)') < %d} % [longitude, latitude, radius_meters]) # approx
}
end
How can I get the lonlat which exists on Location from PlaceOfInterest to write the :within scope?
I'm not sure I completely understand your model structure, but you should just be able to do a joins before the where. Note the joins(:location) and the column name locations.lonlat
class PlaceOfInterest < ApplicationRecord
belongs_to :user
has_one :location, as: :locatable
scope :within,
-> (latitude, longitude, radius_meters = 0) {
joins(:location).where(%{ST_Distance(locations.lonlat, 'POINT(%f %f)') < %d} % [longitude, latitude, radius_meters]) # approx
}
end

Rails - How to a validate model being altered in before filter from a different model?

Models - Purchaseorder, Purchaseorderadjustments, Productvariant, Location, Locationinventory
I'm storing inventory in Locationinventory which stores a location_id, productvariant_id, and quantity.
The situation arises when I want to create a purchaseorder. I'm using purchaseorderadjustments as a nested attribute to the purchaseorder. A purchaseorder has_many purchaseorderadjustments that store the productvariant_id and quantity.
I'm using before filters to create,update and destroy the related locationinventory records. Everything works well as it is now except that you can remove items from a location that doesn't have them available and the quantity just goes into the negative. I want to verify that the "From Location" has enough of the productvariant in stock to transfer to the "To Location".
Am I doing it wrong? thanks!
Rails 3.2.14
Purchaseorder.rb
class Purchaseorder < ActiveRecord::Base
attr_accessible :fromlocation_id, :status_id, :tolocation_id, :user_id, :purchaseorderadjustments_attributes
belongs_to :user
belongs_to :status
belongs_to :fromlocation, :class_name => "Location", :foreign_key => :fromlocation_id
belongs_to :tolocation, :class_name => "Location", :foreign_key => :tolocation_id
has_many :purchaseorderadjustments, :dependent => :destroy
accepts_nested_attributes_for :purchaseorderadjustments, allow_destroy: true
end
Purchaseorderadjustment.rb
class Purchaseorderadjustment < ActiveRecord::Base
attr_accessible :adjustmenttype_id, :productvariant_id, :purchaseorder_id, :quantity
belongs_to :purchaseorder
belongs_to :productvariant
belongs_to :adjustmenttype
validates_presence_of :quantity, :message => "You need a quantity for each product."
# On creation of a purchaseorderadjustment go ahead and create the record for locationinventory
before_create :create_locationinventory
def create_locationinventory
# Get some info before updating the locationinventory
if fromlocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.fromlocation_id, :productvariant_id => productvariant_id })
fromlocation_current_quantity = fromlocationinventory.quantity
end
if tolocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.tolocation_id, :productvariant_id => productvariant_id })
tolocation_current_quantity = tolocationinventory.quantity
end
# Create or update the from locationinventory
unless fromlocationinventory.nil?
fromlocationinventory.quantity = fromlocation_current_quantity - quantity
fromlocationinventory.save
else
new_fromlocationinventory = Locationinventory.new({ location_id: purchaseorder.fromlocation_id, productvariant_id: productvariant_id, quantity: 0 - quantity })
new_fromlocationinventory.save
end
# Create or update the to locationinventory
unless tolocationinventory.nil?
tolocationinventory.quantity = tolocation_current_quantity + quantity
tolocationinventory.save
else
new_tolocationinventory = Locationinventory.new({ location_id: purchaseorder.tolocation_id, productvariant_id: productvariant_id, quantity: quantity })
new_tolocationinventory.save
end
end
#On update of purchaseorderadjustment
before_update :update_locationinventory
def update_locationinventory
# Get some info before updating the locationinventory
fromlocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.fromlocation_id, :productvariant_id => productvariant_id })
tolocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.tolocation_id, :productvariant_id => productvariant_id })
fromlocation_current_quantity = fromlocationinventory.quantity
tolocation_current_quantity = tolocationinventory.quantity
fromlocationinventory.quantity = fromlocation_current_quantity - quantity + self.quantity_was
fromlocationinventory.save
tolocationinventory.quantity = tolocation_current_quantity + quantity - self.quantity_was
tolocationinventory.save
end
#On destroy of purchaseorderadjustment
before_destroy :destroy_locationinventory
def destroy_locationinventory
# Get some info before updating the locationinventory
fromlocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.fromlocation_id, :productvariant_id => productvariant_id })
tolocationinventory = Locationinventory.find(:first, conditions: { :location_id => purchaseorder.tolocation_id, :productvariant_id => productvariant_id })
fromlocation_current_quantity = fromlocationinventory.quantity
tolocation_current_quantity = tolocationinventory.quantity
fromlocationinventory.quantity = fromlocation_current_quantity + quantity
fromlocationinventory.save
tolocationinventory.quantity = tolocation_current_quantity - quantity
tolocationinventory.save
end
end
productvariant.rb
class Productvariant < ActiveRecord::Base
attr_accessible :barcode, :compare_at_price, :fulfillment_service, :grams,
:inventory_management, :inventory_policy, :inventory_quantity,
:option1, :option2, :option3, :position, :price, :product_id,
:requires_shipping, :shopify_id, :sku, :taxable, :title, :shopify_product_id, :product_title
belongs_to :product, primary_key: "shopify_id", foreign_key: "shopify_product_id"
has_many :purchaseorderadjustments
has_many :locationinventories
def product_plus_variant
"#{self.product.title} - #{self.title}"
end
end
locationinventory.rb
class Locationinventory < ActiveRecord::Base
attr_accessible :location_id, :productvariant_id, :quantity
belongs_to :productvariant
belongs_to :location
end
I'll write this answer because I feel you've provided so much code, you might have scared some answerers away!
Our experience is as follows:
Nested
You can validate nested models in several different ways
Your question is related to passing data in a accepts_nested_attributes_for - you can validate this directly:
#app/models/purchase.rb
Class Purchase < ActiveRecord::Base
has_many :purchase_items
accepts_nested_attributes_for :purchase_items
end
#app/models/purchase_item.rb
Class PurchaseItem < ActiveRecord::Base
belongs_to :purchase
validates :name,
presence: { message: "Your Purchase Needs Items!" } #Returns to initial form with this error
end
Standard
If you want to conditionally validate based on another model, you'll have to use inverse_of: to keep the object available throughout the data transaction:
#app/models/purchase.rb
Class Purchase < ActiveRecord::Base
has_many :purchase_items, inverse_of: :purchase
accepts_nested_attributes_for :purchase_items
end
#app/models/purchase_item.rb
Class PurchaseItem < ActiveRecord::Base
belongs_to :purchase, inverse_of: :purchase_items
validates :name,
presence: { message: "Your Purchase Needs Items!" },
if: :paid_with_card?
private
def paid_with_card?
self.purchase.payment_method == "card"
end
end

Rails `has_many through` with distinct tags

I have the following active record classes. I am trying to make a tagging similar to twitter without using gem. Can save_tags and create_tags be optimised further?
class Opinion < ActiveRecord::Base
belongs_to :user
has_many :taggings
has_many :tags, through: :taggings
after_create :save_tags
def self.tagged_with(name)
Tag.find_by!(name: name).opinions
end
private
def save_tags
tags = create_tags
tags.each do |tag|
t = tag.downcase
oldTag = Tag.find_by(name: t)
if oldTag
tagging = Tagging.new(opinion: self, tag: oldTag)
tagging.save
else
self.tags.create(name: t)
end
end
end
def create_tags
tags = self.about.scan(/(\#{1}[a-zA-Z1-9]*\b)/).flatten
if tags
tags.uniq.reject! { |tag| tag.length < 2 }
end
return tags
end
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :opinions, through: :taggings
validates_uniqueness_of :name
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :opinion
end
This code invokes tags.count DB queries:
tags.each do |tag|
t = tag.downcase
oldTag = Tag.find_by(name: t)
...
end
You could preload them:
old_tags = Tag.where(name: tags)

Rails filtering resource one to many through a joint table

I have the following resources:
- restaurant
- category
- item
- check item
Relationship:
class Restaurant < ActiveRecord::Base
has_many :items
has_many :categories
has_many :check_items
class Category < ActiveRecord::Base
belongs_to :restaurant
has_many :items
class Item < ActiveRecord::Base
belongs_to :restaurant
belongs_to :category
class CheckItem < ActiveRecord::Base
belongs_to :item
I need to filter all the check_items of a restaurant where category.uuid = '123123'
so I have my #restaurant.check_items. How do I join these together to basically implement this sql query:
SELECT * from checkitem
INNER JOIN item ON(checkitem.item_id = item.id)
INNER JOIN category ON(category.id = item.category_id)
WHERE category.restaurant_id = 1 AND category.uuid = '123123'
LIMIT 20;
I've tried with scope:
#already have my restaurant resource here with id 1
#restaurant.check_items.by_item_category params[:category_uuid]
And in my models I would have:
class CheckItem < ActiveRecord::Base
...
scope :by_item_category, -> value { joins(:item).by_category value }
class Item < ActiveRecord::Base
...
scope :by_category, -> value { joins(:category).where('%s.uuid = ?' % Category.table_name, value)}
Buut this doesn't seem to work
This appears to be the only way I found this to be working if anyone is interested.
CheckItem.joins(:item => {:category => :restaurant}).where('category.uuid=? and restaurant.id=?', 123123, 1)

Resources