Has many conditions in Rails 4 - ruby-on-rails

How to do this:
has_many :space_mappings, -> { where(group_id: group_id) }, through: :category
That is - SpaceMapping and this model both has a group_id and they have to match. I could just make a method but I would like for this to be possible.
Here I get:
undefined local variable or method `group_id' for #<ActiveRecord::Relation::ActiveRecord_Relation_SpaceMapping:0x007fe5ac118bd8>
I have done this instead:
def space_mappings
category.space_mappings.where(space_id: Space.where(group_id: group_id))
end
Thanks in advance.

You must assign a value for category_id.
See http://guides.rubyonrails.org/association_basics.html (4.3.3.1 section).
It can help your for understanding details and clear concept.

If "this model" has a belongs_to :category relationship or a has_one :category relationship, then you shouldn't need that where clause at all. The whole point of "has many through" is to restrict the associated models to those that are associated with the model they're associated through
That is, you should just be able to do
belongs_to :category
has_many :space_mappings, through: :category
assuming that space mappings also belong to a category.

Related

rails4 scoped has_many association

In my product_users joint table there is a role column besides the product_id and user_id.
I have this association in my product model.
has_many :owners, -> { where(product_users: { role: "owner" }) },
through: :product_users, source: :user
All of the products will have only one "owner" and the rest will be "member".
What association should I use to to get the owner of the product instead of an owners collection. So in the views I wanna use product.owner. I couldn't figure out how to use either has_one or belongs_to.
I could use this instance method, but I guess it would be better to define a fine association somehow.
def owner
owners.first
end
I guess the easiest way to do that, would be to add a column "owner_id" to the product. Then, on the product:
belongs_to :owner
and on the user something like this
has_many :owned_products, class_name: "Product", foreign_key: "owner_id"
The "class_name" tells the association that you will be looking for a "Product" and the foreign_key, will define what column will be used to compare with users id.
If you don't want to add additional column, then you can name association to "has_many :owner", but that's wrong on so many levels, that you shouldn't do it. So in the case you don't want to add additional column, stick to the method.
def owner
owners.first
end

Rails 4 and referencing parent id in 'has_many' SQL

TL;DR: How do I use the ID of the respective parent object in a has_many SQL clause to find child objects?
Long version:
I have the following example code:
class Person < AR::Base
has_many :purchases, -> {
"SELECT * from purchases
WHERE purchase.seller_id = #{id}
OR purchase.buyer_id = #{id}"
}
This was migrated from Rails 3 which worked and looked like
has_many :purchases, :finder_sql => proc { #same SQL as above# }
I want to find all purchases associated with a Person object in one association, no matter whether the person was the one selling the object or buying it.
Update: I corrected the SQL, it was inside out. Sorry! Also: The association only needs to be read-only: I am never going to create records using this association, so using id twice should be OK. But I do want to be able to chain other scopes on it, e.g. #person.purchases.paid.last_5, so creating the sum of two associations in a method does not work (at least it didn't in Rails 3) since it doesn't return an AM::Relation but a simple Array.
When using this above definition in Rails 4.2, I get
> Person.first.purchases
undefined method `id' for #<Person::ActiveRecord_Relation:0x...>
The error is clear, but then how do I solve the problem?
Since this is only an example for much more complicated SQL code being used to express has_many relationships (e.g. multiple JOINS with subselects for performance), the question is:
How do I use the ID of the parent object in a has_many SQL clause?
I don't think your code will work at all. You are defining an association with two foreign keys ... that'd mean that in case you want to create a new Person from a present Purchase, what foreign key is to be used, seller_id or buyer_id? That just don't make sense.
In any case, the error you are getting is clear: you are calling a variable id which is not initialized in the block context of the SQL code.
A better approach to the problem I understand from your question would be to use associations in the following way, and then define a method that gives you all the persons, both buyers and sellers that a product has. Something like this:
class Purchase < ActiveRecord::Base
belongs_to :buyer, class_name: 'Person'
belongs_to :seller, class_name: 'Person'
def persons
ids = (buyer_ids + seller_ids).uniq
Person.where(ids: id)
end
end
class Person < ActiveRecord::Base
has_many :sold_purchases, class_name: 'Purchase', foreign_key: 'buyer_id'
has_many :buyed_purchases, class_name: 'Purchase', foreign_key: 'seller_id'
end
Im my approach, buyer_id and seller_id are purchase's attributes, not person's.
I may have not understood correctly, in that case please clarify.

