Rails 3 HABTM Правильная форма для запроса с условием = массив - PullRequest
0 голосов
/ 13 апреля 2011

Я реализовал свои первые отношения HABTM и столкнулся с проблемой в моем запросе.

Я хочу проверить свой подход и выяснить, нашел ли я ошибку в коде AREL (или какой-либо другой части Rails).

У меня есть следующие модели

class Item < ActiveRecord::Base
  belongs_to :user
  belongs_to :category
  has_and_belongs_to_many :regions
end

class Region < ActiveRecord::Base
  has_ancestry
  has_and_belongs_to_many :items
end

У меня есть связанная таблица items_regions:

class CreateItemsRegionsTable < ActiveRecord::Migration
  def self.up
    create_table :items_regions, :id => false do |t|
      t.references :item, :null => false
      t.references :region, :null => false
    end
    add_index(:items_regions, [:item_id, :region_id], :unique => true)
  end

  def self.down
    drop_table :items_regions
  end
end

Моя цель состоит в том, чтобы создать область / запрос следующим образом:

Найти все предметы в регионе (и его субрегионах)

Драгоценный камень предков предоставляет метод для извлечения категорий потомков для региона в виде массива. В этом случае

ruby-1.9.2-p180 :167 > a = Region.find(4)
 => #<Region id: 4, name: "All", created_at: "2011-04-12 01:14:00", updated_at: "2011-04-12 01:14:00", ancestry: nil, cached_slug: "all"> 
ruby-1.9.2-p180 :168 > region_list = a.subtree_ids
 => [1, 2, 3, 4] 

Если в массиве только один элемент, работает следующее

items = Item.joins(:regions).where(["region_id =  ?", [1]])

Сгенерированный sql

"SELECT `items`.* FROM `items` INNER JOIN `items_regions` ON `items_regions`.`item_id` = `items`.`id` INNER JOIN `regions` ON `regions`.`id` = `items_regions`.`region_id` WHERE (region_id =  1)"

Однако, если в массиве несколько элементов, и я пытаюсь использовать IN

Item.joins(:regions).where(["region_id IN  ?", [1,2,3,4]])
ActiveRecord::StatementInvalid: Mysql::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1,2,3,4)' at line 1: SELECT `items`.* FROM `items` INNER JOIN `items_regions` ON `items_regions`.`item_id` = `items`.`id` INNER JOIN `regions` ON `regions`.`id` = `items_regions`.`region_id` WHERE (region_id IN  1,2,3,4)

Сгенерированный sql имеет ошибку в конце

"SELECT `items`.* FROM `items` INNER JOIN `items_regions` ON `items_regions`.`item_id` = `items`.`id` INNER JOIN `regions` ON `regions`.`id` = `items_regions`.`region_id` WHERE (region_id IN  1,2,3,4)"

последняя часть сгенерированного кода должна быть (region_id IN ("1,2,3,4"))

Если я редактирую sql вручную и запускаю его, я получаю то, что ожидаю.

Итак, два вопроса:

  1. Верен ли мой подход к случаю с одним значением?
  2. Является ли создание sql ошибкой или я настроил неправильно?

Спасибо Alan

Ответы [ 4 ]

3 голосов
/ 13 апреля 2011
.where('regions.id' => array)

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

Причина, по которой исходный запрос не работает, заключается в том, что вам действительно нужно указать действительный SQL.Так что в качестве альтернативы вы можете сделать

.where('region_id IN (?)', [1,2,3,4])
1 голос
/ 19 апреля 2011

Другие респонденты правы относительно использования хэша условий, но конкретная проблема, с которой вы столкнулись после этого, связана со спецификой поля: Mysql :: Ошибка: неизвестный столбец 'items.region_id' в ', где предложение'

Вы пытаетесь нарисовать условие, основанное на "region_id", но, поскольку вы явно не дали таблицу, по умолчанию используются "items". Похоже, ваш столбец на самом деле находится в таблице "item_regions". Попробуйте это:

where("item_regions.region_id IN (?)", [1,2,3,4])

Или альтернативно:

where(:item_regions => {:region_id => [1,2,3,4]})
0 голосов
/ 30 июня 2014

Я думаю, что самый чистый и идиоматический способ сделать это в Arel - это синтаксис вложенных хэшей, который позволяет избежать строковых литералов (и любых прямых ссылок на таблицу соединений HABTM):

Item.joins(:regions).where(regions: { id: [1,2,3,4] })
0 голосов
/ 19 апреля 2011

Вы пробовали

.where('region_id IN (?)', [1,2,3,4])

форма? Вам нужно, чтобы () был действительным.

...