【问题标题】:rails4 has_one association scope with where queryrails4 has_one 关联范围与 where 查询
【发布时间】:2016-10-06 01:14:19
【问题描述】:

我正在开发一个产品模型,它与自身具有 has_one 关联,包括原材料记录。模型通过 REST API 返回数据。我正在使用 Active Model Serializer 而不是 JBuilder。

产品型号有一个“代码”字段,其中包含字符串中的产品代码:

'001-000-01-01' (This is a product.)
'001-000-00-01' (This is a material.)

两个代码之间的唯一区别是右起第三个数字。 “1”是产品。 “0”是物质。我想在检索产品记录时包含“raw_material”记录。因此,我尝试设置 has_one 与具有“where”子句的范围的关联(稍后我可以编写查询以从产品代码中获取材料)。现在我只需在 lambda 中传递“产品”对象并在 where 中使用它。

首先我在 def raw_material 中写,这行得通。但是,我不知道如何将对象传递给 def 并在 where 子句中使用它。因此,我在 has_one 中提出了范围模式,但是即使它生成与 def 模式完全相同的 SELECT,它也会返回错误。我得到的是“NoMethodError”。

class Product < ActiveRecord::Base
  has_many :supplies, ->{order('row_order ASC') }, primary_key: :code, foreign_key: :product_code
  #This Works!
  #has_one :raw_material, class_name: 'Product', primary_key: :code, foreign_key: :code
  #This Works!
  #has_one :raw_material
  #Does not work. Why?
  has_one :raw_material, ->(product) { where('code = ?', product.code).take }, class_name: 'Product'
  accepts_nested_attributes_for :supplies, allow_destroy: true

  #def raw_material
  #  Product.where('code = ?', '001-000-01-01').take
  #end
end

“def”模式有效:

Started GET "/products/1.json" for ::1 at 2016-10-05 21:48:15 +0900
Processing by ProductsController#show as JSON
  Parameters: {"id"=>"1"}
  Product Load (0.3ms)  SELECT  "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1  [["id", 1]]
[active_model_serializers]   Supply Load (0.1ms)  SELECT "supplies".* FROM "supplies" WHERE "supplies"."product_code" = ?  ORDER BY row_order ASC  [["product_code", "031-052-00-01"]]
[active_model_serializers]   Product Load (0.1ms)  SELECT  "products".* FROM "products" WHERE (code = '001-000-01-01') LIMIT 1
[active_model_serializers] Rendered ProductSerializer with ActiveModelSerializers::Adapter::Attributes (6.71ms)
Completed 200 OK in 24ms (Views: 9.2ms | ActiveRecord: 1.4ms)

但是,范围模式不起作用:

Started GET "/products/1.json" for ::1 at 2016-10-06 08:19:13 +0900
Processing by ProductsController#show as JSON
  Parameters: {"id"=>"1"}
  Product Load (0.1ms)  SELECT  "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1  [["id", 1]]
[active_model_serializers]   Supply Load (0.1ms)  SELECT "supplies".* FROM "supplies" WHERE "supplies"."product_code" = ?  ORDER BY row_order ASC  [["product_code", "031-052-00-01"]]
[active_model_serializers]   Product Load (0.1ms)  SELECT  "products".* FROM "products" WHERE (code = '031-052-00-01') LIMIT 1
[active_model_serializers] Rendered ProductSerializer with ActiveModelSerializers::Adapter::Attributes (10.6ms)
Completed 500 Internal Server Error in 27ms (ActiveRecord: 1.2ms)

NoMethodError (undefined method `except' for #<Product:0x007fe97b090418>):
  app/controllers/products_controller.rb:17:in `block (2 levels) in show'
  app/controllers/products_controller.rb:14:in `show'


  Rendered vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/_source.erb (8.8ms)

产品控制器简单地定义显示如下:

# GET /products/1
# GET /products/1.json
def show
  respond_to do |format|
    format.html
    format.json do
      render json: @product, include: ['raw_material']
    end
  end
end 

product_serializer.rb 是:

class ProductSerializer < ActiveModel::Serializer
  attributes(*Product.attribute_names.map(&:to_sym))
  has_one :raw_material
end

非常感谢任何帮助。

谢谢

更新:

我自己解决了这个问题。请在下面查看我的答案。感谢所有编写解决方案的人。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 scope associations has-one


    【解决方案1】:
     # Does not work. Why?
     has_one :raw_material ->(product) { where('code = ?', product.code).take }...
    

    因为您试图在此处定义范围...而不是关联。如果你想要一个范围,请使用一个范围,例如:

    scope :raw_material ->(product) { where('code = ?', product.code).first }
    

    如果您想要关联,请使用关联,例如

    has_one :raw_material, class_name: 'Product', primary_key: :code, foreign_key: :code
    

    不要试图将两者混为一谈。

    【讨论】:

    • 谢谢。如果使用了 scope:,我必须从我的控制器中调用它。并且嵌套的json数据就是这样省略的。这就是我避免从控制器调用它的原因。关于 Rails 文档,调用“范围”似乎是可以的。 apidock.com/rails/ActiveRecord/Associations/ClassMethods/…
    • 您可以使用 include 在您的 json 中包含内容...所以我不确定您的意思是它被省略了:tigraine.at/2011/11/17/…
    • 我不记得确切的问题了。调用 REST API 时未预加载嵌套数据。它可能在 MVC 中工作。也许我错了。如果我可以在控制器中使用“查找”,那么您的方式也可能是不错的选择。
    • 调试代码的口头描述真的很困难......(您对代码在脑海中的样子非常清楚,但很难与外界交流)。而不是描述您可以/不可以做什么 - 通过将它们添加到您的问题末尾来向我们展示不同的示例作为实际代码:)
    【解决方案2】:

    您可以按如下方式自定义 has_one :raw_material。

    class ProductSerializer < ActiveModel::Serializer
      attributes(*Product.attribute_names.map(&:to_sym))
    
      has_one :raw_material do
        Product.where(code: object.code)
      end
    
    end
    

    【讨论】:

    • 这很简单,可以立即测试。但它不起作用。谢谢。
    【解决方案3】:

    我可以自己解决这个问题。因为我不必写成一行,所以 def(method) 模式是最好的选择。如何覆盖“has_many”可以帮助我解决问题。

    Overriding a has_many association getter

    "self" 可用于 has_one 获取 def 中的对象。

    has_one :raw_material
    
    def raw_material
      Product.where('code like ?', (self.code)[0,9] + '_' + (self.code)[10,3]).take
    end
    

    这很有效,可以在模型中生成您喜欢的任何数据集。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 2017-07-11
      • 1970-01-01
      • 2015-12-07
      • 2017-08-29
      • 2012-01-11
      • 1970-01-01
      相关资源
      最近更新 更多