Rails 5 API - has_many через действие create возвращает 2 записи, хотя в базе данных сохраняется только 1 - PullRequest
0 голосов
/ 16 декабря 2018

У меня есть следующие M2M через ассоциации для этих 3 моделей

Customer -> Residences <- Properties

Также модель свойства связана с адресом:

class Address < ApplicationRecord
  has_one :property
end

Клиент всегда будет существовать до создания свойства.Свойство создается путем отправки адреса.

Вот действие контроллера, которое работает, за исключением случая, когда рендеринг всегда возвращает 2 свойства (т. Е. В основном 2 записи о резидентности).

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

Я пытался добавить @customer.reload и @customer.reload.residences и @customer.reload.properties, но все равно получаю 2 записи.

  # POST /customers/:id/properties
  def create
    @customer = set_customer
    Customer.transaction do
      address = Address.find_by_place_id(address_params[:place_id]) 
      if address.nil?
        @property = @customer.properties.create
        @property.address = Address.new(address_params)
        if @property.save
          @customer.reload
          render json: @customer, status: :created
        else
          render json: @property.errors, status: :unprocessable_entity
        end
      else
        # irrelevant code to the problem
      end
    end
  end

def set_customer
  Customer.find(params[:customer_id])
end

Комментарий к этому вопросу (из @Swaps) указывает, что использование << вместо создания может иногда приводить к дублированию, но каким бы способом я это не делал, я всегда получаю 2. </p>

РЕДАКТИРОВАТЬ

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

    if @property.save
      @customer = set_customer
      render json: @customer, status: :created
    else

** ОБНОВЛЕНИЕ - модели **

class Customer < ApplicationRecord
  has_many :residences
  has_many :properties, through: :residences
end

class Residence < ApplicationRecord
  belongs_to :customer
  belongs_to :property
end

class Property < ApplicationRecord
  belongs_to :address
  has_many :residences
  has_many :customers, through: :residences
end

class Address < ApplicationRecord
  has_one :property
  has_one :location # ignore this, not relevant
end

Ответы [ 3 ]

0 голосов
/ 16 декабря 2018

Не могли бы вы попробовать это?

def create
  @customer = set_customer
  Customer.transaction do
    address = Address.find_by_place_id(address_params[:place_id]) 
    if address.nil?
      @customer.properties.new(address_params)
      if @customer.save
        render json: @customer, status: :created
      else
        render json: @customer.errors, status: :unprocessable_entity
      end
    else
      # irrelevant code to the problem
    end
  end
end

Я думал, вам действительно нужна переменная экземпляра @property.Это для ваших файлов просмотра?

Обновление 1

Не могли бы вы добавить свою модель Customer и Residence следующим образом:

Модель клиента

class Customer < ApplicationRecord
  has_many :residences
  has_many :properties, through: :residences
end

Резиденция модель

class Residence < ApplicationRecord
  belongs_to :customer
  belongs_to :property
end
0 голосов
/ 02 марта 2019

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

«.reload» не работал, мне пришлось фактически заставить ActiveRecord использовать мой хак, чтобы заставить ActiveRecord снова найти клиента в базе данных, и это вызывает перезагрузку (я полагаю, это делает недействительным кэш AR):

  def create
    @customer = set_customer
    Customer.transaction do
      address = Address.find_by_place_id(address_params[:place_id]) 
      if address.nil?
        @property = @customer.properties.create
        @property.address = Address.new(address_params)
        if @property.save!
          @customer = set_customer # force reload from db
          render json: @customer, status: :created
        end
      else
        address.update!(address_params)
        if @customer.properties.find_by_id(address.property.id).nil?
          # although we updated the address, that is just a side effect of this action
          # the intention is to create an actual residence record for this customer
          @customer.properties << address.property
          @customer = set_customer # force reload from db
          render json: @customer, status: :created
        else
          @customer.errors.add(:customer, 'already has that property address')
          render json: ErrorSerializer.serialize(@customer.errors), status: :unprocessable_entity
        end
      end
    end
  end

  def set_customer
    Customer.find(params[:customer_id])
  end
0 голосов
/ 16 декабря 2018

Вы пытаетесь сделать вручную то, что ActiveRecord может делать автоматически с accepts_nested_attributes_for.Он даже работает с has_many through операциями.

class Customer < ApplicationRecord
  has_many: :residences, inverse_of :customer
  has_many: :properties, through: :residences

  accepts_nested_attributes_for :residences
end

class Residence < ApplicationRecord
  belongs_to :customer
  belongs_to :property

  validates_presence_of :customer
  validates_presence_of :property

  accepts_nested_attributes_for :property
end

class Property < ApplicationRecord
  has_one :address
  has_many :residences
  has_many :customers, through: :residences

  accepts_nested_attributes_for :address
end

class Address < ApplicationRecord
  belongs_to :property
end

class CustomersController < ApplicationController
  def create
    customer = Customer.new(customer_params)
    if customer.save
      redirect_to customer, notice: 'Customer saved!'
    else
      render :new
    end
  end

  def customer_params
    params.require(:customer).permit(
      name:, ..., 
      residences_attributes: [
        property_attributes: [
          name, ..., 
          address_attributes: [
            street, city, state, postal_code, ...
          ]
        ]
      ]
    )
  end
end

Ссылки:

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