Сложные запросы в Rails - PullRequest
       18

Сложные запросы в Rails

4 голосов
/ 28 августа 2009

Rails и ActiveRecord отлично справляются с тем, что я считаю чрезвычайно простыми запросами, имеющими дело с одной моделью. Теперь у меня есть ситуация, когда мне нужно сделать что-то немного более сложное, но все же тривиальное, и я не знаю, как это сделать.

У меня есть User, что has_many :images. Каждый Image has_many :thumbnails.

На моей индексной странице /images я хочу отобразить миниатюру small для каждого изображения, которое принадлежит зарегистрированному пользователю. Простой запрос, но поскольку для этого требуются условия для нескольких таблиц, я не уверен, как подходить к нему по-настоящему в стиле Rails. Я бы предпочел избегать написания SQL и справляться с вытекающими отсюда головными болями.

Я вижу, что рельсы предлагают опцию :joins, но это кажется довольно не элегантным задним числом. Это лучший способ удовлетворить такое требование или есть лучший способ, которого я просто не нашел?

Спасибо.

ОБНОВЛЕНИЕ: Мне сказали об именованных областях, и я должен признать, что сейчас испытываю к ним очень неуместные чувства. Это позволяет применять довольно сложные условия очень элегантно с большим количеством синтаксического сахара. Например, я могу создать именованную область в моей модели изображений для изображений, принадлежащих данному пользователю, и сделать ее динамической:

class Image < ActiveRecord::Base
  named_scope :owned_by, 
              lambda { 
                |user_id| { 
                  :conditions => { :user_id => user_id } 
                } 
              }
end

Я также могу применить именованную область на моей модели миниатюр, чтобы указать маленькую миниатюру:

class Thumbnail < ActiveRecord::Base
  named_scope :small, :conditions => { :name => 'Small' }
end

Теперь я могу связать их вместе, чтобы сделать довольно мощные вещи, которые читаются красиво. В моем контроллере я могу вернуть все изображения для данного пользователя:

@images = Image.owned_by( current_user )

Однако, на мой взгляд, я хочу отобразить маленькую миниатюру, так что мы сильно цепляемся:

<% for image in @images %>
  <tr>
    <td><%= h( image.name ) %></td>
    <td><%= link_to( image_tag( image.thumbnails.small.first.binary.uri, :alt => h( image.name ), :title => h( image.description ) ), :action => :show, :id => image.id ) %></td>
  </tr>
<% end %>

Проверьте image_tag. Для каждого изображения он идентифицирует первую маленькую миниатюру, а затем связывает ее, чтобы получить физическое местоположение файла. Это не совсем то, что я искал, так как для каждого изображения требуется дополнительное попадание в базу данных, но, возможно, это более точно соответствует моим потребностям в данном конкретном случае.

Ответы [ 3 ]

3 голосов
/ 28 августа 2009

RailsGuides всегда удобны для этого, я провел там немало времени.

Проверьте связь has_many: through, которая позволяет модели Images присоединиться к этим таблицам и разрешить дополнительные поля, связанные с изображением.

Thumbnail
has_many :images
has_many :users, :through => :images

Image
belongs_to :user
belongs_to :thumbnail

User
has_many :images
has_many :thumbnails, :through => :images

Тогда при доступе к миниатюрам, связанным с пользователем, вы можете сделать;

@user.thumbnails

Или, возможно, миниатюра должна иметь одно изображение и одного пользователя?

Thumbnail
belongs_to :image

Image
belongs_to :user
has_one :thumbnail

User
has_many :images
has_many :thumbnails, :through => :images

Вот пример, который я почерпнул из этого.

2 голосов
/ 29 августа 2009

Чтобы красиво написать сложные запросы, вы также можете использовать что-то вроде белка. Squirrel - это плагин, который позволяет вам писать сложные запросы более естественным образом:

User.find(:all) do
  first_name == "Jon"         # first_name = 'Jon'
  email =~ "%@thoughtbot.com" # email LIKE '%@thoughtbot.com'
  created_at >= 4.years.ago   # created_at >= '2004-06-27 10:34:22'
  awesomeness <=> (1..10)     # awesomeness BETWEEN 1 AND 10
  banned_since == nil         # banned_since IS NULL
end

Вы даже можете использовать ассоциации (что вам нужно):

Post.find(:all) do
  user.first_name.contains? "Jon" 
end

Больше информации на Белка - естественный вид для рельсов Кроме того, не забудьте проверить их вики-страницу на Github.

0 голосов
/ 28 августа 2009

для конкретного пользователя вы можете найти все эскизы, выполнив что-то вроде этого

@user = User.find(:id, :include => :thumbnails)

: миниатюры должны быть определены в пользовательской модели как ассоциация, подобная показанной в его ответе revgum.

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