【问题标题】:How to build this has_many relation through an has_one relation?如何通过 has_one 关系建立这个 has_many 关系?
【发布时间】:2019-01-11 08:59:21
【问题描述】:

如果我的英语不完美,请原谅。

我希望能够创建一个带有一个subcategorytheme(并且子类别有一个主题),并将这个theme 链接到多个categories。我希望我能做到Theme.first.categoriesCategory.first.themes

在主题表中,我只保存名称和 slug。在子类别表中,我保存了描述、页面标题等,因为每个类别都会有同名但描述不同的主题。

例如,我可以有两个类别,如“法语到英语”和“意大利语到中文”。这些类别都有一个名为“科学”的主题,但描述不同。

目前,我可以创建一个主题,但只能使用一个类别。例如,如果我选择两个类别,则会出现错误:can't modify frozen Hash

theme.rb:

    class Theme < ApplicationRecord
      has_one(:subcategory, dependent: :destroy)
      has_many(:categories, through: :subcategory)

      accepts_nested_attributes_for :subcategory
    end

subcategory.rb:

    class Subcategory < ApplicationRecord
      belongs_to(:category)
      belongs_to(:theme)
    end

category.rb:

    class Category < ApplicationRecord
      has_many(:subcategories, dependent: :destroy)
      has_many(:themes, through: :subcategories)
    end

schema.rb:

    create_table "themes", force: :cascade do |t|
      t.string "name", null: false
      t.string "slug"
      t.datetime "created_at", null: false
      t.datetime "updated_at", null: false
    end

    create_table "subcategories", force: :cascade do |t|
      t.text "description"
      t.string "page_title"
      t.string "meta_description"
      t.string "h1_title"
      t.bigint "category_id"
      t.bigint "theme_id"
      t.datetime "created_at", null: false
      t.datetime "updated_at", null: false
      t.index ["category_id"], name: "index_subcategories_on_category_id"
      t.index ["theme_id"], name: "index_subcategories_on_theme_id"
    end

    create_table "categories", force: :cascade do |t|
      t.string "name", null: false
      t.datetime "created_at"
      t.datetime "updated_at"
      t.string "slug"
      t.text "description"
      t.string "page_title"
      t.string "meta_description"
      t.string "h1_title"
      t.string "source_language"
      t.string "target_language"
    end

themes_controller.rb:

# frozen_string_literal: true

    module Admin
      class ThemesController < AdminController
        load_and_authorize_resource except: :update
        before_action(:load_categories, only: %i[new edit])
        before_action(:load_theme, only: :update)

        def index
          @themes = Theme.all.to_a
        end

        def new
          @theme.build_subcategory
        end

        def create
          @theme = Theme.new(theme_params)
          if @theme.save
            redirect_to(admin_themes_url, notice: 'Theme successfully created')
          else
            render(:new)
          end
        end

        def edit
        end

        def update
          if @theme.update(theme_params)
            redirect_to(admin_themes_url, notice: 'Theme successfully edited')
          else
            render(:edit)
          end
        end

        def destroy
          @theme.destroy
          redirect_to(admin_themes_url, notice: 'Theme successfully deleted')
        end

      private

        def theme_params
          params.require(:theme).permit(
            :id,
            :name,
            :slug,
            subcategory_attributes: %i[description h1_title page_title meta_description],
            category_ids: [],
          )
        end

        def load_categories
          @categories = Category.all.to_a
        end

        def load_theme
          @theme = Theme.find_by(slug: params[:id])
        end
      end
    end

和我的表格:


    = simple_form_for([:admin, @theme]) do |f|
      = f.input(:name, autofocus: true)
      = f.simple_fields_for :subcategory_attributes, @theme.subcategory do |s|
        = s.input(:h1_title)
        = s.input(:description)
        = s.input(:page_title)
        = s.input(:meta_description)
      = f.input(:category_ids, collection: @categories.map { |cat| [cat.name, cat.id] }, as: :check_boxes, wrapper: :vertical_radio_and_checkboxes)
      = f.button(:submit , class: 'btn btn-primary')

日志:

