Rails 5 Form не сохраняется правильно, когда есть две ассоциации has_one одной модели - PullRequest
0 голосов
/ 27 сентября 2018

Итак, я хочу создать поездку (HhAttribute), которая имеет одну отправную точку и пункт назначения.start и destination модели Location.Моя проблема в том, что в моей форме, которая состоит из вложенных атрибутов, она не сохраняется должным образом, что означает: start и destination всегда имеют одинаковое значение.

Я уже прочитал Множество внешних ключей, ссылающихся на одну и ту же таблицу в RoR , и попытался в принадлежащих ей принадлежать в обеих моделях, но это не сработало при сохранении, потому что мне нужно было установить hh_attribute.start.hh_attribute.

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

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

hh_attribute.rb

has_one :start, class_name: "Location", dependent: :destroy, inverse_of: :hh_attribute
has_one :destination, class_name: "Location", dependent: :destroy, inverse_of: :hh_attribute

accepts_nested_attributes_for :start, allow_destroy: true
accepts_nested_attributes_for :destination, allow_destroy: true

location.rb

belongs_to :hh_attribute

миграционный файл hh_attributes

def change
    create_table :hh_attributes do |t|
      t.time :time
      t.integer :lifts
      t.references :simple_post, foreign_key: true
      t.references :start
      t.references :destination
    end
    add_foreign_key :hh_attributes, :locations, column: :start_id, primary_key: :id
    add_foreign_key :hh_attributes, :locations, column: :destination_id, primary_key: :id
  end

расположение файла миграции

  def change
    create_table :locations do |t|
      t.string :address
      t.float :latitude
      t.float :longitude
      t.references :hh_attribute, foreign_key: true

      t.timestamps
    end
  end

вложенная форма:

<%= form.fields_for :hh_attribute do |h| %>
  <%= h.fields_for :start do |s| %>
    <%= s.label :address,"Starting Point" %>
    <%= s.text_field :address, class: "form-control" %>
  <% end %>
  <%= h.fields_for :destination do |d| %>
    <%= d.label :address, "Destination" %>
    <%= d.text_field :address, class: "form-control" %>
  <% end %>
<% end %>

Обновление

Выдержка изсгенерированная форма:

<input class="form-control" type="text" value="Trier" name="simple_post[hh_attribute_attributes][start_attributes][address]" id="simple_post_hh_attribute_attributes_start_attributes_address" />

<input type="hidden" value="1" name="simple_post[hh_attribute_attributes][start_attributes][id]" id="simple_post_hh_attribute_attributes_start_attributes_id" />

<input class="form-control" type="text" value="Trier" name="simple_post[hh_attribute_attributes][destination_attributes][address]" id="simple_post_hh_attribute_attributes_destination_attributes_address" />

<input type="hidden" value="1" name="simple_post[hh_attribute_attributes][destination_attributes][id]" id="simple_post_hh_attribute_attributes_destination_attributes_id" />

журнал запросов:

 SimplePost Load (0.0ms)  SELECT  "simple_posts".* FROM "simple_posts" WHERE "s
imple_posts"."id" = ? LIMIT ?  [["id", 4], ["LIMIT", 1]]
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT
 ?  [["id", 1], ["LIMIT", 1]]
   (0.0ms)  begin transaction

HhAttribute Load (0.0ms)  SELECT  "hh_attributes".* FROM "hh_attributes" WHERE
     "hh_attributes"."simple_post_id" = ? LIMIT ?  [["simple_post_id", 4], ["LIMIT",
     1]]

Location Load (1.0ms)  SELECT  "locations".* FROM "locations" WHERE "locations
    "."hh_attribute_id" = ? LIMIT ?  [["hh_attribute_id", 1], ["LIMIT", 1]]
      CACHE Location Load (0.0ms)  SELECT  "locations".* FROM "locations" WHERE "loc
    ations"."hh_attribute_id" = ? LIMIT ?  [["hh_attribute_id", 1], ["LIMIT", 1]]

User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT
     ?  [["id", 1], ["LIMIT", 1]]
      SQL (5.0ms)  UPDATE "locations" SET "address" = ?, "latitude" = ?, "longitude"
     = ?, "updated_at" = ? WHERE "locations"."id" = ?  [["address", "Köln"], ["latit
    ude", 50.938361], ["longitude", 6.959974], ["updated_at", "2018-09-27 16:49:29.3
    96327"], ["id", 1]]

1 Ответ

0 голосов
/ 27 сентября 2018

Поместите внешние ключи на стороне владельца

Проблема здесь - это фундаментальная проблема с настройкой ассоциаций.Невозможно надлежащим образом разрешить ассоциации has_one :start и has_one :destination, поскольку они указывают на один и тот же внешний ключ.

Вам необходимо разместить столбец внешнего ключа и принадлежат_вто на стороне владельца - hh_attributes.

Сначала добавьте столбцы внешнего ключа в hh_attributes.

class AddStartAndDestinationToHhAttributes < ActiveRecord::Migration[5.2]
  def change
    add_reference :hh_attributes, :start, foreign_key: { to_table: :locations }
    add_reference :hh_attributes, :location, foreign_key: { to_table: :locations }
  end
end

Затем удалите столбец locations.hh_attribute_id.

Затем вы хотите добавить ассоциацию own_to кправильная сторона:

class HhAttribute < ApplicationClass
  belongs_to :start, class_name: 'Location'
  belongs_to :destination, class_name: 'Location'
end

Трудности объединения двух столов дважды

Затем настройте настройку обратной связи.Вы могли бы подумать, что мы могли бы просто сделать has_one :hh_attribute и покончить с этим.Нет.

Это на самом деле очень грязно, так как мы присоединяемся к одной таблице дважды, и местоположение может быть в любом из столбцов внешнего ключа:

class Location < ApplicationClass
  has_one :hh_attribute_as_start, class_name: 'HhAttribute', foreign_key: 'start_id'
  has_one :hh_attribute_as_destination, class_name: 'HhAttribute', foreign_key: 'destination_id'
  def hh_attribute
    hh_attribute_as_start || hh_attribute_as_destination
  end  
end

STI для спасения

Существует менее сложное, но более продвинутое решение, которое использует наследование одной таблицы .

Сначала добавьте столбец type к locations:

$ rails g migration AddTypeToLocations type:string:index

Это имя столбца играет очень важную роль в ActiveRecord.Он сообщает AR, какой класс создавать для каждой строки.

Затем мы создаем два подкласса Location:

class Location < ApplicationRecord
  # add the common logic here
end

# app/models/locations/start.rb
module Locations
  class Start < Location
    has_one :hh_attribute, class_name: 'HhAttribute',
                           foreign_key: 'start_id'
  end
end

# app/models/locations/destination.rb
module Locations
  class Destination < Location
    has_one :hh_attribute, class_name: 'HhAttribute',
                           foreign_key: 'destination_id'
  end
end

Каждый подкласс теперь имеет ассоциацию has_one :hh_attribute, указывающую на правильный внешний ключ.Нам также нужно рассказать HhAttribute о нашей новой необычной STI настройке:

class HhAttribute < ApplicationRecord
  belongs_to :start, class_name: 'Locations::Start'
  belongs_to :destination, class_name: 'Locations::Destination'
  # ...
end

Крутая часть в том, что AR будет отслеживать типы почти волшебным образом, так как мы установили егов ассоциациях.

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