为多个模型设置标记时出错

Error while setting up tagging for multiple models

我正在尝试在我的应用程序中设置多个标记。我使用了单个标签和标记模型,它可以很好地标记单个模型。

但是,在设置关联后,当我想创建一个实际上指向 <%= select_tag(:kategory_id, options_for_select(@kategories), :prompt => "Select Category", class: "form-control") %> 的新问题 undefined method map for nil:NilClass 时出现此错误。我不知道为什么它指向这个,因为它与标记无关。我可能在某个地方犯了一个我无法弄清楚的错误。

这是我进行设置的方法。

我在标签 table 中添加了 taggable_id 和 taggable_type。

class AddAttributesToTaggings < ActiveRecord::Migration[5.1]
  def change
    add_column :taggings, :taggable_id, :integer
    add_column :taggings, :taggable_type, :string

    add_index :taggings, [:taggable_type, :taggable_id]
  end
end

Tag.rb

class Tag < ApplicationRecord
    has_many :taggings

    has_many :posts, through: :taggings, source: :taggable, source_type: Post
    has_many :questions, through: :taggings, source: :taggable, source_type: Question

    has_many :user_tags, dependent: :destroy
    has_many :users, through: :user_tags

    extend FriendlyId
    friendly_id :name, use: :slugged

    def should_generate_new_friendly_id?
      name_changed?
    end
end

tagging.rb

class Tagging < ApplicationRecord
  belongs_to :tag
  belongs_to :taggable, :polymorphic => true
end

Question.rb

class Question < ApplicationRecord
  belongs_to :user
  belongs_to :kategory

  validates :title, :content, presence: true
  is_impressionable counter_cache: true, unique: :all
  Question.order('impressions_count DESC')
  scope :most_recent, -> { order(created_at: :desc) }

  extend FriendlyId
    friendly_id :title, use: :slugged

    def should_generate_new_friendly_id?
        title_changed?
    end

  include Taggable

  def related_questions
      Question.joins(:tags).where(tags: { id: self.tags.pluck(:id) }).where.not(id: self.id)
  end
end

models/concerns/taggable.rb

module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, :as => :taggable
    has_many :tags, :through => :taggings
  end  

  def tag_list
    self.tags.collect do |tag|
      tag.name
    end.join(", ")
  end

  def tag_list=(tags_string)
    tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
    new_or_found_tags = tag_names.collect { |name| Tag.friendly.find_or_create_by(name: name) }
    self.tags = new_or_found_tags
  end

end

标签和标签 table schema.rb

create_table "taggings", force: :cascade do |t|
    t.bigint "tag_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "taggable_id"
    t.string "taggable_type"
    t.index ["tag_id"], name: "index_taggings_on_tag_id"
    t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable_type_and_taggable_id"
  end

  create_table "tags", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "slug"
    t.index ["slug"], name: "index_tags_on_slug", unique: true
  end

questions/_form

<%= form_with(model: question, local: true) do |form| %>
  <% if question.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(question.errors.count, "error") %> prohibited this question from being saved:</h2>

      <ul>
      <% question.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <b><%= form.label "Category" %></b>
    <%= select_tag(:kategory_id, options_for_select(@kategories), :prompt => "Select Category", class: "form-control") %>
  </div>

  <br>

  <div class="field">
    <b><%= form.label :title %></b>
    <%= form.text_field :title, id: :question_title, class: "form-control" %>
  </div>

  <br>

  <div class="field">
    <%= form.label :content %>
    <%= form.text_area :content, id: :question_content, class: "form-control" %>
  </div>

  <br>

  <div>
    <%= form.label :tag_list %>
    <%= form.text_field :tag_list %>
  </div>

    <br>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

questions_controller.rb

