Ruby - Query another table in a model definition - ruby-on-rails

I want to query another table in a model definition. For instance, I have a table called miq_user_roles and I want to query and retrievesettings column value.
I tried adding the following
has_many :miq_user_roles
but when I try the where condition where(:settings => nil)
I get the error service_template doesn't have settings column. How can I query miq_user_roles for settings instead of service_template
service_template has a column called miq_group_id and its the id of miq_user_rolestable.
Following is the actual model definition where I want to include miq_user_roles table.
class ServiceTemplate < ApplicationRecord
include SupportsFeatureMixin
DEFAULT_PROCESS_DELAY_BETWEEN_GROUPS = 120
GENERIC_ITEM_SUBTYPES = {
"custom" => N_("Custom"),
"vm" => N_("Virtual Machine"),
"playbook" => N_("Playbook"),
"hosted_database" => N_("Hosted Database"),
"load_balancer" => N_("Load Balancer"),
"storage" => N_("Storage")
}.freeze
SERVICE_TYPE_ATOMIC = 'atomic'.freeze
SERVICE_TYPE_COMPOSITE = 'composite'.freeze
RESOURCE_ACTION_UPDATE_ATTRS = [:dialog,
:dialog_id,
:fqname,
:configuration_template,
:configuration_template_id,
:configuration_template_type].freeze
include CustomActionsMixin
include ServiceMixin
include OwnershipMixin
include NewWithTypeStiMixin
include TenancyMixin
include ArchivedMixin
include CiFeatureMixin
include_concern 'Filter'
include_concern 'Copy'
validates :name, :presence => true
belongs_to :tenant
has_many :service_templates, :through => :service_resources, :source => :resource, :source_type => 'ServiceTemplate'
has_many :services
has_many :service_template_tenants, :dependent => :destroy
has_many :additional_tenants, :through => :service_template_tenants, :source => :tenant, :dependent => :destroy
has_one :picture, :dependent => :destroy, :as => :resource, :autosave => true
belongs_to :service_template_catalog
belongs_to :zone
belongs_to :currency, :inverse_of => false
has_many :dialogs, -> { distinct }, :through => :resource_actions
has_many :miq_schedules, :as => :resource, :dependent => :destroy
has_many :miq_requests, :as => :source, :dependent => :nullify
has_many :active_requests, -> { where(:request_state => MiqRequest::ACTIVE_STATES) }, :as => :source, :class_name => "MiqRequest"
virtual_column :type_display, :type => :string
virtual_column :template_valid, :type => :boolean
virtual_column :template_valid_error_message, :type => :string
virtual_column :archived, :type => :boolean
virtual_column :active, :type => :boolean
default_value_for :internal, false
default_value_for :service_type, SERVICE_TYPE_ATOMIC
default_value_for(:generic_subtype) { |st| 'custom' if st.prov_type == 'generic' }
virtual_has_one :config_info, :class_name => "Hash"
scope :with_service_template_catalog_id, ->(cat_id) { where(:service_template_catalog_id => cat_id) }
scope :without_service_template_catalog_id, -> { where(:service_template_catalog_id => nil) }
scope :with_existent_service_template_catalog_id, -> { where.not(:service_template_catalog_id => nil) }
scope :displayed, -> { where(:display => true) }
scope :public_service_templates, -> { where(:display => true) }
supports :order do
unsupported_reason_add(:order, 'Service template does not belong to a service catalog') unless service_template_catalog
unsupported_reason_add(:order, 'Service template is not configured to be displayed') unless display
end
alias orderable? supports_order?
alias validate_order supports_order?
def self.with_tenant(tenant_id)
tenant = Tenant.find(tenant_id)
where(:tenant_id => tenant.ancestor_ids + [tenant_id])\
end

Add the table name:
where("miq_user_roles.settings" => nil)
Arel is nice, but you still have to use bits of SQL to get over issues like this.