0:39:39 server.1 | Started POST "/admin/themes" for ::1 at 2019-01-11 10:39:39 +0100
10:39:39 server.1 | Processing by Admin::ThemesController#create as HTML
10:39:39 server.1 |   Parameters: {"utf8"=>"✓", "authenticity_token"=>"6p2oxZvOlFJHYvAeYbkjjOrGGk8Y5GrnFOTBuQEmxTcMIhgfK32N2/ZGabLYmoW0pHiUaX6hVqb/EeuTgbr55g==", "theme"=>{"name"=>"rerezre", "subcategory_attributes"=>{"h1_title"=>"reez", "description"=>"rezer", "page_title"=>"rezrez", "meta_description"=>"rezerez"}, "category_ids"=>["", "1", "2"]}, "commit"=>"Créer un(e) Theme"}
10:39:39 server.1 |   User Load (1.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 7], ["LIMIT", 1]]
10:39:39 server.1 |   ↳ app/controllers/application_controller.rb:93
10:39:39 server.1 |   Category Load (0.8ms)  SELECT "categories".* FROM "categories" WHERE "categories"."id" IN ($1, $2)  [["id", 1], ["id", 2]]
10:39:39 server.1 |   ↳ /Users/robin/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.1.1/lib/active_record/log_subscriber.rb:98
10:39:39 server.1 |    (0.3ms)  BEGIN
10:39:39 server.1 |   ↳ /Users/robin/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.1.1/lib/active_record/log_subscriber.rb:98
10:39:39 server.1 |    (0.3ms)  COMMIT
10:39:39 server.1 |   ↳ /Users/robin/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.1.1/lib/active_record/log_subscriber.rb:98
10:39:39 server.1 |   CACHE Category Load (0.0ms)  SELECT "categories".* FROM "categories" WHERE "categories"."id" IN ($1, $2)  [["id", 1], ["id", 2]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:18
10:39:39 server.1 |    (0.2ms)  BEGIN
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:18
10:39:39 server.1 |    (0.1ms)  COMMIT
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:18
10:39:39 server.1 |    (0.1ms)  BEGIN
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |   Category Exists (0.5ms)  SELECT  1 AS one FROM "categories" WHERE LOWER("categories"."name") = LOWER($1) AND "categories"."id" != $2 LIMIT $3  [["name", "Français à anglais"], ["id", 1], ["LIMIT", 1]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |   Category Exists (0.5ms)  SELECT  1 AS one FROM "categories" WHERE LOWER("categories"."name") = LOWER($1) AND "categories"."id" != $2 LIMIT $3  [["name", "Italien à chinois"], ["id", 2], ["LIMIT", 1]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |   Theme Create (0.7ms)  INSERT INTO "themes" ("name", "slug", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "rerezre"], ["slug", "rerezre"], ["created_at", "2019-01-11 09:39:39.193694"], ["updated_at", "2019-01-11 09:39:39.193694"]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |   Subcategory Create (0.5ms)  INSERT INTO "subcategories" ("description", "page_title", "meta_description", "h1_title", "category_id", "theme_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["description", "rezer"], ["page_title", "rezrez"], ["meta_description", "rezerez"], ["h1_title", "reez"], ["category_id", 2], ["theme_id", 24], ["created_at", "2019-01-11 09:39:39.196215"], ["updated_at", "2019-01-11 09:39:39.196215"]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |   Category Exists (0.3ms)  SELECT  1 AS one FROM "categories" WHERE LOWER("categories"."name") = LOWER($1) AND "categories"."id" != $2 LIMIT $3  [["name", "Français à anglais"], ["id", 1], ["LIMIT", 1]]
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 |    (0.2ms)  ROLLBACK
10:39:39 server.1 |   ↳ app/controllers/admin/themes_controller.rb:19
10:39:39 server.1 | Completed 500 Internal Server Error in 32ms (ActiveRecord: 7.0ms)
10:39:39 server.1 |
10:39:39 server.1 |
10:39:39 server.1 | can't modify frozen Hash excluded from capture: Not configured to send/capture in environment 'development'
10:39:39 server.1 |
10:39:39 server.1 | FrozenError (can't modify frozen Hash):
10:39:39 server.1 |
10:39:39 server.1 | app/controllers/admin/themes_controller.rb:19:in `create'

如果我想做我想做的事,我知道关系有问题,但我不知道该怎么做。

有人可以帮帮我吗?

【问题讨论】:

  • 我们能否看到您在错误发生时尝试提交的数据、它使用的代码以及错误回溯的几行代码?
  • 我尝试了相同的架构,但记录没有保存,我没有任何错误,但问题可能只是在我的 simple_form 中。我要去找这个
  • 是的@TomDunning,我编辑了帖子

标签: ruby-on-rails ruby database


【解决方案1】:

这里有一个概念问题。 由于ThemeSubcategory 是一对一的关系,而Category 是一对多的关系,所以ThemeCategory 之间的传递关系不能是多对多的。为此,Theme 必须至少与 Subcategory 存在一对多关系。

换句话说: 每个 Theme 都只有一个 Subcategory,每个 Subcategory 都只有一个 Category,因此每个 Theme 通过 Subcategory 都有一个 Category。 如果您希望一个主题有多个类别(通过子类别),那么您应该允许一个主题有多个子类别。

【讨论】:

  • 谢谢,我一开始就是这样,所以问题出在我的表单或控制器上,而不是关系上:/
  • 也许关系问题与您看到的错误无关。但是,除非您恢复到之前的状态,即允许主题具有多个类别,否则您不能期望成功创建具有多个类别的主题。如果您仍然看到错误,我建议在 rails 控制台中创建一个包含两个类别的主题,并查看错误是否也发生在那里,以缩小问题的根源。但首先你需要修正你的关系规范。
  • 我恢复了我的更改,现在,在控制台中,一个主题可以有很多类别。当我尝试在视图中创建具有多个类别的主题时,我不明白是什么问题,我仍然需要搜索
  • 那么,你提交给控制器的参数和你在控制台中使用的参数有什么区别呢?请记住,您不能直接将类别分配给主题,而必须分配子类别。
猜你喜欢
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多