【问题标题】:Ruby on Rails getting translation fields using model methods in renderRuby on Rails 在渲染中使用模型方法获取翻译字段
【发布时间】:2015-05-22 15:06:28
【问题描述】:

我有一个具有这种逻辑的 RoR API:

船长

def index

  Lot of stuff...
  @boats = query...

  render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :include => {:province => {:only => [:id, :name]}}}}}}
end

船模型

belongs_to :port

端口模型

belongs_to :city

等等。

在 BoatController.index 而不是城市中,我想返回翻译后的城市(出于国际化目的)。我不会详细介绍数据库关系(如果需要,请告诉我)。无论如何,我有一项服务可以使用语言和 cityId 参数翻译城市。 所以,我想做这样的事情:

船长:

render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:translatedCity => [:name], :city => {:only => [:id, :name], :include => {:province => {:only => [:id, :name]}}}}}}

端口模型

def translatedCity 

    TranslationService.translateCity(self.city_id, lang)
end

belongs_to :city

这是服务方法:

def self.translateCity(cityId, lang)

      city = Translation.find_by_sql(["SELECT t.element_translation
                                       FROM translations t, element_types et, languages l, cities c, states s
                                       WHERE et.name = 'City'
                                       AND l.locale = ?
                                       AND c.id = ?
                                       AND t.element_type_id = et.id
                                       AND t.language_id = l.id
                                       AND s.id = c.state_id
                                       AND c.id = t.element_id", lang, cityId]).first
      puts "translateCity.city:" + city.inspect
      return city
    end

查询正在运行,如果我检查结果,我会得到如下信息:

translatedCity.city=#<Translation id: nil, element_translation: "Palma de Mallorca">

但是我收到了这个错误:

TypeError (no implicit conversion of Symbol into Integer):
  app/models/boat.rb:29:in `as_json'
  app/controllers/boats_controller.rb:253:in `index'

我不知道是否清楚我要做什么。否则,请告诉我,我会澄清的。

【问题讨论】:

  • 我会在 City 添加一个 has_many :translations, as: :resource。还有一个belongs_to :resource, polymorphic: true 给翻译。这样你就可以简单地查询city.translations.find_by(language: 'de')
  • 如果您在translations.resource_id 上创建索引,那么复杂性会大大降低,查询也会快很多。

标签: ruby-on-rails ruby-on-rails-4


【解决方案1】:

在我看来,您的翻译设置过于复杂。您可以通过向translations 添加一列将其直接链接到它所翻译的实体来简化和加快翻译查找。

class AddResourceToTranslation < ActiveRecord::Migration
  def change
    add_reference :translations, :entity, polymorphic: true, index: true
  end
end

class City < ActiveRecord::Base
  has_many :translations, as: :entity
end

class Translation < ActiveRecord::Base
  belongs_to :entity, polymorphic: true
end

> london = City.create
> london.translations.create(element_translation: 'Londres', language: 'fr', locale: 'fr')
> london.translations.where(language: 'fr').uniq.pluck('element_translation').first
=> "Londres"

此 sql 查询的结果:

SELECT DISTINCT element_translation 
FROM "translations" 
WHERE "translations"."entity_id" = ? AND "translations"."entity_type" = ? 
    AND "translations"."language" = 'fr'  [["entity_id", 1], ["entity_type", "City"]]

加法

前提是你set the locale via I18n.locale你可以做到:

class TranslationService
  # ...
    def translateCity(city, locale = I18n.locale)
        city.translations.where(locale: locale).pluck(:element_translation).first
    end
end

如果不是,你可以使用虚拟属性:

class City
  attr_accessor :_locale 
  attr_accessor :_language
end

您必须在控制器中设置。

@boats.port.city._locale = 'fr'

然后可以使用like:

class TranslationService
  # ...
    def translateCity(city)
        city.translations.where(locale: city._locale).pluck(:element_translation).first
    end
end

但这有点hacky。

我会按照@devkaoru 的建议使用ActiveModel::Serializers 创建json。 jBuilder 也是一种替代方案,但它是一种需要测试的 PITA(它是 DHH 的宠物,因此它被包含在 Rails 中)。

class BoatSerializer < ActiveModel::Serializer
   attributes :id, :name
   has_one :port
end

class PortSerializer < ActiveModel::Serializer
   attributes :id, :name
   has_one :city
end

class ProvinceSerializer < ActiveModel::Serializer
   attributes :id, :name
end

class CitySerializer < ActiveModel::Serializer
  attributes :id, :name, :translated_name
  belongs_to :province

  def translated_name
    TranslationService.translateCity(object)
  end
end

render :json => @boats, each_serializer: BoatSerializer

这可能看起来有点臃肿——但在你的控制器方法中重复 JSON 序列化要好得多。

【讨论】:

  • 我知道你已经回答了你自己的问题,但我已经完成了 3/4 这个答案,我仍然认为它比你的解决方案更有价值。
  • 没问题,正确的答案是给你的:)。虽然我一直在用我的解决方案寻找答案。也许你可以帮助我更多。我已经意识到我需要将语言参数(在请求中传入控制器)传递给模型中的 translateCity 方法。
  • 编辑了使用 AR 序列化程序的示例以及如何为翻译服务提供语言环境。
  • 我希望总是能得到像你一样的答案。
【解决方案2】:

我想我明白了:

render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :include => {:province => {:only => [:id, :name]}}}}, :methods => :translatedCity},

【讨论】:

  • 还有像as_json 这样的方法可以帮助您解决问题。我还会考虑使用ActiveRecord::Serializer (github.com/rails-api/active_model_serializers) 之类的东西,这样你的控制器就不会因为深度散列而变得臃肿。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-18
  • 2016-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-23
相关资源
最近更新 更多