I was trying to find answer on my question, but didn't success with it.
I have models Event, participants, participation_form, invitation, user.
Event has_many participants
User has_many invitations
User has_many participation_form
For Participant I want to have field like "based_on" and it will be references with invitation or participation_form.
I have one idea about it - make two fields and one model method that will be check which field contains value and return "based_on"
My question is - is there any way to reference one model to two models with pair of fields - class (model name) and value (id) so I will add another type if I need it in future.
You could use polymorphic associations for that: (http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Could you tell more about models relations so I can write some example? Why do you need Participant model?
As mentioned byKuba Ploskonka, you'll probably benefit from a polymorphic association here:
--
Setup
For Participant I want to have field like "based_on" and it will be references with invitation or participation_form.
As per your specifications, you'll want to use the following:
#app/models/participation.rb
Class Participation < ActiveRecord::Base
belongs_to :participle, polymorphic: true
end
#app/models/invitation.rb
Class Invitation < ActiveRecord::Base
has_many :participations, as: :participle
end
#app/models/participation_form.rb
Class ParticipationForm < ActiveRecord::Base
has_many :participations, as: :participle
end
This will give you the ability to save your objects as follows:
#app/controllers/invitations_controller.rb
Class InvitationsController < ApplicationController
def create
#invitation = Invitation.new invitation_params
#invitation.participations.build #-> will save a blank "Participation" object
#inviation.save
end
end
Related
I have the following models:
user.rb
class User < ApplicationRecord
has_many :invitations
end
organization.rb
class Organization < ApplicationRecord
has_many :invitations
end
invitation.rb
class Invitation < ApplicationRecord
belongs_to :user
belongs_to :organization
end
I'm trying to query Active Record in the following way:
user = User.find(params[:id])
user.invitations.includes(:organization)
I want to be able to get all invitations for the user and also have the invitations include attributes of their related organization. However, I am only getting the invitation and none of the organization's attributes.
Even if I try:
Invitation.includes(:organization)
I'm still not getting each invitation's associated organization.
Any and all help is greatly appreciated!
includes method provides eager loading. This solves the N + 1 queries problem. You can access the loaded organization like user.invitations.first.organization. There will be no new queries here.
If you want to combine invitation and organization attributes, you can use joins and select.
user.invitations
.joins(:organization)
.select('invitations.*, organizations.foo, organizations.boo as blabla')
#demir has already answered your question.
Just as an addition - probably, you mixed up includes for models with include option of to_json \ as_json.
If you want to return some JSON result (e.g. in your API response), then you can do user.as_json(include: [invitations: { include: :organization }])
I am using Ahoy Email to track emails I send to my restaurants.
However I'dd like to create a relationship like so :
an AhoyMessage belongs_to a Restaurant
a Restaurant has_many AhoyMessages
so that I can access, for example:
ahoy_message.restaurant.phone_number
==> "+33612345678"
I know when I look at the docs that there is an easy way to do so with the User model, but I can only use my Restaurant model and hence the example in the doc does not work for my particular case.
The docs say it's polymorphic and you can use any model.
Try
class CouponMailer < ApplicationMailer
track user: -> { Restaurant.find_by(email: message.to.first) }
end
class Restaurant < ApplicationRecord
has_many :messages, class_name: "Ahoy::Message", as: :user
end
So you would still use ahoy_message.user.phone_number but the ahoy_message.user is a polymorphic association to a restaurant object.
A user can have several cars -
User: has_many :cars
Car: belongs_to :user
Every time I call #user.cars it returns the list of cars in default search order.
If I wanted the association sorted on some arbitrary field, I could do
class User < ActiveRecord::Base
has_many :cars, -> { order :num_wheels }
end
But let's say my ordering logic is complex and I want to just override the association getter to implement my own logic
I try something something like -
class User < ActiveRecord::Base
has_many :cars
def cars
# Pretend this is complex logic
cars.order(:num_wheels)
end
end
However that obviously fails because you can't reference the original cars from inside the overridden cars method without it looping infinitely.
Is there a way to reference the "original" getter from inside my overridden getter?
Thanks!
Use super:
class User < ActiveRecord::Base
has_many :cars
def cars
# Pretend this is complex logic
super.order(:num_wheels)
end
end
when you use a macro like has_many, Rails dynamically creates a module(which could be accessed by User.generated_association_methods).In your case, define the accessors and readers(such as "cars" in your case, which could be accessed by User.generated_association_methods.instance_methods). This module becomes the ancestor of your User class, so you can access the reader method(cars) by "super" in your own cars method.
With my understanding I believe what has_many is essentially doing is:
Class User < ActiveRecord::Base
has_many :cars
# is essentially
def cars
Car.where(user_id: self.id)
end
end
So when a user wants to list all the cars it would still be User.cars. When using ActiveRecord the has_many is assuming both the method name of cars and the foreign keys associated.
Try this:
class User < ActiveRecord::Base
has_many :cars
def cars
Car.where(user_id: id).order(:num_wheels)
end
end
In many to many fields delete method is deleting all the occurrence of collection. Say I have:
class user < ActiveRecord::Base
has_and_belongs_to_many :cars
end
class car < ActiveRecord::Base
has_and_belongs_to_many :users
end
users and cars are many to many relationship, I have defined my users_cars table. Now user can have repetitive car entry as relation. For example:
Car: A,B,C
User: U1,U2,U3
U1=[A,B,C,A,A,A,B]
Which can be implemented using many to many relationship, the way I have implemented. BUT, at the time when I want to delete one of the car entries of user the problem occurs.
User.cars.delete(car) #deletes all occurrence of car
User.cars.delete_at(User.cars.find_index(video_card)) #delete_at does not exist
Now how to resolve this?
First of all, you can't call User.cars unless you have defined a class level method cars in your User model, but in this way, you would return all cars, and that - in no way - would make sense.
Second, delete_at is a method that works on Array objects, and expects an integer to be passed in. So as a little hack, you can turn your ActiveRecord::Associations object into an array, and then call delete_at method.
user = User.first
user.cars.to_a.delete_at(Car.last.id) # assuming that the last car belongs
# to the first user, something you would never do in actual
# production code.
Edit:
You can also try the following to achieve the same functionality:
user = User.first
user.cars.where("cars.id = ?", Car.first.id).first.delete
Edit 2:
For what you asked in comment, you can have a model for the table cars_users.
rails g model CarUser
class Car < ActiveRecord::Base
has_many :cars_users
has_many :users, through: car_users
end
class User < ActiveRecord::Base
has_many :cars_users
has_many :cars, through: car_users
end
class CarUser < ActiveRecord::Base
belongs_to :car
belongs_to :user
end
And now, you can do:
CarUser.where("car_id = ? AND user_id = ?", Car.first.id, User.first.id).first.delete
I have model Message. It may be posted by either Person or Organization from my DB.
I want to call update.contact, where contact is Organization or Person.
class Update < ActiveRecord::Base
has_one :contact
The decision I like to use is like
class Organization < ActiveRecord::Base
belongs_to :update, as: :contact
But this approach not available in Rails.
Should I use polymorphic association? How to organize the architecture for this case?
It sounds like Organization and Person might be two different variants of a same entity (a customer, user, whatever). So, why not create a common parent model for the two of them? Such a parent wouldn't necessarily need a table, and you might just define common methods inside of it. Update is more of an action rather than an object, which could be applied to a Contact object (typically in its controller). For the Contact class, polymorphic association can be used. So you might have:
class Parent < ActiveRecord::Base
# stuff Person and Organization share
end
class Person < Parent
has_one :contact, as: :owner
# Person methods etc.
end
class Organization < Parent
has_one :contact, as: :owner
# Organization stuff
end
class Contact
belongs_to :owner, polymorphic: true
def update
#...
end
# other stuff for Contact
end
Then you can write lines like:
Person.first.contact.update
or whatever you need to do with your objects.
In case your Organization and Person don't differ too much, you could just create a table for the parent class, and add the has_one etc. in there.