Going back to an example I had previously asked about, I'll try and make this question as simple as possible.
Supposed I have User and Document models.
A User has_many Documents and a Document belongs_to a User. This relationship works fine today.
I want to introduce an Edits model that belongs_to a Document and belongs_to a User. The User can be any user, not necessary the one who created the document. With this new model, the Document now has_many :edits and the User has_many :edits.
So far it would look like:
# user.rb
class User < ActiveRecord::Base
has_many :edits
has_many :documents
end
# document.rb
class Document < ActiveRecord::Base
belongs_to :user
has_many :edits
end
# edit.rb
class Edit < ActiveRecord::Base
belongs_to :user
belongs_to :document
end
When I create a Document through a User, the association between the User and Document are fine in both directions (user.documents and document.user)
Now when I want to create an Edit, the edit should be against a Document but should also be associated with the user who generated the edit (edit.user).
When I'm building this in my RSpec tests I'm struggling to get the associations correct using the "standard" association methods. If I do #user.edits.build({...}) it will associate the user in the returned Edit object, but not the Document. Likewise when I do #document.edits.build({...}) it will associate the Document but not the User.
I supposed I could expose the user_id and post_id in the attr_accessible declaration but won't this but I fear this is not a best way of doing this. I have no real reason to fear other than the attributes are now accessible through mass assignment (from what I understand).
Am I going about this the wrong way or is there a better way to create and test all the associations?
class User < ActiveRecord::Base
has_many :edits, :through => :documents
has_many :documents
end
Related
In Rails, how would one conditionally associated records on a has_many_through relationship? Using the following Rails docs example:
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
Suppose I wanted to have an appointment reference exactly two physicians. That is, there will not be any appointment record one there is less than two physicians assigned. However, how could that appointment then reference each physician?
Example
Basically, I want to keep track of users liking other users and mutual likes between them. A connection is established when both users like each other. But I don't want a connection when only one user likes another but it is not reciprocal.
When User A likes User B. A "like" is created.
When User B likes User A. A "like" is created. A "connection" is also created.
The connection should be able to call:
connection.users
The user should be able to call:
user.likes
user.connections
The problem that I'm having is how can that relationship table know when it is mutual?
For the original question, a connection doesnt make a difference between the two users, so i would model it as a one to many relationship and validate it only has two users.
A like has two users, the liker (giver of the like) and the likee (receiver of the like). Every time you create new like, you should check if the likee also likes the liker. If likee.likes.where(likee: liker)? If yes, then create the new connection with both users.
class User < ApplicationRecord
has_many :likes
has_many :connections
end
class Like < ApplicationRecord
belongs_to :user, :foreign_key => 'liker_id'
belongs_to :user, :foreign_key => 'likee_id'
end
class Connection < ApplicationRecord
has_many :likes
has_many :users, through: likes
end
I want to add that i am not 100% sure of this as i am currently learning Rails myself. But this is what I came up with and hopefully its useful (and correct).
Let's imagine that I have a CPA tracking system.
I would have following models: an Offer, it has some Landings, each of them has multiple Links, each of the links has a bunch of Visits.
So, I what I want is DRY code, therefore offer_id column within visits table is unacceptable. The workaround here is delegated methods like this:
class Offer < ActiveRecord::Base
has_many :landings
has_many :links, through: :landings
has_many :visits, through: :landings
end
class Landing < ActiveRecord::Base
belongs_to :offer
has_many :links
has_many :visits, through: :links
end
class Link < ActiveRecord::Base
belongs_to :landing
has_many :visits
delegate :offer, to: :landing
end
class Visit < ActiveRecord::Base
belongs_to :link
delegate :landing, to: :link
delegate :offer, to: :link
end
It works nice with a single visit, e.g. visit.offer.id. But what if I need different visits associated with one offer?
The issue is that I'm unable to construct a valid query using ActiveRecord API. It might look like Visits.where(offer: Offer.first), but it doesn't work this way, saying ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: visits.offer: SELECT "visits".* FROM "visits" WHERE "visits"."offer" = 1, which is predictable.
Question: How should I organize my code to make statements like Visits.where(offer: Offer.first) work efficiently without duplicating offer_id column within visits table?
You code was organized nicely, don't need to refactor I think. You can achieve that by defining a scope in Visit like this:
class Visit < ActiveRecord::Base
scope :from_offer, -> (offer) {
joins(link: :landing).where(ladings: {offer_id: offer.id})
}
scope :from_landing, -> (landing) {
joins(:link).where(links: {landing_id: landing.id})
}
end
So the query will be:
Visit.from_offer(Offer.first)
Given two associated models in rails (4.0),
class User < ActiveRecord::Base
has_one :subscription, dependent: :destroy
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
The above code will ensure that when an instance of User is destroyed, its associated record will be, too.
So far so good.
My question is, is it possible to equally easily invoke a dependent update as well, so that every time User is updated, Subscription will be updated as well?
This could look like this:
class User < ActiveRecord::Base
has_one :subscription, dependent: [:update, :destroy]
end
So that when User gets updated successfully, the associated Subscription will re-save, thus invoking its update filters (i.e. before_save, before_update, after_save, after_update).
Is there an elegant way to do this? If not, what is the closest way to cleanly get to this?
Thank you!
Try this,
has_one :subscription, :dependent => destroy, :autosave => true
For more details see the documentation http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
I've reread the rails guide to associations but still am hitting my head up against the wall. I've seen some advanced questions on Stack Overflow and am not able to determine if they shed light on my problem. Perhaps I've haven't gotten my mind wrapped around this. Please show me what I'm not seeing.
I have a School.rb which has_many Events, has_many Venues. Each of the Events and Venues belongs_to School. I'm trying to link up the Venue to an Event. They are tied to the school because they have a matching school_id. The name of the school is easily applied in Event#show and Venue#show as expected. The trick is how do I craft the Event controller to use the school_id to pull the Venue's addy in the Event#show page?
My attempts keep missing so I got to thinking maybe I have to make the Event belongs_to Venue and Venue has_many Events. Is that the right thing to do?
I attempt <%= #event.venue.address %> but that fails with 'undefined method `address' for nil:NilClass'
Maybe I'm overthinking it but as I mention above, I don't know enough to ask the right question. If I was to put my query in English terms it would be "Grab the instance of Venue whose school_id matches the school_id of the current/active Event." Does that make sense? I've attempted to find something close to that in the rails guides and attempted this:
#venues = Venue.where(:school_id => #school_id)
undefined method `address' for []:ActiveRecord::Relation. The venue address is in the venue model.
Here's my school.rb:
class School < ActiveRecord::Base
has_many :events, :dependent => :destroy
has_many :venues, :dependent => :destroy
has_and_belongs_to_many :users
belongs_to :event belongs_to :venue
def self.fetch_for_name(_name)
school = self.new(:name => _name)
end
end
Here's my event.rb:
class Event < ActiveRecord::Base
resourcify
belongs_to :school
belongs_to :venue
end
Here's my Venue.rb:
class Venue < ActiveRecord::Base
resourcify
belongs_to :school
has_many :events
end
Please help me get over this baby step, sam
You need to look at this part of the guide and ensure you have has_many through relationship between venue and events here as explained in the link...
To do the exact thing you are asking, you'd create a method in Venue to query for a given event.
class Venue < ActiveRecord::Base
resourcify
belongs_to :school
has_many :events
def self.venue_for_event(event)
where("school_id = ?", event.school_id)
end
end
However, there are some questions to be asked about your models. Why does a school have many events and venues yet also belong to one event and venue? What is the problem you are trying to solve with these models? If school is the glue that holds venues and events together, than consider making it a join model in a has_many, :through relationshuip
I'm trying to get a grasp of how to work with associations in Rails, specifically, when and when not to write explicit SQL code.
In my application, I have four models, which are defined as follows:
class User < ActiveRecord::Base
has_many :comments
has_many :geographies
has_many :communities, through: :geographies
class Comment < ActiveRecord::Base
belongs_to :user
class Community < ActiveRecord::Base
has_many :geographies
has_many :users
class Geography < ActiveRecord::Base
belongs_to :user
belongs_to :community
Users can post comments, and are associated to one or more communities through the geography table (the geography table stores user_id and community_id).
I have an index action listing all comments, and I would like to filter by community. Given a comment object, I can get the user object via comment.user, but I can't chain beyond that (i.e., something like comment.user.geography(0).community doesn't work).
It seems this object chaining is a key feature of rails, but does it work with has_many :through associations? Given my example, is it possible to get the community object from the comment object by using object chaining, or would I need to write the SQL to get anything other than the user when given the comment object?
Since User is associated with multiple communities, you will need to tell ActiveRecord (or raw SQL) which community you want:
comment.user.communities #=> should give you all the communities
If you don't particularly care for getting all communities and just want to get any community
comment.user.communities.first #=> should give you the first community
But, generally you will be interested in one particular community, based on a condition.
comment.user.communities.where(name: 'Europe') #=> should give you the European community.
I don't think you need the geographies table.
Try
class Community < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :community
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
end
Then you can access a comment's user's community like
#comment.user.community