Migration has_one and has_many - ruby-on-rails

class Post < ActiveRecord::Base
has_one :owner, class_name: "User", foreign_key: "owner_id" #creator post
has_many :users #followers post
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
end
What command line I need to perform to migrate to perform these different relationships between the User and Post tables?
Thanks

Post should belong_to:owner, because the posts table has the foreign key. Also, its #users is a bit too ambiguously named, as is User#posts.
Here are your models:
class Post < ActiveRecord::Base
belongs_to :owner, class_name: 'User', inverse_of: :owned_posts # foreign_key: :owner_id will be inferred
has_many :subscriptions
has_many :followers, through: :subscriptions, source: :user, class_name: 'User', inverse_of: :followed_posts
end
class Subscription < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
class User < ActiveRecord::Base
has_many :owned_posts, class_name: 'Post', inverse_of: :owner
has_many :subscriptions
has_many :followed_posts, through: :subscriptions, source: :post, class_name: 'Post', inverse_of: :followers
end
And here are the migrations to support them:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
# ...
end
end
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.integer :owner_id
# ...
end
end
end
class CreateSubscriptions < ActiveRecord::Migration
def change
create_table :subscriptions do |t|
t.integer :post_id
t.integer :user_id
end
end
end
If it weren't for the 'ownership' relation, you could use a has_and_belongs_to_many relationship:
rename the subscriptions migration to posts_users (must be plurals in alphabetical order),
do away with its model entirely, and
have Post.has_and_belongs_to_many :users and User.has_and_belongs_to_many :posts.
In fact, you technically could do that, but having ambiguous names like that is bad practice.

Related

Create a relationship that allows a user to follow multiple types of records

I am currently working on a system that allows a user to follow different records (funds or wallets) but I am blocking on relationships.
I have three models: User, Fund and Wallet.
A user must be able to follow to multiple funds or wallets, and a fund or wallet must be able to have multiple followers.
The goal is to be able to do user.follows which would give me the list of records he follows (funds and wallets), and to do fund.followers or wallet.followers and get the list of users who follow these records.
I have created this table:
class CreateFollows < ActiveRecord::Migration[6.0]
def change
create_table :follows do |t|
t.integer :user_id
t.integer :followable_id
t.string :followable_type
t.timestamps
end
end
end
then I did:
class User < ApplicationRecord
has_many :follows, dependent: :destroy
end
class Follow < ApplicationRecord
belongs_to :follower, foreign_key: :user_id, class_name: 'User', inverse_of: :follows
belongs_to :followable, polymorphic: true
end
and this concern that I added to Fund and Wallet models:
module Followable
extend ActiveSupport::Concern
included do
has_many :followers, dependent: :destroy
end
end
I know it can't work as it is now, but I don't really understand what to put in the User model and in my concern. Can someone show me the path please?
I finally did the following:
class Follow < ApplicationRecord
belongs_to :follower, foreign_key: :user_id, class_name: 'User', inverse_of: :follows
belongs_to :followable, polymorphic: true
end
class User < ApplicationRecord
has_many :follows, dependent: :destroy
has_many :funds_followings, through: :follows, source: :followable, source_type: 'Fund'
has_many :wallets_followings, through: :follows, source: :followable, source_type: 'Wallet'
def followings
funds_followings + wallets_followings
end
end
module Followable
extend ActiveSupport::Concern
included do
has_many :follows, foreign_key: :followable_id, dependent: :destroy, inverse_of: :followable
has_many :followers, through: :follows, as: :followable
end
end
So I'm able to do everything I wanted.

Polymorphic has_and_belongs_to_many

How define a has_and_belongs_to_many polymorphic association?
Situation:
Figure that we have users, tracks, lists, etc... and all these models can be tagged and use this tag for filter.
What i'm trying to do is:
Use has_and_belongs_to_many that enable to a tag to have other objects, and that other objects also can have other tags.
So for enable a tag that belongs to more than one kind of object (users or tracks or tracklists), we need to use polymorphic relations.
That's my code on this moment:
tags migration
class CreateTags < ActiveRecord::Migration[5.2]
def change
create_table :tags do |t|
t.string :name
t.references :taggable, polymorphic: true, index: true
t.timestamps
end
create_table :assemblies_parts, id: false do |t|
t.belongs_to :assembly, index: true
t.belongs_to :part, index: true
end
end
end
tag model
class Tag < ApplicationRecord
has_and_belongs_to_many :taggable, polymorphic: true
end
user model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_and_belongs_to_many :tags, as: :taggable
has_one :top_list
has_many :tracks, through: :top_list
end
track model
class Track < ApplicationRecord
has_and_belongs_to_many :tags, as: :taggable
belongs_to :top_list
end
You can use a has_many association:
Tag model
class Tag < ApplicationRecord
has_many :relation
end
Relation model
class Relation < ApplicationRecord
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
User model
class User < ApplicationRecord
has_many :relations, as: :taggable
has_many :tags, through: :relations
has_one :top_list
has_many :tracks, through: :top_list
end
Track model
class Track < ApplicationRecord
has_many :relations, as: :taggable
has_many :tags, through: :relations
belongs_to :top_list
end
Tags migration
class CreateTags < ActiveRecord::Migration[5.2]
def change
create_table :tags do |t|
t.string :name
t.timestamps
end
create_table :relations, id: false do |t|
t.references :tags, index: true
t.references :taggable, polymorphic: true, index: true
end
end
end

