I have the following AR relations,
class Restaurant < ActiveRecord::Base
has_many :deals
end
class Deal < ActiveRecord::Base
belongs_to :restaurant
acts_as_taggable_on :tags
end
What I want to do is to find the deals with given tags, and return the related restaurants.
I have the following scope in my Restaurant model
scope :food_type, ->(food_types){
select("*")
joins(:deals).merge(Deal.tagged_with([food_types], any: true, wild: true ))
}
But the issue is, when I call Restaurant.food_type(<tags>) it returns ActiveRecord relation with Restaurant objects with Deal data
Ex: #<Restaurant id: 383, name: "sample deal name", note: "sample deal note", created_at: "2014-03-18 22:36:27", updated_at: "2014-03-18 22:36:27"
I even used select in the scope , without any luck. How can I return Restaurant attributes from the above scope?
Related
Goal: I would like to include all of a customers medical conditions as an array in the result of a customer.
for:
cust = Customer.includes(:conditions).find(1)
expected result:
#<Customer id: 1, first_name: "John", last_name: "Doe", conditions [...]>
actual result:
#<Customer id: 1, first_name: "John", last_name: "Doe">
code:
I have 2 classes and a 3rd join class (ConditionsCustomer).
class Customer < ApplicationRecord
has_many :conditions_customers
has_many :conditions, through: :conditions_customers
end
#join table. Contains 2 foreign_keys (customer_id, condition_id)
class ConditionsCustomer < ApplicationRecord
belongs_to :customer
belongs_to :condition
end
class Condition < ApplicationRecord
has_many :conditions_customers
has_many :customers, through: :conditions_customers
end
What's interesting is that I see 3 select queries getting fired (customer, join table and medical conditions table) so I know the includes is somewhat working but unfortunately customer returns without the medical conditions.
I've also tried using a join but I get an array of same customer over and over again.
Is there an easy way to do this with ActiveRecord? I would prefer not having to merge the record manually.
Not really possible via active record, as json offers some cool possibilities :
render json: customers,
include: {
conditions: {
only: [:attr1, :attr2], # filter returned fields
methods: [:meth1, :meth2] # if you need model methods
},
another_joined_model: {
except: [:password] # to exclude specific fields
}
}
I created 3 models as below, and used cocoon nested form to create associations between them.
class Unit < ApplicationRecord
has_many :mapping_categories, -> { distinct }, dependent: :destroy, inverse_of: :unit
accepts_nested_attributes_for :mapping_categories,
allow_destroy: true,
reject_if: :all_blank
end
class MappingCategory < ApplicationRecord
belongs_to :unit
has_many :mapping_items, -> { distinct }, dependent: :destroy, inverse_of: :mapping_category
accepts_nested_attributes_for :mapping_items,
allow_destroy: true
end
class MappingItem < ApplicationRecord
belongs_to :mapping_category
has_many :mapping_item_links
has_many :linked_mapping_items, through: :mapping_item_links, dependent: :destroy
end
Each mapping_item can have many other mapping_items through a joint table. In every mapping_item section in Unit form, this association is displayed as a select input.
When creating or updating Unit, there are many mapping_categories tabs in the Unit form, and there are many mapping_items sections in each mapping_category section.
For example, I have Mapping Category A and Mapping Category B. I want to add Mapping Item 1 to Mapping Category A and Mapping Item 2 to Mapping Category B. The question is: How to create the association between Mapping Item 1 and Mapping Item 2, as these two items are not saved yet?
Thanks in advance.
YOU CAN DO IT
You have to write right code
user = User.new(name: 'Jons', email: 'jons#qq.ww')
bank_account = BankAccount.new(number: 'JJ123456', user: user)
bank_account.save
in this way will be saved both raws and user and bank_account
in your case:
unit = Unit.new(mapping_categories: [mapping_category])
mapping_category = MappingCategory.new(mapping_items: [mapping_item])
mapping_item = MappingItem.new
unit.save
and if you wanna use nested_attributes, you just have to build hash with attributes
params = { mapping_categories: [mapping_items: [{.....}]}] }
Unit.create(params)
but you have to figure out with right nesting
From my understanding of your question... You can't. These items don't yet have ids and there for can't be associated with another model.
> contact = Contact.new(full_name: "Steve", email:"example#asdf.com")
=> #<Contact id: nil, full_name: "Steve", email: "example#asdf.com", created_at: nil, updated_at: nil>
> invoice = Invoice.new(contact_id: contact.id, invoice_type: "Something")
=> #<Invoice id: nil, contact_id: nil, invoice_type: "Something" created_at: nil, updated_at: nil>
> invoice.save
=> false
A Manager has many contacts via polymorphic association
class Manager
has_many :contacts, as: :contactable
end
class Contact
belongs_to :contactable, polymorphic: true
end
The relation works fine but now a contact can be associated to many managers.
So, added a new model Contactable, a joins table 'contactables' and moved contactable_id and contactable_type fields from contacts table to contactables table.
class Contactable
belongs_to :contact
belongs_to :contactable, polymorphic: true
end
Now confused about the Manager and Contact relation that how it would be defined in models correctly to make it working. Tried the following but it doesn't work:
class Manager
has_many :contacts, through: :contactables, source: :contactable, source_type: "Contact"
end
So I checked this interesting topic and will tell what I know.
When you create objects as usual in has_many :through:
class Contact
has_many :contactables
has_many :managers, :through => :contactables
end
class Manager
has_many :contactables
has_many :contacts, :through => :contactables
end
class Client
has_many :contactables
has_many :contacts, :through => :contactables
end
class Contactable
belongs_to :contact
belongs_to :manager
belongs_to :client
end
You get to use foreign keys fro each referenced object. Polymorphic looks like a great solution. So:
class Contactable
belongs_to :contact
belongs_to :polymorphic_model, polymorphic: true
end
class Contact
has_many :contactables
has_many :managers, :through => :contactables, :source => :polymorphic_model, :source_type => 'Manager'
end
class Manager
has_many :contactables, :as => :polymorphic_model
has_many :contacts, :through => :contactables
end
Setting the :as option indicates that this is a polymorphic
association
:source => :polymorphic_model is used to tell Rails to get the related object from the subclass. :source means the same as :class_name. Without this option Rails would try to get associated Manager from the Contactables table, while it should be reached via virtual Polymorphic_model.
By adding belongs_to :polymorphic_model to Contactable you enable Contact (witch already sits there, because of belongs_to :contact) to be associated with a Manager or Client, because thats what Polymorphic association does - references two or more parent tables. And because Contact have_many Contactables, the same Contact object can be associated with many managers or clients. So after you understand it, it looks really simple - Joined model belongs to Contact and Joined model also holds references to Manager and Client through Polymorphic association. So in order for Contact to have many managers, you create another Contactable object that belongs to the same Contact, but different Manager. Doesn't look super efficient, but personally me, not knowing a better way..
Here is a tested proof:
Manager.create!(name: "Bravo")
=> #<Manager id: 1, created_at: "2017-04-12 12:17:41", updated_at: "2017-04-12 12:17:41", name: "Bravo">
Manager.create!(name: "Johnny")
=> #<Manager id: 2, created_at: "2017-04-12 12:18:24", updated_at: "2017-04-12 12:18:24", name: "Johnny">
Contact.create!(number:"123")
=> #<Contact id: 1, created_at: "2017-04-12 12:18:59", updated_at: "2017-04-12 12:18:59", number: 123>
c = Contactable.new
c.contact = Contact.first
c.unit = Manager.first
c
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 1, contact_id: 1>
Now to set another Manager to the same contact, we create a new Contactable:
cc = Contactable.new
cc.contact = Contact.first
cc.unit = Manager.last
cc
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 4, contact_id: 1>
And to get all associated:
Contact.first.managers
Contactable's database:
contact_id
unit_id
unit_type
And one interesting quote by #Bill Karwin:
The Polymorphic Associations design breaks rules of relational
database design. I don't recommend using it.
But he wrote this long time ago. Probably irrelevant now.
Why can you not have a foreign key in a polymorphic association?
I have an STI model with many associations:
class MyModel < ActiveRecord::base
has_many :things
has_many :other_things
# ... a lot of `has_many`
end
Then I add non STI model as nested just to add some specific behaviour to MyModel without extending it directly:
class Nested < MyModel
self.inheritance_column = nil
end
But then my associations don't work. They have my_model_id column because they refer to MyModel and they should refer to Nested as well. But all these has_many's expect to use nested_id column as a foreign key (it depends on class name).
I could've typed inside class Nested:
has_many :things, foreign_key: 'my_model_id'
has_many :other_things, foreign_key: 'my_model_id'
But if it's possible, how to specify the foreign key for all the associations at once in Nested class?
If all your has_many associations are declared on MyModel, you should be fine, or you may benefit from upgrading Rails; the following works perfectly for me in 4.2.4. Rails uses the class that declares has_many to generate the foreign_key, so even when Nested inherits, :things still get looked up by my_model_id.
class MyModel < ActiveRecord::Base
has_many :things
end
class MyA < MyModel
end
class Nested < MyModel
self.inheritance_column = nil
end
class Thing < ActiveRecord::Base
belongs_to :my_model
end
> m = MyModel.create
=> #<MyModel id: 1, type: nil, created_at: "2015-12-22 02:21:16", updated_at: "2015-12-22 02:21:16">
> m.things.create
=> #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 1, my_model_id: 1, created_at: "2015-12-22 02:21:19", updated_at: "2015-12-22 02:21:19">]>
> n = Nested.create
=> #<Nested id: 2, type: nil, created_at: "2015-12-22 02:21:27", updated_at: "2015-12-22 02:21:27">
> n.things.create
=> #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 2, my_model_id: 2, created_at: "2015-12-22 02:21:32", updated_at: "2015-12-22 02:21:32">]>
> n.reload.things
Nested Load (0.2ms) SELECT "my_models".* FROM "my_models" WHERE "my_models"."id" = ? LIMIT 1 [["id", 2]]
Thing Load (0.1ms) SELECT "things".* FROM "things" WHERE "things"."my_model_id" = ? [["my_model_id", 2]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 2, my_model_id: 2, created_at: "2015-12-22 02:21:32", updated_at: "2015-12-22 02:21:32">]>
If you're giving Nested its own has_many associations, the code that generates foreign keys is pretty deep and inaccessible. You end up in the HasManyReflection, which either uses the foreign_key (or as) option declared on your has_many or derives it from the class name. There's no obvious way to customize those methods unless you do something inadvisable like override Nested.name.
foreign_key:
def foreign_key
#foreign_key ||= options[:foreign_key] || derive_foreign_key
end
derive_foreign_key:
def derive_foreign_key
if belongs_to?
"#{name}_id"
elsif options[:as]
"#{options[:as]}_id"
else
active_record.name.foreign_key
end
end
So the simplest way to go about this might just be a loop:
class Nested < MyModel
self.inheritance_column = nil
%i[things other_things].each do |association|
has_many association, foreign_key: 'my_model_id'
end
end
Or if you're feeling metaprogrammy, redefine has_many:
class Nested < MyModel
self.inheritance_column = nil
def self.has_many(klass, options={})
options.reverse_merge!(foreign_key: 'my_model_id')
super
end
has_many :things
has_many :other_things
end
My solution might be is not recommended, but it works. Here it is:
class Nested < MyModel
def self.name
MyModel.name
end
end
ActiveRecord will be looking for my_model_id foreign key for all the associations defined or redefined in Nested and MyModel classes.
I have 2 very simple models:
class Teacher < ActiveRecord::Base
has_one :fee, :foreign_key => 'teacher_id'
end
...
class Fee < ActiveRecord::Base
belongs_to :teacher
end
When loading teacher information with the "find" method, only teacher table information are present. (data exists in the fee table)
#teacher = Teacher.find(id)
With the debug method :
--- !ruby/object:Teacher
attributes:
id: 7
…
presentation:
presentation_flag: true
created_at: 2013-09-16 00:38:14.000000000 Z
updated_at: 2013-09-16 00:38:14.000000000 Z
status:
last_login:
first_name:
…
But nothing about fee table. Any ideas ? Thank you
This is by design. ActiveRecord is 'lazy' by default, it only fetches the fee data when you access #teacher.fee for the first time.
In case you want to load both at the same time, you should use 'eager loading' by adding includes like this:
#teacher = Teacher.includes(:fee).find(id)
Here are a couple of references for 'eager loading':
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
http://railscasts.com/episodes/22-eager-loading-revised