Rails many-to-many relations and nested forms: associate with object if it already exists

I'm having an issue with several many-to-many relations in my Rails project. It can be illustrated with an example:
Say I have the models Person and PhoneNumber, joined by PersonPhoneNumber. The relation is many-to-many because people can have more than one phone number, and more than one person can be reached at the same phone number (in a case such as a help desk).
class Person < ActiveRecord::Base
has_many :person_phone_numbers
has_many :phone_numbers, :through => :person_phone_numbers
end
class PhoneNumber < ActiveRecord::Base
has_many :person_phone_numbers
has_many :people, :through => :person_phone_numbers
validates :number, :uniqueness => true
end
class PersonPhoneNumber < ActiveRecord::Base
belongs_to :person
belongs_to :phone_number
end
I have a person form that lets me create/update people's contact information. I use it to assign the number 555-555-1212 to Bob. If a PhoneNumber object with that number doesn't exist, I want it to be created (as in the standard accepts_nested_attributes_for behavior). But if it does exist, I want to just create a PersonPhoneNumber object to associate Bob with that PhoneNumber.
How can I accomplish this most elegantly? I tried putting a before_validation hook in PersonPhoneNumber to look for a matching PhoneNumber and set phone_number_id, but this caused really bizarre behavior (including making my Rails server crash with the message Illegal instruction: 4).
You can use exists? method to check for existence first, like this:
#person.phone_numbers.build(number: "555-555-1212") unless #person.phone_numbers.exists(number: "555-555-1212")
Or you can do something like this:
PhoneNumber.find_or_create(person_id: #person.id, number: "555-555-1212")
Rachel the Rails documentation says this:
A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening model.
What is the difference?

has_and_belongs_to_many or polymorphic has_many :through?

I'm having trouble figuring out the right way to set this association up.
I have 3 models: Musicians, Bands and Managers. Each of those can have a certain "level" associated with them (stress, happiness, etc).
But I'm not sure how to correctly associated my Levels model with the other 3.
Do I need some sort of has_many :through that's polymorphic? And how on earth do I set that up? Is there some other type of associated I need?
If this is the case where you're defining attributes that can be assigned to a particular class of model, then you probably want to use a more conventional approach. Instead of kind use something like record_type and build a scope on that instead.
That way what you'd do to fetch them is create an accessor method, not a relationship, between any of your entities and this column. Something like this:
def levels
Level.for_record_type(self.class)
end
The for_record_type is a scope:
scope :for_record_type, lambda { |record_type|
where(:record_type => record_type.to_s)
}
There's no convention for associating models with classes instead of instances of other models.
Yep, this would be a polymorphic association:
class Level < ActiveRecord::Base
belongs_to :leveled, :polymorphic => true # maybe there's a better word than "leveled"
end
class Musician < ActiveRecord::Base
has_many :levels, :as => :leveled
end
You'll need to add a leveled_type and leveled_id to your Levels table.

Rails using a join model attribute in a condition for a find

I'm using a :has_many, :through association to link two models, User and Place
It looks like this -
In User:
has_many :user_places
has_many :places, :through=>:user_places
In Place:
has_many :user_places
has_many :users, :through=>:user_places
In User_Place
belongs_to :user
belongs_to :place
belongs_to :place_status
On that last one note the place_status.
I want to write a find that returns all places associated to a user with a particular place_status_id.
Place_Status_id is on the join model, user_place.
So basically I want
User.places.where(:place_status_id=>1)
(in rails 3)
but i get an error with that because place_status_id isnt on the place model.
Any ideas? Thanks all.
I believe you can do your find this way
#user.places.joins(:user_places).where(:user_places => {:place_status_id => 1})
I've never used Rails 3, so I'm sorry if there's any errors.

Resources