class QuestionsController < ApplicationController
  before_action :set_question, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!
  before_action :find_contacts
  before_action :find_kategory
  impressionist :actions=>[:show,:index]

  # GET /questions
  # GET /questions.json
  def index
    if params[:kategory].blank?
      @questions = Question.most_recent
    else
      @kategory_id = Kategory.find_by(name: params[:kategory]).id
      @questions = Question.where(:kategory_id => @kategory_id).most_recent
    end
  end

  # GET /questions/1
  # GET /questions/1.json
  def show
    impressionist(@question)
    @most_viewed = Question.order('impressions_count DESC').take(20)
  end

  # GET /questions/new
  def new
    @question = current_user.questions.build
    @kategories = Kategory.all.map{ |k| [k.name, k.id] }
  end

  # GET /questions/1/edit
  def edit
    @kategories = Kategory.all.map{ |k| [k.name, k.id] }
  end

  # POST /questions
  # POST /questions.json
  def create
    @question = current_user.questions.build(question_params)
    @question.kategory_id = params[:kategory_id]

    respond_to do |format|
      if @question.save
        flash[:success] = 'Your question was successfully posted.'
        format.html { redirect_to @question }
        format.json { render :show, status: :created, location: @question }
      else
        format.html { render :new }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /questions/1
  # PATCH/PUT /questions/1.json
  def update
    respond_to do |format|
      if @question.update(question_params)
        flash[:success] = 'Your question was successfully updated.'
        format.html { redirect_to @question }
        format.json { render :show, status: :ok, location: @question }
      else
        format.html { render :edit }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /questions/1
  # DELETE /questions/1.json
  def destroy
    @question.destroy
    respond_to do |format|
      flash[:success] = 'Your question has been deleted.'
      format.html { redirect_to questions_url }
      format.json { head :no_content }
    end
  end

  def most_viewed
    @questions = Question.order('impressions_count DESC').take(20)
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_question
      @question = Question.friendly.includes(:tags).find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def question_params
      params.require(:question).permit(:title, :content, :kategory_id, :tag_list)
    end

    def find_contacts
      @contacts = user_signed_in? ? current_user.all_active_contacts : ''
    end

    def find_kategory
      @questions = Kategory.where(:kategory_id => @kategory_id)
    end
end

更新:**我现在可以在不添加任何标签的情况下保存问题,但是添加标签后,我收到一个表单错误 tagging is invalid。 **标签在我编辑问题时正确加载。

错误日志:

