I have a has_one association that is reflective on the objects of another association. I have Project, which has many ProjectUsers, which tie Projects and Users. One of those ProjectUsers is authoritative. The issue is that both the has_one and the has_many use the same foreign key on project_users. Here's the base idea of the models.
class Project < ActiveRecord::Base
has_many :project_users, :class_name => 'ProjectUser', :foreign_key => 'project_id'
has_one :authoritative_user, :class_name => 'ProjectUser', :foreign_key => 'project_id', :conditions => {:authoritative => true}
end
class ProjectUser < ActiveRecord::Base
belongs_to :project
belongs_to :user
# has a boolean column 'authoritative'
end
What I would like to be able to do is call something like.
project = Project.new
project_user = ProjectUser.new
project.project_users << project_user
project.authoritative_user = project_user
other_project_user = ProjectUser.new
project.project_users << other_project_user
project.authoriative_user = other_project_user
Where authoritative_user= would update the project user to have authoritative be set to true, and make the previous authoritative user have authoritative set to false. Another issue I am having is that second time I set authoritative_user on the project, project_id on the previous ProjectUser gets set to nil, and so it is no longer associated though the Project's project_users.
I'm not sure if I'm just doing this completely wrong, or if I'm just missing something.
class Project < ActiveRecord::Base
has_many :project_users
has_many :users, :through => :project_users
belongs_to :authoritative_project_user, :class_name => 'ProjectUser'
has_one :authoritative_user, :through :authoritative_project_user
end
class ProjectUser < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_as_authorized_user
end
then just let the has_one project_as_authorized_user relationship nil out your belongs_to authorized_project_user
Personally, I'd probably look to simplify/separate concerns. Here is an example (note: untested):
class Project < ActiveRecord::Base
has_many :project_users
has_many :users, :through => :project_users
has_one :authoritative_user
end
class ProjectUser < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
class AuthoritativeUser < ActiveRecord::Base
belongs_to :project
belongs_to :user
validates_uniqueness_of :user_id, :scope => :project_id
end
Essentially, I break out the authoritative_user attribute of your ProjectUser model into it's own. Very simple, clean and not very exciting.
You could probably build a few convenience methods like has_authoritative_user? and update_authoritative_user in your Project model.
I'm sure you'll get a few better suggestions.
Hope this helps!
Related
I have a fairly normal class structure, using a polymorphic association:
class Contact < ActiveRecord::Base
has_many :opportunities, :as => :has_opportunities, dependent: :destroy
end
class Company < ActiveRecord::Base
has_many :opportunities, :as => :has_opportunities, dependent: :destroy
end
class Opportunity < ActiveRecord::Base
belongs_to :has_opportunities, polymorphic: true
belongs_to :contact, foreign_key: 'has_opportunities_id', conditions: "opportunities.has_opportunities_type = 'Contact'"
belongs_to :company, foreign_key: 'has_opportunities_id', conditions: "opportunities.has_opportunities_type = 'Company'"
end
In Rails 4 using :conditions has been deprecated, but I can't figure out the "new" syntax required to allow access to the parent object from the child.
Edit: Yes, you can do opportunity.has_opportunities which will return you a Contact or a Company, but it is often "nicer" in code to use opportunity.contact or opportunity.company
Can't you simply set it up as a regular polymorphic association?
class Opportunity < ActiveRecord::Base
belongs_to :has_opportunities, polymorphic: true
end
I have a User model which has many projects and a Project model which can have many users, but also belongs to a single user (ie the user who created this project). It must belong to a User. It also allows a list of users to be associated with it, think collaboration.
With this in mind, my models look like this:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
end
class AssignedProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Now, when I want to create a new project through a User, this is how I would do it:
user = User.create(:name => 'injekt')
user.projects.create(:name => 'project one')
Now, I know that projects is provided through an AssignedProject join model, which is why project.user will return nil. What I'm struggling to get my head around is the best way to assign the project creator (which by the way doesn't need to be user, it could be creator or something else descriptive, as long as it is of type User).
The idea then is to create a method to return projects_created from a User which will select only projects created by this user. Where user.projects will of course return ALL projects a user is associated with.
Assuming this kind of association is fairly common, what's the best way to achieve what I want? Any direction is greatly appreciated.
Add a creator_id column to your projects table for the creator relationship, and then add the associations to the models:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
I wanted to add little improvement to design. We don't actually need intermediate model because it does not contain any extra column other than reference_ids hence HABTM association is best suited over here.
class User < ActiveRecord::Base
has_and_belongs_to_many :projects, :join_table => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
Right now I have a rich many-to-many association with VideoVote as the independent record.
class VideoVote < ActiveRecord::Base
belongs_to :user
belongs_to :video
end
class User < ActiveRecord::Base
has_many :video_votes
has_many :voted_videos,
:through => :video_votes,
:source => :video
end
class Video < ActiveRecord::Base
has_many :video_votes
has_many :voted_users,
:through => :video_votes,
:source => :user
end
However, I want to trasform this into a polymorphic association where comments can also have many VideoVotes (I realize this is confusing, so I should probably change it to Votes). (also, a video will have many comments.) How should I do this?
You first want to add voteable_id:integer and voteable_type:string to your video_votes table.
Then your models will look like:
class VideoVote < ActiveRecord::Base
belongs_to :voteable, :polymorphic => true
end
class Comment < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
class Video < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
Then you can access them just like any other has_many:
#video.video_votes
#comment.video_votes
#etc.
I've got a Project model, and a Contact model. The Project model has an owner and a client, both of which are Contacts. I've obviously got something ambiguous going on, because if I have a contact and ask for its projects, Rails won't know whether I'm asking for it's projects where it's the client or where it's the owner. So far I've got this:
class Contact < ActiveRecord::Base
has_many :projects
end
class Project < ActiveRecord::Base
belongs_to :owner, :class_name => 'Contact', :foreign_key => 'owner_id'
belongs_to :client, :class_name => 'Contact', :foreign_key => 'client_id'
end
How do I make two relationships here?
Its similar to the way belongs_to is defined in the other class.
So Basically
class Contact < ActiveRecord::Base
has_many :projects_owned, :class_name => "Project", :foreign_key => "owner_id"
has_many :projects_as_client, :class_name => "Project", :foreign_key => "client_id"
end
names of associations could be better. The Single Table inheritance approach described before me is also a neat way, but go for it if you have a lot of different behaviour for each of the OwnerContact and ClientContact class, otherwise it might be just a useless overhead.
I think here's should be polymorphic association, something like this
class Owner < ActiveRecord::Base
has_many :projects, :as => :person
end
class Client < ActiveRecord::Base
has_many :projects, :as => :person
end
class Project < ActiveRecord::Base
belongs_to :person, :polymorphic => true
end
Now you can retrieve projects by #client.projects or #owner.projects. If you want to get person from #project you should add to Project migration this:
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.references :person, :polymorphic => true
t.timestamps
end
end
...
You should try to use a single table inheritance on the Contact table. All you need to do for this to work is to implement a 'type' column (string). Rails will handle the rest
class Contact < ActiveRecord::Base
# implement a type column
has_many :projects
end
class OwnerContact < Contact
end
class ClientContact < Contact
end
class Project < ActiveRecord::Base
belongs_to :owner, :class_name => 'OwnerContact'
belongs_to :client, :class_name => 'ClientContact'
end
I am new to rails, and have a situation that I can't quite get my head around.
Lets say I have two resources, users and widgets.
Users can use widgets, but widgets are also user created, and should be owned by the user that created them. There needs to be a user uses widget, and a user owns widget. Is the following what I am looking for?
Class User < ActiveRecord::Base
has_many :uses
has_many :widgets, :through => :uses
has_many :owns
has_many :widgets, :through => :owns
end
Class Widget < ActiveRecord::Base
has_one :own
has_many :uses
has_many :users, :through => :uses
end
Class Use < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
Class Own < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
I would do it slightly different.
Class User < ActiveRecord::Base
has_many :uses
has_many :widgets, :through => :uses
has_many :owned_widgets, :class_name => "Widget"
end
Class Widget < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :uses
has_many :users, :through => :uses
end
Class Use < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
I changed the names a bit, you had a name conflict on widget, you cannot have two associations with the same name. I also removed the has_one and just set an owner which will have a foreign_key of owner_id with the class set to User. Other than that, you set the many-to-many relationship up nicely.