понедельник, 28 июля 2008 г.

Простая выборка записей через метод Find. Кратко о Conditions.

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


































# name last name age create_date updated_at
1 John Doe 31 29.07.2008 29.07.2008
2 Sergey Saenko 23 29.07.2008 29.07.2008
3 Ivan Petrenko 25 29.07.2008 29.07.2008


Как же работает с SQL используя ActiveRecord?

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

User.find(1) - результатом такого запроса будет первая запись нашей таблицы, которая соответствует пользователю John Doe, результатом выполнения запроса будет обьект класа User.
Таким образом, если метод find() принимает в качестве параметра число, то он будет искать запись, ІD которой будет соответствовать параметру.
User.find(:all) - вернет коллекцию єкземпляров класса User. Согласно переданому параметру - выберет все записи из таблицы.
User.find(:first), и по аналогии User.find(:last) - эти два запроса соответственно вернут первого и последнего зарегистрированного пользователя в системе.


При генерации любой модели, независимо от полей таблицы заданных в миграции - в автоматическом режиме создаются 2 дополнительных поля:
create_date и updated_at которые имеют тип дата/время. Оба поле заполняется автоматически при создании записи(дата создания и дата обновления сначала совпадают), но второе поле автоматически изменяется при любой модификации записи.


Так к чему же я это все писал - при любой выборке коллекции записей, данные будут отсортированы как-раз по полю create_date (если порядок сортировки не задан в явном виде). Таким образом я точно знаю что User.find(:first) вернет первый созданный обьект, а User.find(:last) соответствует последнему созданному экземпляру User.


Немного отвлекусь от темы. Рельсы сделаны очень интиресным образом, сейчас немного объясню свое понимание почему же фреймворк назвали именно Рельсы. Все что можно было задать по-умолчанию - разработчики фрейворка уже описали. Другими словами частичное поведение всех методов и классов при их генерации уже заданы, и менять программисту нужно только то что ему надо. Таким образом у нас как-бы есть уже "сырые заготовки", которые осталось только немного подправить и настроить. Создается впечатление, что все делается как-бы по накатанному пути - "как по рельсам". На самом деле все не так, Руби очень гибкий язык программирования, который позволяет реализовать в принципе любой алгоритм. Рельсы писались на Руби, то понятно что это очень быстрый в разработке и довольно гибкий - учитывая язык на котром он написан - инструмент разработки. Но повторяю - это только мое субъективное мнение.


Для того чтобы задать параметры выборки используется метод :conditions, например:
User.find(:all, :conditions=>"age=23") - выберет второго пользователя.
Чтобы передавать в :conditions параметры, можно использовать следующие способы:
name = "Sergey"

1. User.find(:all,:conditions=>["name=:name",{:name=>name}])

2. User.find(:all,:conditions=>["name= ?",name])

3. User.find(:all,:conditions=>"name=#{name}")

Рельса в любом из 3 случаев предварительно формирует для БД запрос, а потом его отсылает. Тоесть неверно говорить что что параметры на сервере в 1 и 2 передаются отдельно, а ядро запроса кешируется. В любом случае на сервер идет готовый запрос, проверял лично. По скорости работы выигрывает на сотые доли секунды 3 вариант, но его целиесобразно использовать только если мы не вставляем в параметры пользовательские данные. Для того чтобы предотвратить сюл-иньекции используйте вариант 1 или 2.

И как завершение я расскажу разницу вариантов 1 и 2. В первом случае, мы передаем именованые параметры, тоесть, в случае если надо чтобы 1 параметр встречался в запросе боьше одного раза, во 2 варианте придется через запятую 2 раза его дублировать:


User.find(:all,:conditions=>["start_date= ? and end_date = ?",current,current])


а в 1 варианте можно сделать так:


User.find(:all,:conditions=>["start_date= :current_date and end_date = :current_date",{:current_date=>current}])


таким образом удается избавится от избыточности параметров


Сonditions- очень похожи на С-функцию sprintf, так что сложности с пониманием быть не должно. А само тело условия формируется аналогично постороения условия запроса через WHERE.

2 комментария:

  1. Спасибо огромное! Вы так понятно объясняете и отвечаете как раз на те вопросы, которые хочется задать.

    Не могли бы Вы добавить описание, как выбрать связанные данные из двух таблиц?

    Что-то вроде "SELECT tab1.long_eng, tab2.translation FROM tab1, tab2 WHERE tab1.l_eng_word LIKE tab2.eng_word"

    ОтветитьУдалить
  2. Освободится немного времени и обязательно отвечу! Думаю в понедльник-вторник.

    ОтветитьУдалить