Looking for advice on how to setup a relationship.
I'm trying to figure out the best way to setup relationships between models which allows users to publish saved content from multiple models.
ExampIe:
I have a Post model which allows users to post and schedule content from other models such as Articles & Reviews.
So it looks something like this:
Post
t.datetime "scheduled_at"
t.string "state"
t.text "error"
t.boolean "facebook_wall_post"
t.boolean "facebook_page_post"
t.boolean "twitter"
t.boolean "linkedin"
Article
t.string "title"
t.text "body"
t.string "authror"
Review:
t.string "title"
t.text "body"
t.string "rating"
So a User might create a new Article and then optionally choose to post it to Facebook or they may go to /post/new
and select the Article from a select field. The can also choose from multiple networks i.e. facebook, twitter etc.
So far I have something like:
class Post < ApplicationRecord
belongs_to :user
has_many :articles
has_many :reviews
end
class Article < ApplicationRecord
belongs_to :user
belongs_to :post
end
class Review < ApplicationRecord
belongs_to :user
belongs_to :post
end
How would you setup this sort of relationship?
Hey Morgan,
I don't think there's anything inherently wrong with your setup. You may play with a few query scenarios in your console to make sure you're able to access all the objects as you'd expect.
How would you setup this sort of relationship?
If I were doing this, I think I would reverse the associations of Article
and Post
. Since a Review
and Post
can't exist without an Article
, I think the Article
needs to be the main driving model.
class Article < ApplicationRecord
belongs_to :user
has_many :reviews
has_one :post
end
class Post < ApplicationRecord
belongs_to :user
belongs_to :article
end
class Review < ApplicationRecord
belongs_to :user
belongs_to :article
end
I changed the association between Article
and Post
to a has_one
. There's really no need for an Article
to have multiple Post
since a single Post
object should be sufficient for you to manage the desired behavior.
You'll of course have to update your migrations:
Article
t.string "title"
t.text "body"
t.string "author"
Post
t.references "article", index: true, foreign_key: true
t.datetime "scheduled_at"
t.string "state"
t.text "error"
t.boolean "facebook_wall_post"
t.boolean "facebook_page_post"
t.boolean "twitter"
t.boolean "linkedin"
Review
t.references "article", index: true, foreign_key: true
t.string "title"
t.text "body"
t.string "rating"
Thanks for the detailed reply Jacob!
Just to be clear, Article and Review are content models that belong to a User and don't necessarily have to be shared via Post which means they can exist independently without being associated to a Post.
Basically, all the Post model does is post and schedule content to various sites so it might look like this:
User creates a:
Video -> Post ->
YouTube
Article -> Post ->
Facebook feed
Review --> Post ->
Twitter, Facebook & YouTube
SomeOtherModel -> Post ->
Some other network
The reason I'm using a separate (perhaps confusingly named) Post model is that it also handles scheduling via background jobs so it has some extra fields to track errors and status etc.
Would your suggestion still apply in this case?
Ah, your Post
model sounds like a good candidate for a polymorphic association.
Article
class Article < ApplicationRecord
belongs_to :user
has_many :reviews
has_many :posts, as: :postable
end
Post
class Post < ApplicationRecord
belongs_to :postable, polymorphic: true
belongs_to :user
end
Then all your other models that are postable
would be setup like your Article
model is with has_many :posts, as: :postable
Your a legend!
It appeasr to be all working now except I'm unnsure how to set the user_id
on Post
Post Model
class Post < ApplicationRecord
belongs_to :postable, polymorphic: true
belongs_to :user
end
Article Model
class Article < ApplicationRecord
belongs_to :user
has_many :posts, as: :postable
end
Review Model
class Review < ApplicationRecord
belongs_to :user
has_many :posts, as: :postable
end
Post Controller
class PostsController < ApplicationController
def create
# @post = current_user.posts.build(posts_params) << How do I do this now?
@parent = parent
@post = @parent.posts.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to posts_path, notice: "Post successfully created!"}
else
format.html { render :new }
end
end
end
private
def parent
Article.find(post_params[:article_id]) if post_params[:article_id]
Review.find(post_params[:review_id]) if post_params[:review_id]
end
end
Just save it before redirecting
def create
# @post = current_user.posts.build(posts_params) << How do I do this now?
@parent = parent
@post = @parent.posts.new(post_params)
@post.user_id = current_user.id
respond_to do |format|
if @post.save
format.html { redirect_to posts_path, notice: "Post successfully created!"}
else
format.html { render :new }
end
end
end