Ask A Question

Notifications

You’re not receiving notifications from this thread.

Looking for advice on how to setup a relationship.

Morgan asked in Databases

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?

Reply

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"
Reply

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?

Reply

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

Reply

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
Reply

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
Reply

I'm an idiot.

Thanks for all your help Jacob!

Reply

You bet! :)

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 86,946+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.