Rails polymorphic comments associate

Trying to work out an polymorphic association where Comments can belong to, for example, Photos and Users. Where a Comment on a user is treated as a "direct message". But I'm getting the User association a bit messed up.
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable
end
This is incorrect. Ideally, user.comments should retrieve all Comment records where user_id == user.id and something like user.messages should retrieve all Comments where the type is User and they are the subject.
Relationships:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable, class_name: 'Comment'
end
Schema:
# Comments
...
t.integer :user_id
t.integer :commentable_id
t.string :commentable_type
...
Then you can invoke:
#user.comments # Get all comments created by particular user
#user.messages # Get all comments where particular user is a subject
have you added the foreign key and type columns to the comments table? within a migration file:
def change
add_column :comments, :commentable_type, :integer
add_column, :commentable_type, :string
add_index :comments, [:commentable_type, :commentable_id]
end
Also make sure you have a Message model and it has the associations
class Message < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :commentable, polymorphic: true
end

NoMethodError Rails 5

I am trying to allow users to bookmark various coffee shops. I have successfully managed to allow users to favorite coffee shops, so was trying to follow the same steps, but I get a NoMethodError when trying to load Coffeeshops#show.
My code:
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :validatable,
has_many :comments
has_many :coffeeshops
has_many :favorite_coffeeshops # just the 'relationships'
has_many :favorites, through: :favorite_coffeeshops, source: :coffeeshop
has_many :bookmarked_coffeeshops # just the 'relationships'
has_many :bookmarks, through: :bookmarked_coffeeshops, source: :coffeeshop
coffeeshop.rb
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
belongs_to :roaster
belongs_to :user
has_many :favorite_coffeeshops# just the 'relationships'
has_many :favorited_by, through: :favorite_coffeeshops, source: :user
has_many :bookmarked_coffeeshops# just the 'relationships'
has_many :bookmarked_by, through: :bookmarked_coffeeshops, source: :user
bookmarked_coffeeshop.rb
class BookmarkedCoffeeshop < ApplicationRecord
belongs_to :coffeeshop
belongs_to :user
end
coffeeshop.show.html.erb
<%= link_to "Bookmark", bookmarked_coffeeshops_path(#coffeeshop, type: "bookmarked"), method: :put %>
Migration
class CreateBookmarkedCoffeeshops < ActiveRecord::Migration[5.0]
def change
create_table :bookmarked_coffeeshops do |t|
t.integer :coffeeshop_id
t.integer :user_id
t.timestamps
end
end
end
Error
undefined method `bookmarked_coffeeshop_path' for #<#<Class:0x007fadb9ccad30>:0x007fadc3232e90>
Schoolboy error - I hadn't updated my routes:
Adding:
resources :coffeeshops do
put :bookmarked, on: :member
end
resolved the issue.

How do I make my task assignment associations?

I have a User model, a TodoList model, which has many todoItems. My models are :
User Model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :todo_lists
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
TodoList Model
class TodoList < ActiveRecord::Base
has_many :todo_items
belongs_to :user
end
ToItem Model
class TodoItem < ActiveRecord::Base
include AASM
belongs_to :todo_list
def completed?
!completed_at.blank?
end
#belongs_to :user
#belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
aasm :column => 'state', :whiny_transitions => false do
state :not_assigned, :initial => true
state :assigned
state :taskCompleted
end
I am trying to modify my models in such that any user can request to be assigned a taskItem and the user whom the task belongs to can accept or deny the requests. Once a an assignment request is approved, I want the task to be also associated to the user assigned to it.
How do I go about that with my model associations and relationships ? Thanks in advance for the help .
You could use an assignments association table, in a many-to-many relationship between User and TodoItem. Your association table would have an additional boolean attribute, indicating whether the item owner has accepted the request. Something like:
class TodoItem < ActiveRecord::Base
...
has_many :users, through: :assignments
...
end
For User:
class User < ActiveRecord::Base
...
has_many :todo_items, through: :assignments
...
end
And finally the association table:
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :todo_item
end
Your migration to create the association table would be something like this:
class CreateAssignments < ActiveRecord::Migration
def change
create_table :assignments do |t|
t.belongs_to :user, index: true
t.belongs_to :todo_item, index: true
t.boolean :request_accepted, default: false, null: false
t.timestamps null: false
end
end
end

Resources