【问题标题】:How implement the Has Many Through Association on Controllers and methods如何在控制器和方法上实现 Has Many Through 关联
【发布时间】:2017-12-26 18:56:32
【问题描述】:

在我的应用程序中,我正在管理员中创建类别(因此管理员创建、更新和销毁类别名称),然后当用户创建他们将选择的帖子时(或者我正在考虑使用复选框进行切换)帖子的类别。

我决定用一个有很多的帖子和类别来做这个实现。但我对工具有疑问:

  1. post_params;
  2. 添加类别然后销毁类别的方法
  3. 以及创建、更新和销毁帖子的部分。

我该如何实现呢?这是一种更好的方法吗?因此,如果有人帮助我,我将不胜感激。

Post.rb

  class Post < ActiveRecord::Base
       has_many :categorizations
       has_many :categories, through: :categorizations
    ...
      def add_category(category)
        categorizations.create(category_id: category.id)
      end

      def remove_category(category)
        categorizations.find_by(category_id: category.id).destroy
      end

category.rb

class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :posts, through: :categorizations

  validates :name,
                  presence: true,
                  length: { minimum: 3, maximum: 30 },
                  uniqueness: true
end

分类.rb

class Categorization < ActiveRecord::Base
  belongs_to :post
  belongs_to :category

  validates :post_id, presence: true
  validates :category_id, presence: true
end

控制器/管理员/PostsController

class Admin::PostsController < Admin::ApplicationController
  def new
    @post = Post.new
    @categories = Category.all.map{ |c| [c.name, c.id]}
  end

  def create
    @post = Post.new(post_params)
    @post.author = current_user
    @post.categories << params[:category_id]

    if @post.save
      flash[:notice] = "Post has been created."
      redirect_to @post
    else
      flash[:alert] = "Post has not been created."
      render "new"
    end
  end



  def destroy
    @post = Post.find(params[:id])
    @post.destroy

    flash[:notice] = "Post has been deleted."
    redirect_to posts_path
  end

  private

  def post_params
    params.require(:post).permit(:title,
                                 :subtitle,
                                 :content,
                                 :attachment,
                                 :attachment_cache,
                                 :remote_attachment_url,
                                 :categorizations_attributes => [:id,
                                                                 :post_id,
                                                                 :category_id,
                                                                 :_destroy,
                                                                 :categories_attributes => [:id,
                                                                                            :name]
                                                                ]
                                 )
  end
end

控制器/posts_controller.rb

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update]

  def index
    @posts = policy_scope(Post)
  end

  def show
    authorize @post, :show?
  end

  def edit
    authorize @post, :update?
  end

  def update
    authorize @post, :update?
    if @post.update(post_params)
      flash[:notice] = "Post has been updated."
      redirect_to @post
    else
      flash.now[:alert] = "Post has not been updated."
      render "edit"
    end
  end

  private

  def set_post
    @post = Post.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    flash[:alert] = "The post you were looking for could not be found."
    redirect_to posts_path
  end

  def set_category
    @category = Category.find(params[:category_id])
  end

  def post_params
    params.require(:post).permit(:title, :subtitle, :content, :attachment, :attachment_cache, :remove_attachment, :remote_attachment_url, :category_id)
  end
end

posts/_form.html.slim

= simple_form_for([:admin, @post], :html => { :multipart => true }) do |f|
   = select_tag(:category_id, options_for_select(@categories), :prompt => "Select ad Category")

路线

 Rails.application.routes.draw do

  namespace :admin do
    root 'application#index'

    resources :posts, only: [:new, :create, :destroy]
    resources :categories

    resources :users do
      member do
        patch :archive
      end
    end
  end

  devise_for :users

  root "posts#index"

  resources :posts, only: [:index, :show, :edit, :update]
end

【问题讨论】:

    标签: ruby-on-rails simple-form has-many-through simple-form-for


    【解决方案1】:

    在你的表单中

    = select_tag(:category_ids, options_for_select(@categories), :prompt => "Select ad Category", multiple: true)
    

    在你的控制器中

    params.require(:post).permit(:title, :subtitle, :content, :attachment, :attachment_cache, :remove_attachment, :remote_attachment_url, :category_ids)
    

    还需要给一些建议,在你的posts_controller.rb 删除下一行

    rescue ActiveRecord::RecordNotFound
    

    Insterad 将它写在 application_controller.rb 中,因此它适用于整个应用程序。

    【讨论】:

    • 您好 Vishal,感谢您的回复。我试试这个,但代码不完整。我认为是在分类中添加类别的部分。我正在尝试这样做:@post.categories &lt;&lt; params[:category_id],但不起作用。但我在Post.rb 上做了一个名为add_category 的方法。我认为这是某种方式,但我并不完全有信心。你能帮我解决这个问题吗?
    【解决方案2】:

    我所做的修复是放在 PostsController 上的:

    class Admin::PostsController < Admin::ApplicationController
      before_action :set_categories, only: [:new, :create]
       .
       .
       .
      private
    
      def set_categories
        @categories = Category.all.select(:id, :name)
      end
    
      def post_params
        params.require(:post).permit(:title,
                                     :subtitle,
                                     :content,
                                     :attachment,
                                     :attachment_cache,
                                     :remote_attachment_url,
                                     category_ids:[]
                                     )
      end
    

    在我更改为使用复选框的表单上,我可以为帖子选择和添加更多类别:

       = f.association :categories, label: "Select the Categories", as: :check_boxes , collection: @categories.map{|c| [c.name, c.id]}, include_hidden: false
    

    添加类 Post 和 Category a dependent: :destroy 以正确销毁连接。

    has_many :categorizations, dependent: :destroy
    has_many :categories, through: :categorizations
    

    并且有必要在分类中删除 post_id 的验证,所以我只发表评论,因为当我尝试创建帖子时未评论时这是不可能的,所以我这样做:

    class Categorization < ActiveRecord::Base
      belongs_to :post
      belongs_to :category
    
      #validates :post_id, presence: true
      validates :category_id, presence: true
    end
    

    还有工作!

    【讨论】:

    • 我认为您不需要删除任何关联。你可以这样做&lt;%= f.collection_check_boxes(:categories_ids, @categories, :id, :name) do |category| %&gt; &lt;%= category.label { category.check_box } %&gt; &lt;% end %&gt;&lt;% @categories.each do |category| %&gt; &lt;label&gt; &lt;%= check_box_tag "post[categories_ids][]", category.id, f.object.categories.include?(category) %&gt; &lt;%= category.name %&gt; &lt;/label&gt; &lt;% end %&gt;
    • 我认为选择框是比复选框更好的解决方案,因为当您的类别超过 100 时,它在 ui 部分中看起来不像复选框。
    • 感谢您向我展示您的观点。但我认为它不会在我认为的最多 10 个类别中包含这么多类别。但如果是这样的话,我会改变。但是有一个问题,如果我改变选择。我将如何选择一个以上的类别?感谢 Vishal 的帮助和回复!
    • 可以使用select2 jquery插件select2.org,非常有帮助,用select2可以传递属性multipe: true,这样就可以在select框中选择多个选项了。
    • 不错的插件 Vishal。我会尝试用这个(这对标签也很好)和你的复选框示例做一些事情。谢谢!
    猜你喜欢
    • 2017-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    • 2014-04-15
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多