Ruby Rails Tagging - отношение многие ко многим с 3 таблицами - в двойных строках средней (сквозной) таблицы - PullRequest
1 голос
/ 08 мая 2020

У меня такая ситуация в Ruby on Rails (5.0)

  • Многие теги имеют много условий
  • В случае одного и того же условия для нескольких тегов - тогда должно быть определено конкретное процентное разделение

Примеры

  • Тег «работа» имеет условие «VS Code» процентов 40
  • Тег «оплачен» имеет условие «VS Code» процент 60
  • Тег «работа» имеет условие «Cal c» процент 100
  • Тег «смешной» имеет условие «Youtube» процент 100

Тег основной таблицы

table structure---------------------  

      t.string :name
      t.datetime :valid_from
      t.datetime :valid_to

with model--------------------------

class Tag < ApplicationRecord
    has_many :groupings, dependent: :destroy
    has_many :groups, through: :groupings

    has_many :tag_conditionings, dependent: :destroy
    has_many :tag_conditions, through: :tag_conditionings

    accepts_nested_attributes_for :tag_conditions
    accepts_nested_attributes_for :tag_conditionings
end

Другая таблица Tag_condition

table structure---------------------  

condition:string

with model--------------------------

class TagCondition < ApplicationRecord
  belongs_to :tag, optional: true
end

И между таблицей Tag_conditioning (с процентами)


table structure---------------------  

      t.references :tag_condition, foreign_key: true
      t.references :tag, foreign_key: true
      t.integer :percent

with model--------------------------


class TagConditioning < ApplicationRecord
  belongs_to :tag_condition, optional: true
  belongs_to :tag, optional: true
end

У меня есть одно представление с 2 вложенными формами - если я понимаю справа

<%= form_for(tag) do |f| %>

  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>


  <%= f.fields_for :**tag_conditions** do |c| %>
    <div class="field">
      <%= c.label :**condition** %>
      <%= c.text_field :**condition** %>
    </div>
  <% end %>

  <%= f.fields_for :**tag_conditionings** do |c| %>
    <div class="field">
      <%= c.label :**percent** %>
      <%= c.text_field :**percent** %>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :valid_from %>
    <%= f.datetime_select :valid_from %>
  </div>

  <div class="field">
    <%= f.label :valid_to %>
    <%= f.datetime_select :valid_to %>
  </div>

Контроллер - tags_controllers.rb

 def new
    @tag = Tag.new
    @tag.tag_conditions.build
    @tag.tag_conditionings.build
  end

  def create
    @tag = Tag.new(tag_params)

     respond_to do |format|
       if @tag.save
         format.html { redirect_to @tag, notice: 'Tag was successfully created.' }
         format.json { render :show, status: :created, location: @tag }
       else
         format.html { render :new }
         format.json { render json: @tag.errors, status: :unprocessable_entity }
       end
     end
  end

private
    def tag_params
      params.require(:tag).permit(:name, :percent, :valid_from, :valid_to, tag_conditions_attributes: [:condition], tag_conditionings_attributes: [:percent])
    end

Все в порядке, только в таблице tag_conditionings у меня есть двойные INSERTS и, следовательно, двойные ROWS

INSERT INTO "tags" ("name", "valid_from", "valid_to", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "sdf"], ["valid_from", "2020-05-08 19:56:00"], ["valid_to", "2020-05-08 19:56:00"], ["created_at", "2020-05-08 19:56:55.811745"], ["updated_at", "2020-05-08 19:56:55.811745"]]                     
**first INSERT**
INSERT INTO "tag_conditionings" ("tag_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["tag_id", 14], ["created_at", "2020-05-08 19:56:55.843693"], ["updated_at", "2020-05-08 19:56:55.843693"]]                                                                                                                                      

INSERT INTO "tag_conditions" ("condition", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["condition", "50sd"], ["created_at", "2020-05-08 19:56:55.865495"], ["updated_at", "2020-05-08 19:56:55.865495"]]                                                                                                                               
**second INSERT**
INSERT INTO "tag_conditionings" ("tag_condition_id", "tag_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["tag_condition_id", 16], ["tag_id", 14], ["created_at", "2020-05-08 19:56:55.919774"], ["updated_at", "2020-05-08 19:56:55.919774"]]   

Я новичок на любителя ie, вы можете мне помочь? Что у меня не так? Я потратил много часов на поиск в Google, чтение, тестирование, но результат нулевой.

Большое спасибо

Айвенго

1 Ответ

2 голосов
/ 09 мая 2020

Причина, по которой вы получаете две дополнительные строки в tag_conditionings, заключается в том, что рельсы неявно создают строки в таблице соединений, когда у вас есть ассоциация has_many through::

Tag.first
   .tag_conditions
   .create(some_attribute: 'some_value') 

Приведенное выше создает 1 строку в tag_conditions и 1 строка в tag_conditionings.

То же самое применимо, когда вы используете вложенные атрибуты:

Tag.create(
  tag_conditions_attributes: [{ some_attribute: 'some_value' }]
)

Если вы хотите создать объект объединенной таблицы явно и присоединенный сущность, вам нужно вложить вложенные атрибуты:

class TagConditioning < ApplicationRecord
  belongs_to :tag_condition, optional: true
  belongs_to :tag, optional: true
  accepts_nested_attributes_for :tag_conditions
end

Tag.create(
   tag_conditioning_attributes: [
      { 
        percent: 50,
        tag_condition_attributes: [
           { some_attribute: 'some_value' }
        ]
      }
   ]
)

В форме вы делаете это, вложив вызов в fields_for:

<%= form_with(model: @tag) do |f| %>
  <%= f.fields_for :tag_conditionings do |conditionings| %>
    <%= conditionings.number_field :percent, in: 1..100 %>
    <%= conditionings.fields_for :tag_conditions do |tc| %>
      <%= tc.text_field :conditions %>
    <% end %>
  <% end %>

  # ...
<% end %>

И вы заносите в белый список вложенные-вложенные атрибуты through:

def tag_attributes
  params.require(:tag)
        .permit(
           :foo, 
           :bar,
           tag_conditionings_attributes: [
              :percent,
              tag_conditions_attributes: [
                :conditions 
              ]
           ]
         )
end

Я бы действительно не рекомендовал вложение глубже этого, поскольку уровень сложности просто безумный. Альтернативой является настройка отдельных конечных точек API и использование вызовов ajax для обновления «вложенных» записей таким образом, чтобы это было удобно для пользователя, но на самом деле это atomi c.

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