I generated namespaced models and how can i set many-to-many
relationships, category has many posts, post has many categories
rails g model Blog::Post body:text, title:string
rails g model Blog::Category title:string
rails g model Blog::CategoryPost post_id:integer, category_id:integer
and my models looks like
class Blog::Category < ActiveRecord::Base
attr_accessible :title
has_many :posts, :class_name => 'Blog::Post', :through => :blog_categories_posts
end
class Blog::CategoryPost < ActiveRecord::Base
belongs_to :category, :class_name => 'Blog::Category'
belongs_to :post, :class_name => 'Blog::Post'
end
class Blog::Post < ActiveRecord::Base
attr_accessible :body, :title
has_many :categories, :class_name => 'Blog::Category', :through => :blog_categories_posts
end
This should work. You need to specify relation to intermediate table.
class Blog::Category < ActiveRecord::Base
attr_accessible :title
has_many :categories_posts, :class_name => 'Blog::CategoryPost'
has_many :posts, :class_name => 'Blog::Post', :through => :categories_posts
end
class Blog::CategoryPost < ActiveRecord::Base
belongs_to :category, :class_name => 'Blog::Category'
belongs_to :post, :class_name => 'Blog::Post'
end
class Blog::Post < ActiveRecord::Base
attr_accessible :body, :title
has_many :categories_posts, :class_name => 'Blog::CategoryPost'
has_many :categories, :class_name => 'Blog::Category', :through => :categories_posts
end
Try adding the associations to the CategoryPosts to the Category and Post models. eg:
class Blog::Category < ActiveRecord::Base
...
has_many :blog_category_posts, :class_name => "Blog::CategoryPost"
...
end
I believe you need to do this for both the Category and the Post models.
Related
I am trying to transform my HABTM to has_many through relations. Sometimes I have to connect the same models in different ways. For example to specify different roles for authors.
With a HABTM I would do this through the declaration of a class_name option. Just like:-
class Project < ActiveRecord::Base
has_and_belongs_to_many :curators, :class_name => :author, :through => :projects_curators
end
class ProjectsCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_and_belongs_to_many :projects, :through => :projects_curators
end
But when I transform everything into a has_many through:
class Project < ActiveRecord::Base
has_many :project_curators
has_many :curators, :class_name => :author, :through => :project_curators
end
class ProjectCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :project_curators
has_many :projects, :through => :project_curators
end
I get: Could not find the source association(s) :curator or :curators in model ProjectCurator. Try 'has_many :curators, :through => :project_curators, :source => <name>'. Is it one of :author or :project?
When I add :source
has_many :curators, :class_name => :author, :through => :project_curators, :source => :author
I get:
uninitialized constant Project::author
How can I get this working? Thank you very much in advance!
Understanding :source option of has_one/has_many through of Rails should help you out
When you declare the source you're not declaring the class of the has_many relationship, but the name of the relationship you're using as the middle man. In your case, try:
has_many :curators, :through => :project_curators, :source => :author
As someone in the above post states:
Most simple answer - the name of the relationship in the middle
I have an app with Two Models Stadium & Team, they have a many-to-many relationship and are joined in the middle by a join table.
My current structure looks like this:
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :stadiums, :through => :stadiumteams
end
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
This relationship show “current residents for a stadium”. But now I also want to have a relationship that can display old residents.
For example Tottenham (Team) used to play at White Hart Lane (Stadium) But now play at (Wembley) Stadium.
I am planning to do the following. But I am unsure if this will work or if there is a better way to do it?
Team
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :stadiums, :through => :stadiumteams
#has_many :oldstadiums, :class_name => 'OldStadium'
#has_many :stadiums, :through => :oldstadiums
end
Stadium
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
#has_many :oldstadiums, :class_name => 'OldStadium'
#has_many :teams, :through => :oldstadiums
end
New Join Table
class OldStadium < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
Any help is very appreciated!
EDIT: (Input fields)
Here's how the code for my inputs currently looks:
<%= f.association :teams, label: 'Current Residents', class:'select2-field', placeholder: "Select teams", collection: #teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.'%>
<%= f.association :teams, label: 'Old Residents', class:'select2-field', placeholder: "Select teams", collection: #teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.' %>
And my params
def stadium_params
params.require(:stadium).permit(:name, :capacity, :city, :country, :location_name, :address, :longitude, :latitude, :image, :surface, :official_opening_date, :cost, :web_url, :also_known_as, :record_attendance, :team_ids => [])
end
You can define relations with a lambda to append an additional where clause in the relation:
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many stadiums, :through => :stadiumteams
has_many :current_stadiums, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Stadium', :source => :stadium
has_many :previous_stadiums, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Stadium', :source => :stadium
end
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
has_many :current_teams, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Team', :source => :team
has_many :previous_teams, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Team'. :source => :team
end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
And then you should be able to do (in rails console):
team = Team.first
stadium = Stadium.first
team.previous_stadiums <<
team.save
team.reload.previous_stadiums.include?(stadium)
# => should return true
So this way you can simply use in your UI a input for the association :previous_teams and :current_teams:
f.association :current_teams, #...
There are several ways to fulfill your requirement. Your approach also enough, but for efficiency, since the number of Stadium is small ( thousands ), you can use STI for Stadium entity.
class Stadium < ActiveRecord::
has_many :stadiums_teams
has_many :teams, through: :stadiums_teams
end
class OldStadium < Stadium; end
class RecentStadium < Stadium; end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :stadiums_teams
has_many :stadiums, through: :stadiums_teams, source: :stadium
has_many :old_stadiums, through: :stadiums_teams,
source: :stadium, class_name: OldStadium.name
has_many :recent_stadiums, through: :stadiums_teams,
source: :stadium, class_name: RecentStadium.name
end
So, in most case, you will work with OldStadium / RecentStadium model, but when you want to check if a team ever played at a specific stadium or not, just use Stadium model.
I'm currently modeling a Rails 3.2 app and I need a polymorphic association named "archivable" in a table named "archives". No worries with it, but my "archives" table must also belongs_to a "connections" table. I just want to know if there's any constraints from Rails to do that.
You can see the model here
Another detail, my Connection model has twice user_id as foreign key. A user_id as sender and a user_is as receiver. Possible? I think what I did below won't work...
Here are my models associations.
class User < ActiveRecord::Base
has_many :connections, :foreign_key => :sender
has_many :connections, :foreign_key => :receiver
end
class Connections < ActiveRecord::Base
belongs_to :user
has_many :archives
end
class Archive < ActiveRecord::Base
belongs_to :connection
belongs_to :archivable, :polymorphic => true
end
class Wink < ActiveRecord::Base
has_many :archives, :as => :archivable
end
class Game < ActiveRecord::Base
has_many :archives, :as => :archivable
end
class Message < ActiveRecord::Base
has_many :archives, :as => :archivable
end
Do you see anything wrong or something not doable with Rails?
Thank you guys.
I think you want to do this :
class Connections
belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
belongs_to :receiver, :class_name => 'User', :foreign_key => 'receiver_id'
end
class User
has_many :sended_connections, :class_name => 'Connection', :as => :sender
has_many :received_connections, :class_name => 'Connection', :as => :receiver
end
Important : Don't declare 2 times has_many :connections with the same name !
In my app I have 3 models: Item, Category and Categorization defined as below:
class Item < ActiveRecord::Base
attr_accessible :name, :description
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Category < ActiveRecord::Base
attr_accessible :name, :description, :parent, :children, :items, :parent_id
has_many :children, :class_name => "Category", :foreign_key => "parent_id", :dependent => :nullify
belongs_to :parent, :class_name => "Category"
has_many :categorizations
has_many :items, :through => :categorizations
end
class Categorization < ActiveRecord::Base
attr_accessible :category, :item
belongs_to :category
belongs_to :item
end
However, doing this:
Category.where(:parent_id => self.id).includes(:items)
won't return me the items associated to the category. What am I missing here?
Try this:
Category.where(parent_id: id).joins(:items)
Try this:
Category.where(:parent_id => id).collect(&:items)
It will return the items of all categories matching the where clause.
I have an app with country preferences. A user will have 2 types of country preferences - event and research. In the future there may be more. I was leaning more towards having 2 tables to represent this over using STI. I'm having a bit of trouble configuring Rails elegantly to do this. I could hack it but I would rather do this by Rails convention. What I want is something like this:
class User < ActiveRecord::Base
has_many event_countries, :through => :event_countries, :class_name => 'Country'
has_many research_countries, :through => :research_countries, :class_name => 'Country'
end
class EventCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class ResearchCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
...
end
This doesn't work though. Given this "pseudo code" does anyone know how to actually implement this in Rails?
I think you're going about declaring them wrong, because this should work properly. That's what the :through directive is for:
class User < ActiveRecord::Base
has_many :event_countries
has_many :countries_with_events,
:through => :event_countries,
:source => :country
has_many :research_countries
has_many :countries_with_researches,
:through => :research_countries,
:source => :country
end
class EventCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class ResearchCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
# ...
end
A lot of the awkwardness comes from the labels you've chosen for the tables. Although they'd seem reasonable at first glance, the way you use them ends up making them difficult.
You might want to call research_countries something like user_research_countries so that the relationship name can be user.research_countries as the :through:
class User < ActiveRecord::Base
has_many :user_event_countries
has_many :event_countries,
:through => :user_event_countries,
:source => :country
has_many :user_research_countries
has_many :research_countries,
:through => :user_research_countries,
:source => :country
end
class UserEventCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class UserResearchCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
# ...
end
You can refactor this even further by adding a field to the user-country association table that includes one or more flags, which in this case would be research or event or whatever you require later:
class User < ActiveRecord::Base
has_many :user_countries
has_many :event_countries,
:through => :user_countries,
:source => :country,
:conditions => { :event => true }
has_many :research_countries,
:through => :user_countries,
:source => :country,
:conditions => { :research => true }
end
class UserCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
# * column :event, :boolean
# * column :research, :boolean
end
class Country < ActiveRecord::Base
# ...
end