代码之家  ›  专栏  ›  技术社区  ›  rmcsharry

Rails 5 API-has_many through create操作返回2条记录,尽管只有1条记录保存在数据库中

  •  0
  • rmcsharry  · 技术社区  · 6 年前

    我有以下三种型号的M2M关联

    Customer -> Residences <- Properties
    

    属性模型还与地址相关:

    class Address < ApplicationRecord
      has_one :property
    end
    

    在创建属性之前,客户将始终存在。 属性是通过提交地址创建的。

    这里是控制器操作,除了成功之外,渲染始终返回2个属性(即基本上2个居住记录)。

    但是,数据库中只有一个。我知道这和 stale objects ,但不知道如何解决。

    我试着加上 @customer.reload @customer.reload.residences @customer.reload.properties 但还是有两张唱片。

      # 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
    

    评论 this question (from@swaps)表示使用<<而不是create有时会导致重复,但无论我使用哪种方法,我总是得到2。

    编辑

    我设法让它像这样工作,但这感觉像一个黑客:

        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
    
    2 回复  |  直到 5 年前
        1
  •  1
  •   aridlehoover    6 年前

    您正在尝试手动执行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
    

    参考文献:

        2
  •  1
  •   mehedi    6 年前

    你能试试这个吗?

    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

    请您将您的客户和居住模式添加如下:

    客户模型

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

    住宅模型

    class Residence < ApplicationRecord
      belongs_to :customer
      belongs_to :property
    end
    
        3
  •  0
  •   rmcsharry    5 年前

    问题是activerecord中的对象与保存后数据库中的对象比较陈旧。

    “.reload”不起作用,我必须使用我的黑客强制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