The goal is for a shop to create rewards and associate each reward to a follower of his choice. This is my setup:
class Shop < ApplicationRecord
has_many :rewards
has_many :follows
has_many :users, through: :follows
end
class Reward < ApplicationRecord
belongs_to :shop
end
class Follow < ApplicationRecord
belongs_to :shop
belongs_to :user
has_many :reward_participant
end
class User < ApplicationRecord
has_many :follows
has_many :shops, through: :follows
end
I created this model in order to capture the reward and follower association.
class RewardParticipant < ApplicationRecord
belongs_to :reward
belongs_to :follow
end
And I have created the following migrations:
class CreateRewards < ActiveRecord::Migration[6.0]
def change
create_table :rewards do |t|
t.string :title
t.text :body
t.date :expires
t.integer :shope_id
t.timestamps
end
end
end
class CreateRewardParticipants < ActiveRecord::Migration[6.0]
def change
create_table :reward_participants do |t|
t.integer :reward_id
t.integer :follow_id
t.timestamps
end
end
end
I'm having trouble figuring out if this is the correct approach to the model associations and migrations. Thanks for the help in advance!
Generally you are right.
We want users to follow a shop, and a shop can create rewards and grant many rewards to many followers.
1. Visual schema:
2. Model associations (complete version)
user.rb
has_many :follows
has_many :reward_follows, through: :follows
has_many :rewards, through: :reward_follows # NOT through shops
has_many :shops, through: :follows
follow.rb
belongs_to :user
belongs_to :shop
has_many :reward_follows
shop.rb
has_many :rewards
has_many :reward_follows, through: :rewards # NOT through follows
has_many :follows
has_many :users, through: :follows
reward.rb
has_many :reward_follows
belongs_to :shop
has_many :follows, through: :reward_follows
has_many :users, through: :follows
3. Do not use date field. Use datetime field.
Justification: https://www.ruby-forum.com/t/time-without-date/194146
This personally saved me hours of work long-term.
Related
I'm trying to figure out how to setup the following. A user can create a review and then like his review or like other reviews if he wants to. I came up with the following setup:
class Like < ApplicationRecord
belongs_to :user
belongs_to :review
end
class User < ApplicationRecord
has_many :reviews
has_many :likes
has_many :liked_reviews, through: :likes, source: :review
end
class Review < ApplicationRecord
belongs_to :user
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
class CreateLikes < ActiveRecord::Migration[6.0]
def change
create_table :likes do |t|
t.references :user, index: true
t.references :likes, index: true
t.integer :review_id
t.timestamps
end
end
end
I'm pretty sure that the model associations I came up with are correct, but I'm not so sure about the like table. Can someone please review the above and tell me if the model associations and like table are correct? Thanks in advance!
In my initial Rails 4 app, I had the following models:
User
has_many :administrations
has_many :calendars, through: :administrations
has_many :comments
Calendar
has_many :administrations
has_many :users, through: :administrations
has_many :posts
has_many :comments, through: :posts
Administration
belongs_to :user
belongs_to :calendar
Post
belongs_to :calendar
has_many :comments
Comment
belongs_to :post
belongs_to :user
I just added a new Ad model to the app:
Ad
belongs_to :calendar
And now I would like to allow users to write comments about the ad records.
Can I use my existing Comment model and do something like:
Ad
belongs_to :calendar
has_many :comments
Comment
belongs_to :post
belongs_to :user
Or do I need to create a distinct "Comment" model, that I would call for instance AdComments or Feedback?
You need to use polymorphic associations. Something on the lines of this:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Ad < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Product < ActiveRecord::Base
has_many :comments, as: :commentable
end
And the migration would look like:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.references :commentable, polymorphic: true, index: true
t.timestamps null: false
end
end
end
I guess you already have the comments table, so you should rather change the table with
class ChangeComments < ActiveRecord::Migration
def change
change_table :comments do |t|
t.rename :post_id, :commentable_id
t.string :commentable_type, null: false
end
end
end
Also beware, that if you have live data you should update the commentable_type field of all already existing comments to Post. You can either do it in a migration or from the console.
Comment.update_all commentable_type: 'Post'
We don't need to use any new model, you can just refactor the current Comment model with polymorphic
So, a comment always belongs to a user, and belongs to a post or ad
I have this relationship between users, teams
class CreateTeamsUsers < ActiveRecord::Migration
def change
create_table :teams_users, :id => false do |t|
t.references :user
t.references :team
t.timestamps
end
end
end
class User < ActiveRecord::Base
has_and_belongs_to_many :teams
end
class Team < ActiveRecord::Base
has_and_belongs_to_many :users
end
The issue is that I want to add extra attribute in HABTM,attribute name is "user_name"
How to do this?
Instead of HABTM you'd use has_many and has_many :through.
class User < ActiveRecord::Base
has_many :memberships
has_many :team, through: :membership
end
class Membership < ActiveRecord::Base # This would be your old 'join table', now a full model
belongs_to :user
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
Short version, you can't do what your're trying to do without a little refactoring. Here is how I would do it (apologies if there's syntax issues, I'm doing this from memory I haven't tested the code but the principle is sound)
Create a new model to represent "membership" of a team (maybe call it "Membership") and the associated migration to create the table:
class Membership
belongs_to :team
belongs_to :user
end
Then change your team and user models to use this new model:
class User
has_many :memberships
has_many :teams, through: :memberships
end
class Team
has_many :memberships
has_many :users, through: :memberships
end
Once you've refactored this far, adding additional columns / attributes to "memberships" is easy because you can just treat it like any other model.
I am trying to figure out how to best do my model associations. I have 3 tables.
players, teams, teamplayers
my outline is that a player can belong to multiple teams. a team can have multiple players, but then in my teamplayers table I have 2 fields teamid and playerid(not counting the primary key id).
For example:
Player has id of 1000
team has id of 501
In the teamplayers table that would be stored as:
team_id player_id
501 1000
so how would I design the relations belongs_to and has_many in my models?
You can achieve this as given below :
class Team < ActiveRecord::Base
has_many :team_players
has_many :players, :through => :team_players
end
class Player < ActiveRecord::Base
has_many :team_players
has_many :teams, :through => :team_players
end
class TeamPlayer < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
The corresponding migration might look like this:
class CreateTeamPlayers < ActiveRecord::Migration
def change
create_table :teams do |t|
t.string :name
t.timestamps
end
create_table :players do |t|
t.string :name
t.timestamps
end
create_table :team_players do |t|
t.belongs_to :player
t.belongs_to :team
t.timestamps
end
end
end
Then if you want to fetch players of a particular team, just do
#team = Team.first (do as per your requirement)
#team.players
Also, to fetch a player's teams,
#player = Player.first (do as per your requirement)
#player.teams
H2H :)-
Following should do:
class Player < ActiveRecord::Base
has_many :team_players
has_many :teams, through: :team_players
end
class TeamPlayer < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :team_players
has_many :players, through: :team_players
end
you want the has_many through association.
class Player < ActiveRecord::Base
has_many :teamplayers
has_many :teams, through: :teamplayers
end
class TeamPlayer < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :teamplayers
has_many :players, through: :teamplayers
end
http://guides.rubyonrails.org/association_basics.html
This is possible in two way:
first is:
class Team < ActiveRecord::Base
has_and_belongs_to_many :players
end
class Player < ActiveRecord::Base
has_and_belongs_to_many :teams
end
second is:
class Player < ActiveRecord::Base
has_many :teamplayers
has_many :teams, through: :teamplayers
end
class TeamPlayer < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :teamplayers
has_many :players, through: :teamplayers
end
I have to following relationship:
class Course < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :courses
end
Then I have the following table:
create_table :courses_users, :force => true, :id => false do |t|
t.integer :user_id
t.integer :course_id
t.integer :middle_value
end
How can I access (edit/update) the middle value in the many to many record?
HABTM should be used to only store the relation. If you have any fields you want to store in the relation, you should create another model, eg. CourseSignup. You would then use this model to create a has_many :through => :course_signups relation, so your models would look like this:
class Course < ActiveRecord::Base
has_many :course_signups
has_many :users, :through => :course_signups
end
class CourseSingup < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
class User < ActiveRecord::Base
has_many :course_signups
has_many :courses, :through => :course_signups
end
Then you can add your middle_value in the CourseSignup model.
You can find more details in the guide to ActiveRecord associations.
You want a has_many :though, not a HABTM.
A HABTM does not have a join model, but a has_many :through does. Something like:
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
end