In an attempt to expand on this guide for creating multiple associations with the same table I came up with the following user class
class User < ActiveRecord::Base
has_secure_password
validates_uniqueness_of :email
validates_presence_of :email
validates_presence_of :name
belongs_to :role
has_many :clients, foreign_key: 'rep_id'
has_many :submitted_jobs, class_name: 'WorkOrder', through: :clients
has_many :processing_tasks, class_name: 'WorkOrder', foreign_key: 'processor_id'
def has_role?(role_sym)
role.name.underscore.to_sym == role_sym
end
end
My goal is to be able to refer to submitted jobs and processing tasks separately depending on the type of user. So far the processing tasks part works as I expected and so far I can get the rep from the workorder, but when I attempt something like rep.submitted_jobs I get the following error:
NoMethodError: undefined method `to_sym' for nil:NilClass
from /usr/local/rvm/gems/ruby-2.1.5#rails4/gems/activerecord-4.1.6/lib/active_record/reflection.rb:100:in `_reflect_on_association'
ect...
Clearly the has_many through relationship works differently than I'm expecting, but I'm not even quite sure what to call this type of relationship so I'm at something of a loss for what to look for.
Its probably also worth noting that
RSpec.describe User, type: :model do
it {should validate_uniqueness_of(:email)}
it {should validate_presence_of(:name)}
it {should belong_to(:role)}
it {should have_many(:submitted_jobs)}
it {should have_many(:processing_tasks)}
end
all pass
EDIT:
class Client < ActiveRecord::Base
has_many :contacts
has_many :addresses, through: :contacts
has_many :permits
has_many :work_orders
validates :clientnumber, format: { with: /\A\d{3}\z/ },
length: { is: 3 }
belongs_to :rep, class_name: 'User'
belongs_to :default_processor, class_name: 'User'
end
EDIT 2:
work order associations
class WorkOrder < ActiveRecord::Base
belongs_to :client
belongs_to :project_type
belongs_to :status
belongs_to :labels
belongs_to :contact
belongs_to :processor, class_name: 'User'
has_one :rep, through: :client, class_name: 'User'
has_one :presort_information
has_one :printing_instructions
has_one :production_details
........
end
Typical has_many_through_association look like
To solve this problem, you could write custom association, a method that will give you related work_orders
Inside User model
def submitted_jobs
WorkOrder.joins(:client).where('clients.rep_id = ?', self.id)
end
Related
I have 3 models as follows :
class User
has_many :event_series, inverse_of: :user
has_many :events, through: :event_series, inverse_of: :user
end
class EventSeries
belongs_to :user, inverse_of: :event_series
has_many :events, inverse_of: :event_series
end
class Event
belongs_to :event_series, inverse_of: :events
has_one :user, through: :event_series, inverse_of: :events
end
This is all fine.
Now I want to add a special event for each user called the 'showcase_event'.
class User
has_one :showcase_event, class_name: 'Event', inverse_of: :user
end
This isn't working because the Event model doesn't have the user directly, it's associated through EventSeries.
I'm getting an error during serialization:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column events.user_id does not exist
I'm using fast jsonapi:
class PublicUserSerializer
include FastJsonapi::ObjectSerializer
...
has_one :showcase_event, record_type: :event, serializer: EventSerializer
...
end
It seems to me that the inverse relationship that I have between User, Event and EventSeries needs to work between User and showcase_event but I don't know how to specify that ONLY the inverse is through EventSeries
Apologies in advance for not having the best vocabulary to describe this problem.
Solved by my rubber duck.
As seen in the documentation :
The #belongs_to association is always used in the model that has the
foreign key.
So I changed my declaration of the showcase_event to this :
class User
belongs_to :showcase_event, class_name: 'Event', inverse_of: :user, optional: true
end
And problem solved
I have three models making up a basic has_many through relationship:
class Booking < ApplicationRecord
validates_presence_of :user, :ride, :role, :required_seats
belongs_to :user
belongs_to :ride
end
class Ride < ApplicationRecord
validates_presence_of :origin, :destination, :leave_at, :arrive_at, :price, :seats
has_many :bookings, dependent: :destroy
has_many :users, through: :bookings
accepts_nested_attributes_for :bookings, :allow_destroy => true
end
class User < ApplicationRecord
has_secure_password
validates_presence_of :first_name, :last_name, :email, :password_digest
has_many :bookings, dependent: :destroy
has_many :rides, through: :bookings
accepts_nested_attributes_for :bookings, :allow_destroy => true
end
When running a model spec below:
RSpec.describe Booking, type: :model do
it { should belong_to(:users) }
it { should belong_to(:rides) }
it returns
Failure/Error: it { should belong_to(:users) }
Expected Booking to have a belongs_to association called users (no association called users)
Failure/Error: it { should belong_to(:rides) }
Expected Booking to have a belongs_to association called rides (no association called rides)
The belongs_to association has clearly been made in the join model `bookings', yet it is not being recognized in the model.
The bookings table has a user_id and ride_id column, with foreign keys assigned to their respective tables.
I've been stuck on this for a week now, any help would be appreciated as to why this could be happening!
as arieljuod rightfully pointed out, the models in my spec should have been singular instead of plural.
RSpec.describe Booking, type: :model do
it { should belong_to(:user) }
it { should belong_to(:ride) }
Thanks!
I have two models: Users and Posts. The way I have things setup, a post belongs to an owner (i.e. user) and also has many participants (i.e. users). In my User model I'd like to ensure that an owner never belongs to a post. I've done this in the front-end but found more code than need-be.
This led me to believe that using conditions would be an ideal solution. I've seen SQL conditions used in this manner but didn't know exactly what the best way to get this done for an ownership scenario. Suggestions?
class User < ActiveRecord::Base
has_many :posts
# belongs_to :posts, conditions: ...
end
class Post
has_many :participants, class_name: "User", foreign_key: "user_id"
belongs_to :owner, class_name: "User", foreign_key: "user_id"
end
To acheive this, I think you need a third model. If you set things up as follows it should work:
User model:
class User < ActiveRecord::Base
has_many :posts # This is the other side of your owner association
has_many :user_posts # This is your link table for participants
has_many :participations, through: :user_posts, source: :user # These are the posts the user is a participant in
end
Post model:
class Post < ActiveRecord::Base
has_many :user_posts, ->(p) { where.not(user_id: p.user_id) } # Here is your condition for the participants
has_many :participants, through: :user_posts, source: :user
belongs_to :owner, class_name: "User", foreign_key: "user_id"
end
UserPost model:
class UserPost < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
As #Oxynum's answer makes clear, you should also think about putting a validation in the UserPost model to prevent the participant from being saved if he is also the owner:
validate :participant_cannot_be_owner
def participant_cannot_be_owner
if user == post.try(:owner)
errors.add(:user_id, "can't be the owner of the post")
end
end
First, there is probably an error in your associations, cause it seems like you need a join table for the participants relationship.
You should probably use a http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
has_many through association.
Something like this :
class User < ActiveRecord::Base
has_one :owned_post, class_name: "Post", foreign_key: :owner_id
has_many :participations
has_many :posts, through: :participations
end
class Participation < ActiveRecord::Base
belongs_to :post
belongs_to :participant, class_name: "User"
end
class Post < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :participants, through: :participations
end
When you have this model, you can use a validation on the participation model to prevent an owner to be a participant. By using a custom validation : http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
class Participation < ActiveRecord::Base
belongs_to :post
belongs_to :participant, class_name: "User"
validate :participant_is_not_the_owner
def participant_is_not_the_owner
if participant == post.owner
errors.add(:participant, "can't be the owner")
end
end
end
I need a way of referrencing 2 different objects as 1.
I have a Message object with needs to keep track of Recipients. the problem is that Recipients could be a User or a Contact.
should the models be: ?
class Message < ActiveRecord::Base
has_many :users, as: :recipients
has_many :contacts, as: :recipients
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
because, I feel like polymorphic relationships are built to go the opposite way.
also, this way doesn't allow me to reference #message.recipients which is what I need.
I hope this makes sense
Thank you
What you have done is completely incorrect. I think you need many-to-many association. My association whould be that:
class Message < ActiveRecord::Base
has_many :recipient_links
has_many :users, through: :recipient_links, source: :recipient, source_type: 'User'
has_many :contacts, through: :recipient_links, source: :recipient, source_type: 'Contact'
end
class Contact < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
class User < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
# fields: message_id, recipient_id, recipient_type
class RecipientLink < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
belongs_to :message
end
... I can't add comments yet so i give here solution for: When use answer from up to receive all #message.recipients every type in only 2 request:
RecipientLink.includes(:recipient).where(message_id: #message.id).collect(&:recipient)
I have the following models:
class Productmainclass < ActiveRecord::Base
attr_accessible :name, :id, :maintext
has_many :producttaggings, :dependent => :destroy
has_many :products, :through => :producttaggings
has_many :productsubclasses
end
class Productsubclass < ActiveRecord::Base
attr_accessible :name, :id, :maintext
has_many :producttaggings, :dependent => :destroy
has_many :products, :through => :producttaggings
belongs_to :productmainclass
end
class Product < ActiveRecord::Base
attr_accessible :name, :productimage, :size, :description, :price
has_many :producttaggings, :dependent => :destroy
has_many :productsubclasses, :through => :meteoritetaggings
has_many :productmainclasses, :through => :meteoritetaggings
mount_uploader :productimage, ProductimageUploader
end
class Producttagging < ActiveRecord::Base
belongs_to :product
belongs_to :productsubclass
belongs_to :productmainclass
attr_accessible :product_id, :productsubclass_id, :productmainclass_id
end
I now want to create a Product with FactoryGirl and Capybara. In the spec I simply have:
product = FactoryGirl.create(:product)
In my factories.rb I have:
factory :product do
name "Blue glass"
description "Description text of product"
productimage File.new(File.join(::Rails.root.to_s, "spec/factories/", "testimage.jpg"), 'rb')
productsubclass
productmainclass
end
factory :productsubclass do
name "Colored glasses"
productmainclass
end
factory :productmainclass do
name "Glasses"
end
Running the test I get:
Failure/Error: product = FactoryGirl.create(:product)
NoMethodError:
undefined method `productsubclass=' for #<Product:0xcd42090>
I think the way you have it setup would work if you were dealing with a situation where :product belonged to productsubclass, then the product and the productsubclass would be created with the product.productsubclass_id nicely inserted and all would be fine, but that's clearly not your structure, so we'd have to use another way. I think the link that #depa noted is the right way to go, specifically the 'Basic has many associations' section in this document: http://robots.thoughtbot.com/aint-no-calla-back-girl although you have the added complexity of a has_many through. But essentially your looking at a situation where you create an object and then after that you trigger another create to make the many's. Hope this makes sense :)
** Update **
Here's another approach which might be a little limited but you could just create the records from the other direction. So, if you just want one record in each object/table how about this:
FactoryGirl.define do
factory :producttagging do
product
productsubclass
productmainclass
end
end