Started POST "/questions" for 127.0.0.1 at 2018-07-13 12:01:01 +0100
Processing by QuestionsController#create as HTML
  Parameters: {"utf8"=>"√", "authenticity_token"=>"hgLn8bzd6fVSzCIPtRd3P0Jp/LEso
/h5cKNBY0a80x3i4RF6tpLlRyqmls3xRFryqyP1/s/rLDVIt9C1/uK+qw==", "kategory_id"=>"1"
, "question"=>{"title"=>"Help!!! My Dell Inspiron 6400 is hanging", "content"=>"
Lorem ipsum dolor amet seitan offal ethical, beard viral lo-fi put a bird on it
salvia actually yr. Ethical ennui pitchfork fanny pack, gentrify seitan sartoria
l bespoke. Viral 90's church-key swag, you probably haven't heard of them banh m
i intelligentsia brunch DIY iceland wolf pitchfork. Everyday carry photo booth n
ormcore XOXO tumblr portland.\r\n\r\nBrooklyn heirloom kombucha, edison bulb leg
gings hell of DIY chartreuse austin tacos bitters. Blog hexagon copper mug blue
bottle cray. Post-ironic direct trade kale chips mumblecore. Craft beer squid cr
onut vape, hoodie bitters succulents ramps snackwave vegan small batch brunch ch
ia food truck umami. Chillwave blue bottle viral raclette authentic health goth
vape coloring book cardigan 3 wolf moon taxidermy. Gluten-free tote bag hella, l
omo pinterest direct trade gastropub tattooed. Iceland hammock post-ironic, bush
wick cornhole tumeric dreamcatcher blog palo santo chartreuse 90's food truck sy
nth chicharrones.", "tag_list"=>"hardware"}, "commit"=>"Create Question"}
  User Load (2.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  ORDE
R BY "users"."id" ASC LIMIT   [["id", 3], ["LIMIT", 1]]
  Private::Conversation Load (2.9ms)  SELECT "private_conversations".* FROM "pri
vate_conversations" WHERE ("private_conversations"."recipient_id" =  OR "priva
te_conversations"."sender_id" = )  [["recipient_id", 3], ["sender_id", 3]]
  Private::Message Load (2.0ms)  SELECT "private_messages".* FROM "private_messa
ges" WHERE "private_messages"."conversation_id" IN (2, 3, 4, 6)
  Group::Conversation Load (3.9ms)  SELECT "group_conversations".* FROM "group_c
onversations" INNER JOIN "group_conversations_users" ON "group_conversations"."i
d" = "group_conversations_users"."conversation_id" WHERE "group_conversations_us
ers"."user_id" =   [["user_id", 3]]
  Group::Message Load (2.0ms)  SELECT "group_messages".* FROM "group_messages" W
HERE "group_messages"."conversation_id" = 1
  User Load (2.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (2, 3,
5)
   (40.0ms)  SELECT "group_conversations"."id" FROM "group_conversations" INNER
JOIN "group_conversations_users" ON "group_conversations"."id" = "group_conversa
tions_users"."conversation_id" WHERE "group_conversations_users"."user_id" = 
 [["user_id", 3]]
  CACHE  (0.0ms)  SELECT "group_conversations"."id" FROM "group_conversations" I
NNER JOIN "group_conversations_users" ON "group_conversations"."id" = "group_con
versations_users"."conversation_id" WHERE "group_conversations_users"."user_id"
=   [["user_id", 3]]
  User Load (2.9ms)  SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."contact_id" WHERE "contacts"."user_id" =  AND "contacts
"."accepted" =   [["user_id", 3], ["accepted", "t"]]
  User Load (4.9ms)  SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" =  AND "contacts
"."accepted" =  ORDER BY "users"."created_at" ASC  [["contact_id", 3], ["accep
ted", "t"]]
  Tag Load (2.0ms)  SELECT  "tags".* FROM "tags" WHERE "tags"."name" =  LIMIT
  [["name", "hardware"], ["LIMIT", 1]]
  Kategory Load (10.7ms)  SELECT "kategories".* FROM "kategories"
   (1.0ms)  BEGIN
  Question Exists (4.9ms)  SELECT  1 AS one FROM "questions" WHERE ("questions".
"id" IS NOT NULL) AND "questions"."slug" =  LIMIT   [["slug", "help-my-dell-
inspiron-6400-is-hanging"], ["LIMIT", 1]]
  Kategory Load (5.9ms)  SELECT  "kategories".* FROM "kategories" WHERE "kategor
ies"."id" =  LIMIT   [["id", 1], ["LIMIT", 1]]
   (0.0ms)  ROLLBACK
  Rendering questions/new.html.erb within layouts/application
  Rendered questions/_form.html.erb (17.6ms)
  Rendered questions/new.html.erb within layouts/application (141.6ms)
  Rendered layouts/navigation/header/_toggle_button.html.erb (1.0ms)
  Rendered layouts/navigation/header/_home_button.html.erb (30.3ms)
  User Load (28.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  LIM
IT   [["id", 5], ["LIMIT", 1]]
  Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
73.2ms)
  User Load (2.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  LIMI
T   [["id", 3], ["LIMIT", 1]]
  CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT   [["id", 5], ["LIMIT", 1]]
  Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
109.4ms)
  Rendered layouts/navigation/header/dropdowns/conversations/_group.html.erb (3.
9ms)
  User Load (30.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  LIM
IT   [["id", 1], ["LIMIT", 1]]
  Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
40.0ms)
  CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT   [["id", 3], ["LIMIT", 1]]
  User Load (2.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  LIMI
T   [["id", 2], ["LIMIT", 1]]
  Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
100.6ms)
  Rendered layouts/navigation/header/dropdowns/_conversations.html.erb (983.4ms)

  User Load (2.0ms)  SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" =  AND "contacts
"."accepted" =   [["contact_id", 3], ["accepted", "f"]]
  Rendered layouts/navigation/header/dropdowns/contact_requests/_no_requests.htm
l.erb (1.0ms)
  Rendered layouts/navigation/header/dropdowns/_contact_requests.html.erb (374.0
ms)
  Rendered layouts/navigation/header/_dropdowns.html.erb (1713.9ms)
  Rendered layouts/navigation/_header.html.erb (2485.4ms)
  Kategory Load (2.9ms)  SELECT "kategories".* FROM "kategories"
  Rendered layouts/navigation/collapsible_elements/_constant_links.html.erb (2.0
ms)
   (2.0ms)  SELECT COUNT(*) FROM "questions" INNER JOIN "save_questions" ON "que
stions"."id" = "save_questions"."question_id" WHERE "save_questions"."user_id" =
   [["user_id", 3]]
  Rendered layouts/navigation/collapsible_elements/_signed_in_links.html.erb (21
.5ms)
  Rendered layouts/navigation/_collapsible_elements.html.erb (543.0ms)
  Rendered layouts/_navigation.html.erb (3323.2ms)
  Rendered layouts/application/_private_conversations_windows.html.erb (1.0ms)
  Rendered layouts/application/_group_conversations_windows.html.erb (2.0ms)
Completed 200 OK in 22203ms (Views: 21796.6ms | ActiveRecord: 158.2ms)

在我看来,您的 @kategories 或其中的某些内容(可能是 name)为零,因此出现错误 :)(希望这是一件好事,一切都可能如此你的标签很好。)

确保 Kategory.all.map { |k| [k.name, k.id] } 在您的 new 操作中返回记录并且此错误应该消失。

N.B。您也可以在此处使用 options_from_collection_for_select

# in the controller
def new
  @question = current_user.questions.build
  @kategories = Kategory.all
end

# and the view
<%= select_tag(:kategory_id, options_from_collection_for_select(@kategories, :name, :id), prompt: "Select Category", class: "form-control") %>

这样,如果您没有任何类别,您将不会收到错误 - 相反,您将没有任何可用选项。

您需要在创建和更新操作中定义 @kategories = Kategory.all.map{ |k| [k.name, k.id] }。如果无法保存问题,此操作将重新呈现 new/edit 个操作,并且需要 @kategories 进行重新呈现

如果您为创建操作提供完整的服务器日志,我可以尝试帮助您弄清楚 - 为什么无法保存@question