You will have to use joins, to query on the associated table.
ServiceTemplate.joins(:miq_user_roles).where(miq_user_roles: { settings: nil })

Related

Can't join 'ServiceTemplate' to association named 'miq_user_roles'; perhaps you misspelled it?

Why can't I join miq_user_roles with ServiceTemplate? ServiceTemplate has a column miq_group_id which is the id of miq_user_roles.
scope :public_service_templates, -> { joins(:miq_user_roles).where(miq_user_roles: { settings: nil }) }
When I try the above code I get the following error:
Can't join 'ServiceTemplate' to association named 'miq_user_roles'; perhaps you misspelled it?
Following is the actual model definition where I want to include miq_user_roles table.
class ServiceTemplate < ApplicationRecord
include SupportsFeatureMixin
DEFAULT_PROCESS_DELAY_BETWEEN_GROUPS = 120
GENERIC_ITEM_SUBTYPES = {
"custom" => N_("Custom"),
"vm" => N_("Virtual Machine"),
"playbook" => N_("Playbook"),
"hosted_database" => N_("Hosted Database"),
"load_balancer" => N_("Load Balancer"),
"storage" => N_("Storage")
}.freeze
SERVICE_TYPE_ATOMIC = 'atomic'.freeze
SERVICE_TYPE_COMPOSITE = 'composite'.freeze
RESOURCE_ACTION_UPDATE_ATTRS = [:dialog,
:dialog_id,
:fqname,
:configuration_template,
:configuration_template_id,
:configuration_template_type].freeze
include CustomActionsMixin
include ServiceMixin
include OwnershipMixin
include NewWithTypeStiMixin
include TenancyMixin
include ArchivedMixin
include CiFeatureMixin
include_concern 'Filter'
include_concern 'Copy'
validates :name, :presence => true
belongs_to :tenant
has_many :service_templates, :through => :service_resources, :source => :resource, :source_type => 'ServiceTemplate'
has_many :services
has_many :service_template_tenants, :dependent => :destroy
has_many :additional_tenants, :through => :service_template_tenants, :source => :tenant, :dependent => :destroy
has_one :picture, :dependent => :destroy, :as => :resource, :autosave => true
belongs_to :service_template_catalog
belongs_to :zone
belongs_to :currency, :inverse_of => false
has_many :dialogs, -> { distinct }, :through => :resource_actions
has_many :miq_schedules, :as => :resource, :dependent => :destroy
has_many :miq_requests, :as => :source, :dependent => :nullify
has_many :active_requests, -> { where(:request_state => MiqRequest::ACTIVE_STATES) }, :as => :source, :class_name => "MiqRequest"
virtual_column :type_display, :type => :string
virtual_column :template_valid, :type => :boolean
virtual_column :template_valid_error_message, :type => :string
virtual_column :archived, :type => :boolean
virtual_column :active, :type => :boolean
default_value_for :internal, false
default_value_for :service_type, SERVICE_TYPE_ATOMIC
default_value_for(:generic_subtype) { |st| 'custom' if st.prov_type == 'generic' }
virtual_has_one :config_info, :class_name => "Hash"
scope :with_service_template_catalog_id, ->(cat_id) { where(:service_template_catalog_id => cat_id) }
scope :without_service_template_catalog_id, -> { where(:service_template_catalog_id => nil) }
scope :with_existent_service_template_catalog_id, -> { where.not(:service_template_catalog_id => nil) }
scope :displayed, -> { where(:display => true) }
scope :public_service_templates, -> { joins(:miq_user_roles).where(miq_user_roles: { settings: nil }) }
supports :order do
unsupported_reason_add(:order, 'Service template does not belong to a service catalog') unless service_template_catalog
unsupported_reason_add(:order, 'Service template is not configured to be displayed') unless display
end
alias orderable? supports_order?
alias validate_order supports_order?
def self.with_tenant(tenant_id)
tenant = Tenant.find(tenant_id)
where(:tenant_id => tenant.ancestor_ids + [tenant_id])\
end

