Модель и миграция для приложения Rails, которое позволяет пользователям отслеживать / подписываться на сообщения с определенными тегами (как в SO)? - PullRequest
1 голос
/ 02 марта 2012

У меня есть тег , Пользователь и Пост модель:

tag.rb:

class Tag < ActiveRecord::Base
  has_many :taggings, :dependent => :destroy  
  has_many :posts, :through => :taggings
end

tagging.rb:

class Tagging < ActiveRecord::Base
  belongs_to :post  
  belongs_to :tag
end

(Между сообщениями и тегами существует многозначная связь)

user.rb:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :omniauthable


  has_many :posts, :dependent => :destroy
end

post.rb:

class Post < ActiveRecord::Base
  attr_accessible :title, :content, :tag_name

  attr_accessor :tag_name

  validates :title,   :presence => true,
                      :length   => { :maximum => 30 },
                      :uniqueness => true
  validates :content, :presence => true,
                      :uniqueness => true

  belongs_to :user

  has_many :taggings, :dependent => :destroy
  has_many :tags, :through => :taggings

  attr_writer :tag_names
  after_save :assign_tags

  def tag_names
    @tag_names || tags.map(&:name).join(' ')
  end

  private

  def assign_tags
    if @tag_names
      self.tags = @tag_names.split(" ").map do |name|
      Tag.find_or_create_by_name(name)
    end
  end
 end
end

Сейчас зарегистрированный пользователь (через Devise ) видит все сообщения:

posts_controller.rb:

class PostsController < ApplicationController
  before_filter :authenticate_user!, :except => [:show, :index]
  autocomplete :tag, :name

  def index
    @title = "Posts"
    @posts = Post.paginate(:page => params[:page], :per_page => 5)
  end

views / posts / index.html.erb:

<div id="mainbar" class="nine columns">

  <% @posts.each do |post| %>
    <div id="post-<%= post.id %>" class="post">
      <h3><%= link_to post.title, post %></h3>

      <div class="post_content">
        <p><%= post.content %></p>
      </div>

  <%= will_paginate @posts %>

Я хочу, чтобы пользователи могли следить за тегами и подписываться на них, чтобы они могли видеть сообщения только с этими тегами на странице индекса.

Каким было бы модель и миграция для этого?

РЕДАКТИРОВАТЬ:

Теперь все работает нормально, но я получаю эту ошибку при входе на страницу индекса:

ActionView::Template::Error (undefined method `id' for []:ActiveRecord::Relation):
    2:   <h2><%= @title %></h2>
    3: 
    4:   <% @posts.each do |post| %>
    5:     <div id="post-<%= post.id %>" class="post">
    6:       <h3><%= link_to post.title, post %></h3>
    7: 
    8:       <div class="post-meta">
  app/views/posts/index.html.erb:5:in `block in _app_views_posts_index_html_erb__75789648_88240890'
  app/views/posts/index.html.erb:4:in `each'
  app/views/posts/index.html.erb:4:in `_app_views_posts_index_html_erb__75789648_88240890'

представление индекса для сообщений:

<div id="mainbar" class="nine columns">
  <h2><%= @title %></h2>

  <% @posts.each do |post| %>
    <div id="post-<%= post.id %>" class="post">
      <h3><%= link_to post.title, post %></h3>

      <div class="post-meta">
        <span><%= link_to post.user.username, post.user %></span>
        <span>Created At: <%= post.created_at %></span>
      </div>

      <div class="post-content">
        <p><%= post.content %></p>
      </div>

      <p>Votes: <%= post.total_votes %></p>
      <p>Comments: <%= post.comments_count %></p>
      <ul>
        <li><%= link_to 'Show', post %></li>
        <li><%= link_to 'Edit', edit_post_path(post) %></li>
        <li><%= link_to 'Destroy', post, confirm: 'Are you sure?', method: :delete %></li>
      </ul>
      <br />
    </div>
  <% end %>

  <%= will_paginate @posts %>
  <p><%= link_to 'New post', new_post_path, :class => 'sunset-orange button radius' %></p>
</div>

<div id="sidebar" class="three column">
  <%= render 'layouts/sidebar' %>
</div>

1 Ответ

3 голосов
/ 02 марта 2012

Поскольку пользователь может подписаться на множество тегов, а тег может подписываться многими пользователями, вам необходимо связать пользователя и тег с помощью has_many связи (либо через has_and_belongs_to_many, либо has_many :through, аналогично моделям Tag и Post.'association).

class User < ActiveRecord::Base
  has_many :subscriptions
  has_many :subscribed_tags, :source => :tag, :through => :subscriptions

class Tag < ActiveRecord::Base
  has_many :subscriptions
  has_many :subscribed_users, :source => :user, :through => :subscriptions

class Subscription < ActiveRecord::Base
  belongs_to :tag  
  belongs_to :user

Затем в PostsController выберите все сообщения с тегами, на которые подписан пользователь.

def index
  @posts = current_user.subscribed_tags.map(&:posts).flatten.paginate(:page => params[:page], :per_page => 5)
end

Если вы используете Уилл-пагинацию для разбиения на страницы, вы можете получитьошибка undefined method 'paginate' for Arrays, так как использование map приведет к массиву.Поэтому вам может понадобиться добавить require 'will_paginate/array' к соответствующему контроллеру.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...