1

我有两个关联的模型/表

应用程序/模型/city.rb
has_many :企业
字段:id、city_name、state_code

应用程序/模型/business.rb
归属地:城市
字段:id、biz_name、address、city_name、state_code、zip

在您输入企业地址的新企业表单中,通常会有一个州和/或国家/地区的下拉选择框。但是对于有数千个城市来说,这是行不通的。有没有办法在允许保存之前检查城市是否列在城市表中?甚至可能将 city_id 放在该字段中而不是 city_name 中?

4

3 回答 3

0

基于响应建议和我自己的进一步调查,我得出的结论是从关联表中获取表单中的大量选项的最佳方式。

看起来自动完成是最好的解决方案。这是一个常规文本字段,但是当您输入第二个字母时,它会提取前 10 个匹配项,并在下面列出它们。每个额外的字母都会优化搜索。由于我对 JavaScript 的熟练程度不高,因此我没有按照建议添加一个以我自己的 JS 为补充的插件,而是选择使用 gem 来抽象 JS 编码,特别是rails-jquery-autocomplete
以下是步骤:

设置

生成脚手架:

rails generate scaffold City name state
rails generate scaffold Business name address city:references state zip
rake db:migrate

安装宝石。它使用jQuery-UI 的自动完成小部件,因此也为此安装 rails gem。

# gemfile
gem 'jquery-ui-rails'
gem 'rails-jquery-autocomplete'

将这些添加到您的 javascript 和样式表资产中:

# app/assets/javascripts/application.js > put these after //= require jquery_ujs
//= require jquery-ui/autocomplete
//= require autocomplete-rails

# app/assets/stylesheets/application.css     
*= require jquery-ui/autocomplete

楷模

首先创建关联:

# app/model/city.rb
has_many :businesses

# app/model/business.rb
belongs_to :city

如果要确保输入的城市在城市表中,请添加验证:

# app/models/business.rb
validates :city, presence: true

业务表有一个字段为 city_id 而不是城市名称。我们可以使用 getter 和 setter 方法在业务模型文件中创建一个虚拟属性。自从我从一个 4 岁的 RailsCast 那里得到它以来,可能有一种更新的方法来做到这一点,但它确实有效。

#getter method
def city_name
  city.try(:name)
end
#setter method
def city_name=(name)
  self.city = City.find_by(name: name) if name.present?
end

路线

这就是 gem 的魅力所在。添加一条到业务 REST 资源的路由,格式为 autocomplete_model_attribute:

# config/routes.rb
resources :businesses do
  get 'autocomplete_city_name' on: :collection
end

控制器

在控制器的顶部调用自动完成操作,将类名和属性传递给它。默认情况下,自动完成将匹配从单词开头开始的文本。如果您想搜索单词中的任何位置,请将“full”选项设置为 true,否则将其省略。

# app/controllers/business_controller.rb
autocomplete :city, :name, full: true

由于我们在业务模型中创建了 city_name 虚拟属性,因此我们需要将其添加到允许的参数列表中

def business_params
  params.require(:business).permit(:name, :address, :city_id, :state, :zip, :city_name)
end

意见

在创建或编辑新业务实例的表单中,使用 autocomplete_field 帮助器将 city_id 字段更改为 city_name 虚拟属性。'data-auto-focus' 选项将自动选择列表中的第一个值。如果你不想这样,就把它关掉。

# app/views/businesses/_form.html.erb
<div class="field">
  <%= f.label :city_name %><br>
  <%= f.autocomplete_field :city_name, autocomplete_city_name_businesses_path, 'data-auto-focus' => true %>
</div>

完毕!

于 2015-10-22T05:43:16.027 回答
0

我认为这不适validates_associated用于这种情况。我只是制作一个自定义validate方法。如果您的城市都以小写形式存储,这将起作用,否则使用humanize而不是downcase.

validate :city_must_exist_with_city_name

def city_must_exist_with_city_name
    if !City.find_by(name: city_name.downcase)
        errors.add(:item_name, "must be a valid city.")
    end
end

编辑:您可以通过这样做更进一步解决大小写问题:

if !City.where("lower(name) = ?", city_name.downcase).first
于 2015-10-12T23:52:20.357 回答
0

向您的字段添加自动完成功能city_name将解决您的问题。

首先,由于您BusinessCity对象之间的关联,您将希望返回一个business[city_id]参数而不是一个city_name参数(假设您使用的是form_for @business帮助器)。

如果你使用 jQuery,你可以使用 devbridge jQuery-Autocomplete 插件: https ://github.com/devbridge/jQuery-Autocomplete

创建一个以 JSON 格式返回城市列表的简单方法(需要 jbuilder gem,它应该默认包含在 Gemfile 中),例如:

in app/controllers/businesses_controller.rb:

def ac_cities
  @query = params[:query]
  @results = City.where('city_name LIKE ?', "#{@query}%").order(city_name: :asc).limit(10)
  respond_to do |format|
    format.json
  end
end

in app/views/businesses/ac_cities.json.jbuilder:

json.query @query
json.suggestions @results do |result|
  json.value "<div data-id=\"#{result.id}\">#{result.city_name}</div>"
end

city_name初始化 devbridge 实例并使用 CoffeeScript(或 JS)将其连接到您的字段。devbridge 插件还有一个onSelect功能,可以让您填充隐藏的business[city_id]表单字段。例如,给定上面返回的 JSON:

in app/assets/javascripts/businesses.js.coffee:

BusinessCitySelect = (suggestion) ->
  $(this).next('#business_city_id').val(suggestion.value.match(/data-id="(\d+)"/i)[1])
  $(this).val(suggestion.value.replace(new RegExp('<.*?\/?>', 'gi'), ''))

SearchACFormat = (suggestion, cv) ->
  pattern = '(<div.*?>.*?)('+cv+').*?'
  suggestion.value.replace(new RegExp(pattern, 'gi'), '$1<strong>$2<\/strong>')

$("#city_name").devbridgeAutocomplete({
  serviceUrl: "/businesses/ac_cities.json",
  formatResult: SearchACFormat,
  onSelect: BusinessCitySelect,
  noCache: true
})

SearchACFormat方法利用 devbridge 的formatResult功能来清理我们的 JSON,以便在city_name输入字段中正确显示,同时还提供突出显示。

于 2015-10-12T23:59:38.997 回答