API: Update model through an other related model

I'm trying make it possible to update a a LineItem trough a CreditNote. It's for an API, so I'm trying to update that trough a JSON.
My relational model is:
class TestCreditNote < ActiveRecord::Base
self.table_name = :credit_notes
has_many :line_items, :class_name => TestLineItem, :foreign_key => :artef_id
accepts_nested_attributes_for :line_items
end
class TestLineItem < ActiveRecord::Base
self.table_name = :line_items
attr_accessible :description
belongs_to :credit_note, :class_name => TestCreditNote, :foreign_key => :artef_id
end
When executing this test:
it "should update the sales line item record" do
put "api/v1/credit_notes/#{#credit_note.id}", { :test_credit_note => { :line_items => [{ :description => 'PEPITO'}] }}, http_headers
data = JSON.parse(response.body, :symbolize_names => true)
TestCreditNote.find(#sales_credit_note.id).line_item.description.should == 'PEPITO'
end
It fails because of:
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: line_items
I've add the attr_accesible :line_items_attributes
class TestCreditNote < ActiveRecord::Base
self.table_name = :credit_notes
has_many :line_items, :class_name => TestLineItem, :foreign_key => :artef_id
accepts_nested_attributes_for :line_items
attr_accessible :line_items_attributes
end
And the same in the test
it "should update the sales line item record" do
put "api/v1/credit_notes/#{#credit_note.id}", { :test_credit_note => { :line_items_attributes => [{:id => 1, :description => 'PEPITO'}] }}, http_headers
data = JSON.parse(response.body, :symbolize_names => true)
TestCreditNote.find(#sales_credit_note.id).line_item.description.should == 'PEPITO'
end

Rails paperclip unique image

Hi im looking forward to fix this unique image name issue: when a user upload an image (every user have it own folder), and the image exists already,, im getting thie message.
Errno::EACCES in PlayerStepsController#update
Permission denied - /srv/www/myfootballproject.com/mfp/public/assets/people/14/original/chepo.jpg
Rails.root: /srv/www/myfootballproject.com/mfp
Application Trace | Framework Trace | Full Trace
app/controllers/player_steps_controller.rb:30:in `update'
Request
I look into stackoverflow, and first i read about setting the userid for folders this minimize the probs of same name, also i try to randomize, but this is not working at all, i get no errors neither a randomize name for image..
here is the part of the model doing this.
has_attached_file :avatar, :styles => { :profile => "300x300", :thumb => "100x100#"},
:url => "/assets/people/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/people/:id/:style/:basename.:extension"
before_create :randomize_file_name
private
def randomize_file_name
extension = File.extname(image_file_name).downcase
self.image.instance_write(:file_name, "#{ActiveSupport::SecureRandom.hex(16)}#{extension}")
end
here is the model (complete)
class Player < ActiveRecord::Base
belongs_to :user
has_many :clubs
has_many :links
has_many :references
has_many :achievements
has_many :citizens
has_and_belongs_to_many :languages
has_and_belongs_to_many :selections
accepts_nested_attributes_for :clubs, :allow_destroy => true, :reject_if => proc { |attributes| attributes['name'].blank? }
accepts_nested_attributes_for :links, :allow_destroy => true, :reject_if => proc { |attributes| attributes['url'].blank? }
accepts_nested_attributes_for :references, :allow_destroy => true, :reject_if => proc { |attributes| attributes['name'].blank? }
accepts_nested_attributes_for :achievements, :allow_destroy => true, :reject_if => proc { |attributes| attributes['name'].blank? }
accepts_nested_attributes_for :citizens, :allow_destroy => true, :reject_if => proc { }
attr_accessible :name,
:lastname,
:birthday,
:height,
:height_measure,
:weight,
:weight_measure,
:inches,
:city,
:birthplace,
:other_languages,
:cp,
:phone,
:cellphone,
:web_page,
:game_status,
:club,
:actual_club,
:actual_country_club,
:actual_division_club,
:actual_contract_expiration_club,
:last_club,
:last_country_club,
:last_division_club,
:last_contract_expiration_club,
:position,
:alternative_position,
:dominant_leg,
#normal player
:short_passes,
:long_passes,
:shots_half_distance,
:shots_long_distance,
:ball_habilities,
:offensive_capability,
:ball_driving,
:defense_capability,
:dribbling,
:velocity,
:vision_field,
:movements_wothout_ball,
:recovery_ball,
:head_ball,
:lidership,
:teamwork,
#goalkeeper
:air_game,
:clearance_technique, #técnica de despeje
:ball_keep, #atajes
:flexibility, #flexibilidad
:penalty_keep, #atajar penales
:achique,
:defense_communication,
:foot_game,
:velocity_reaction, #reflejos
:area_domination,
:goalkeep_teamwork,
:goalkeep_lidership,
:strenghts,
:weaknesses,
:aditional_information,
:active,
:clubs_attributes,
:links_attributes,
:references_attributes,
:achievements_attributes,
:citizens_attributes,
:avatar_file_name,
:avatar_content_type,
:avatar_file_size,
:avatar,
:language_ids,
:selection_ids
POSITIONS = %w{
goalkeeper
defense
medium
offensive
}
LEG = %w{
left
right
both
}
# altura
HEIGHT = (1..200).to_a
INCH = (1..11).to_a
# peso
WEIGHT = (1..300).to_a
HEIGHT_MEASURE = %w{
cms
pies
}
WEIGHT_MEASURE = %w{
kgs
lbs
}
has_attached_file :avatar, :styles => { :profile => "300x300", :thumb => "100x100#"},
:url => "/assets/people/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/people/:id/:style/:basename.:extension"
before_create :randomize_file_name
private
def randomize_file_name
extension = File.extname(image_file_name).downcase
self.image.instance_write(:file_name, "#{ActiveSupport::SecureRandom.hex(16)}#{extension}")
end
validates_attachment_size :avatar, :less_than => 2.megabytes # Solo aceptar imágenes menores a 2 Mb.
validates_attachment_content_type :avatar, :content_type => ['image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif']
def defeated?
t = Time.now - created_at
mm, ss = t.divmod(60)
hh, mm = mm.divmod(60)
dd, hh = hh.divmod(24)
dd > 180 ? true : false
end
end
Everything is working perfect, i can upload images (As long as they do not exist in server)
Thanks

Rails 3 error: Object doesn't support #inspect on includes (left outer join)

I'm using Rails 3.
I have 3 models:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
end
class User < ActiveRecord::Base
has_many :wishes, :class_name => "Wish", :conditions => { 'wishes.wished' => true }
has_many :wished_deals, :through => :wishes, :source => :deal
end
class Wish < ActiveRecord::Base
belongs_to :user
belongs_to :deal
end
And i'm trying to create the following scope in the Deal model:
scope :not_wished_by_user, lambda { |user| includes(:wishes).where('wishes.wished' != true, 'wishes.user_id' => user) }
What i want is all the Deals, except those that are marked as 'wished' by the given user in the block. But whenever i do that includes, i get the following error:
ruby-1.9.2-head > u = User.all.first
ruby-1.9.2-head > Deal.not_wished_by_user(u)
(Object doesn't support #inspect)
=>
Also, placing it in a function doesn't work. Any idea what this could be?
Thanks!
EDIT: These are Wishes table migration
class CreateWish < ActiveRecord::Migration
def self.up
create_table :wishes do |t|
t.integer :deal_id
t.integer :user_id
t.boolean :wished, :default => true
t.boolean :collected, :default => false
t.datetime :collected_date
t.timestamps
end
add_index :wishes, [:deal_id, :user_id], :uniq => true
end
end
See Update below vv
Old answer
You are not using any Deal attributes for selects so try to move code into Wish class:
class Wish < ActiveRecord::Base
belongs_to :user
belongs_to :deal
scope :'wished?', lambda{ |f| where('wished = ?', f) }
scope :not_wished_by_user, lambda{|user| wished?(false).where('user_id = ?', user)}
end
Usage exmple and output:
ruby-1.9.2-p180 :023 > Wish.not_wished_by_user(User.first).to_sql
=> "SELECT \"wishes\".* FROM \"wishes\" WHERE (wished = 't') AND (user_id = 1)"
Is this correct result for you?
PS:
In the Deal you can leave proxy-method like:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
def self.not_wished_by_user(user)
Wish.not_wished_by_user(user)
end
end
Update1 (subquery)
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
scope :deal_ids_not_wished_by_user, lambda { |user|
joins(:wishes).where('wishes.user_id = ?', user).where('wishes.wished = ?', false).select('deals.id')
}
scope :wished_by_user, lambda { |user|
where("id not in (#{Deal.deal_ids_not_wished_by_user(user).to_sql})")
}
end
Usage example and output:
ruby-1.9.2-p180 :023 > Deal.wished_by_user(User.first).to_sql
=> "SELECT \"deals\".* FROM \"deals\" WHERE (id not in (SELECT deals.id FROM \"deals\" INNER JOIN \"wishes\" ON \"wishes\".\"deal_id\" = \"deals\".\"id\" WHERE (wishes.user_id = 1) AND (wishes.wished = 'f')))"
UPD2 (railish outer join)
Deal class:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
scope :not_wished_excluded, lambda { |user|
joins('LEFT OUTER JOIN wishes on wishes.deal_id = deals.id').
where('wishes.user_id = ? OR wishes.user_id is null', user).
where('wishes.wished = ? OR wishes.wished is null', true)
}
end
Usage:
ruby-1.9.2-p180 :096 > Deal.not_wished_excluded(User.first).to_sql
=> "SELECT \"deals\".* FROM \"deals\" LEFT OUTER JOIN wishes on wishes.deal_id = deals.id WHERE (wishes.user_id = 1 OR wishes.user_id is null) AND (wishes.wished = 't' OR wishes.wished is null)"

JSON include syntax

My setup: Rails 2.3.10, Ruby 1.8.7
I have a rather complicated set of relationships between several models.
class A
has_many :classB
has_many :classD
end
class B
belongs_to :classA
has_many :classC
end
class C
belongs_to :classB
belongs_to :classE
end
class D
belongs_to :classA
belongs_to :classE
end
class E
has_many :classD
has_many :classC
end
I'm having an issue with the JSON syntax to get all the related information starting with classA. Here's what I have working so far.
classA.to_json(:include => {:classB => {:include => [:classC, :classE]}})
I can't get the syntax working to also include classD and related classE records.
UPDATE Actually something like this might work except that I can't mix hashes and arrays
classA.to_json(:include => [ :classB => { :include => { :classC => { :include => :classE } } },
:classD, :classE ])
Note that I didn't use singular/plural in my example code above but in my real code, I am. Any insights will be much appreciated.
Thanks,
Bob
This should work:
classA.to_json(:include => {
:classB => {:include => {:classC => {:include => :classE}}},
:classD => {},
:classE => {},
})
Try this, you should only need one :include =>:
classA.to_json(:include => {:classB => [:classC, { :classE => :classD }] })
I don't know if you want classE included through both class C and classD but this should work:
classA.to_json(:include => { :classB => { :include => { :classC => { :include => :classE } } },
:classD => { :include => :classE } })
EDIT:
class A
has_many :bs
has_many :ds
end
class B
belongs_to :a
has_many :cs
end
class C
belongs_to :b
belongs_to :e
end
class D
belongs_to :a
belongs_to :e
end
class E
has_many :ds
has_many :cs
end
#class_a = A.first
#class_a.to_json(:include => { :bs => { :include => { :cs => { :include => :e } } },
:ds => { :include => :e } })

Resources