Rails 3 Tutorial: Модели: Введение
февраля 19, 2011 | Published in Ruby on Rails, Ruby on Rails 3 | 7 Comments
По многочисленным просьбам в количестве 9 штук, начинаю рубрику на RubyDev — Rails 3 Tutorial в которой я пройдусь по всем составляющим фреймворка Rails, а затем пойдут более практичные и подробные статьи, например о деплое, о внутреннем устройстве Ruby on Rails и т.д.. Учебник не будет самодостаточным, часто на страницах учебника я буду ссылаться на самостоятельные статьи. Ах да, еще статьи будут публиковаться в не правильном порядке, статьи размещенные в правильной последовательности вы найдете на специальной странице, которую я создам, когда их наберется несколько штук.
Существует такой шаблон / подход к разработке приложений, как MVC, который состоит в том, что приложение разбивается на 3 части: Model, View и Controller, каждая из которых выполняет свою определенную роль. В этой статье я не буду рассказывать о том, что такое MVC, просто скажу, что такой подход к разработке хорош тем, что при внисении изменений в одну часть приложения, программисту, по идее, не нужно переписывать все приложени.
Model — модель, это та часть приложения, которая отвечает за работу с данными. В Rails модель реализована в виде ActiveRecord, однако, благодаря модульности привнесенной в Rails из Merb, вы можете использовать другие средства работы с базой данных, например DataMapper или Sequel. В данном цыкле статей речь будет идти лишь о ActiveRecord, однако, в будущем, возможно, будут опубликованы статьи по работе с DataMapper и Sequel.
Для начала изучения вам необходимо установить Ruby (устанавливайте версию 1.9.2), SQLite3 и собственно Rails 3.
После установки Rails создаем новое приложение, для этого воспользуемся имеющщимся в Rails генератором:
$ rails new model_app
Теперь обратите внимание на папку app содержащуюся в новорожденном проекте model_app/. Эта папка будет содержать основную часть приложения на Rails для любого вашего проекта. В директории app/ можно найти поддиректорию models/ в которой собственно и будут содержаться все наши модели.
Давайте создадим первую модель, опять-таки воспользовавшись встроенным в Rails генератором:
$ rails g model User name:string login:string password:string
invoke active_record
create db/migrate/20110218203227_create_users.rb
create app/models/user.rb
invoke test_unit
create test/unit/user_test.rb
create test/fixtures/users.yml
Как видите,было создано 4 файла:
app/models/user.rb — файл содержащий модель User
db/migrate/20110218203227_create_users.rb — файл миграции
test/unit/user_test.rb — файл с тестами
test/fixtures/users.yml — файл фикстуры
В этой статье мы рассмотрим только саму модель и немножко затроним тему миграций.
Если открыть файл содержащий нашу модель, то мы увидим, что там практическки ничего нет:
class User < ActiveRecord::Base end
Однако наша модель User уже только благодаря наследованию от ActiveRecord::Base умеет многое!
Для того, чтобы начать работать с моделью, необходимо сначала создать базу данных и соответствующую модели таблицу в базе. Прежде, чем мы создадим базу данных и таблицу в ней, давайте обратим наше внимание на файл конфигурации базы данных приложения Rails — файл config/database.yml. В этом файле содержатся настройки нашего приложения для работы с БД для трех окружений: development, production и test. Из названий каждого окружения понятно, какое из них когда используется. В данный момент, разрабатывая приложение мы используем окружение development:
development: adapter: sqlite3 database: db/development.sqlite3 pool: 5 timeout: 5000
Здесь указывается сервер базы данных с которой мы работаем (adapter: sqlite3), адрес базы данных (database: db/development.sqlite3), количество одновременных подключений к БД (pool: 5) и максимальная продолжительность запроса в миллисекндах (timeout: 5000). Эти данные необходимы Rails для того, чтобы понять с какой базой данных следует работать и как.
Теперь давайте взглянем на файл миграции db/migrate/20110218203227_create_users.rb:
class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.string :name t.string :login t.string :password t.timestamps end end def self.down drop_table :users end end
Файлы миграции всегда состоят из двух методов класса .up и .down. Метод .up содержит код, который будет использоваться при выполнении миграции, а метод .down, соответственно, код, для отката действий произведенных данной миграцией. В этой статье о миграция рассказываться не будет, но тем не менее я просто обязан в пару строк рассказать, что это такое.
Миграции — это компонент модельной прослойки (представьте наше приложение в виде коктейля мриготовленного в способ называемый «build», который состоит из 3 слоев: model, view и controller). Хотя миграции являются компонентом моделей, под моделью все-же подразумевают собственно тот самый файл app/models/user.rb. Итак, зачем же необходимы миграции? Все очень просто! Модель — реализует сущность, объект, данные о котором храняться, в нашем случае в базе данных, и предоставляет методы для работы с этим объектом и создания новых объектов. Миграции — это просто завернутые в красивый и удобный для использования вид запросы к базе данных на создание, удаление или изменение структуры таблиц, то есть миграции работают не с самими данными, а с таблицами, которые их хранят. Вот такое вот разделение труда.
Миграция определенная выше при запуске создаст таблицу users c тремя столбцами: name, login и password, все из которых будут иметь тип string (О типах данных будет написано в отдельной статье о миграциях. Вы также можете почитать о них здесь), также двумя столбцами в которые будут записываться дата создания записи (created_at) и дата обновления (updated_at), кроме того, будет создан столбец id содержащий уникальный целочисленный идентификатор для каждой записи.
Давайте запустим миграцию и создадим необходимую нам таблицу:
$ rake db:migrate
(in /home/vladimir/proj/model_app)
== CreateUsers: migrating ====================================================
— create_table(:users)
-> 0.0053s
== CreateUsers: migrated (0.0055s) ===========================================
Таблица создана! Теперь мы можем начать работать с нашей моделью! Чтобы статья не была совсем скучной, давайте все-таки поработаем немножко с нашей базой данных, создадим, удалим и изменим несколько записей! Наиболее простой способ это сделать — воспользоваться консолью Rails — Rails’овым аналогом для irb:
$ rails c
Loading development environment (Rails 3.0.4)
ruby-1.9.2-p0 >
Список свойств экземпляра модели и их тип:
User => User(id: integer, name: string, login: string, password: string, created_at: datetime, updated_at: datetime)
Создаем новую модель:
user = User.new name:'Vasya', login:'vasya', password:'paswd' => #<User id: nil, name: "Vasya", login: "vasya", password: "paswd", created_at: nil, updated_at: nil>
Еще можно вот так:
user = User.new => #<User id: nil, name: nil, login: nil, password: nil, created_at: nil, updated_at: nil> user.name = 'Vova' => "Vova" user.login = 'vova' => "vova" user.password = '123' => "123" user => #<User id: nil, name: "Vova", login: "vova", password: "123", created_at: nil, updated_at: nil>
Мы создали модель, но не запись в таблице базы данных! Модель храниться в оперативной памяти компьютера и мы можем работать с нею без каких-либо запросов к базе данных! Для того, чтобы созранить модель в базу данных воспользуемся методом save, но врежде добавим уникальный идентификатор id и время создания поста — created_at:
user.id = 0 => 0 user.created_at = Time.now => 2011-02-18 23:25:45 +0200
А теперь сохраним модель в базу данных:
user.save => true
Запись успешно сохранена!
Создадим еще одну модель:
user = User.new id:1, created_at: Time.now, name:'Petya', login:'petya', password: 'lolpass123' => #<User id: nil, name: "Petya", login: "petya", password: "lolpass123", created_at: "2011-02-18 21:30:08", updated_at: nil>
Добавляем запись в БД:
user.new_record? => true user.save => true user.new_record? => false
Метод new_record? возвращает false, если модель сохранена в базе данных и true, если нет.
Хочу заметить, что свойства :id, :created_at и :updated_at можно не указывать (на самом деле их очень редко специально указывают), потому, что при сохранении экземпляра модели в базу данных они будут сгенерированы автоматически.
Получаем все объекты модели User (записи сохраненные и не сохраненные):
User.all => [ #<User id: 0, name: "Vova", login: "vova", password: "123", created_at: "2011-02-18 21:25:45", updated_at: "2011-02-18 21:27:14">, #<User id: 1, name: "Vasya", login: "vasya", password: "paswd", created_at: "2011-02-18 21:18:16", updated_at: "2011-02-18 21:18:16">, #<User id: 2, name: "Petya", login: "petya", password: "lolpass123", created_at: "2011-02-18 21:30:08", updated_at: "2011-02-18 21:30:57">] User.all.each {|r| puts r.new_record?} false false false
Итак, мы создали 3 модели User и сохранили их все в базу данных.
Давайте попробуем удалить одну запись:
user = User.find(0) => #<User id: 0, name: "Vova", login: "vova", password: "123", created_at: "2011-02-18 21:25:45", updated_at: "2011-02-18 21:27:14"> user.delete => #<User id: 0, name: "Vova", login: "vova", password: "123", created_at: "2011-02-18 21:25:45", updated_at: "2011-02-18 21:27:14"> User.count => 2 # осталось 2 экземпляра модели
А теперь, давайте отредактируем одну модель. Представим, что пользователь Петя решил помянять свой пароль на более надежный:
user = User.find(2) => #<User id: 2, name: "Petya", login: "petya", password: "lolpass123", created_at: "2011-02-18 21:30:08", updated_at: "2011-02-18 21:30:57"> user.password = '34dFg&5$hHWEr@#195-1uT' => "34dFg&5$hHWEr@#195-1uT" user.save => true
Метод find принимает в качестве аргумента индекс записи в БД, или массив записей и возвращает соответствующие объекты — экземпляры класса модели User:
User.find(1) => #<User id: 1, name: "Vasya", login: "vasya", password: "paswd", created_at: "2011-02-18 21:18:16", updated_at: "2011-02-18 21:18:16"> User.find([1,2]) => [#<User id: 1, name: "Vasya", login: "vasya", password: "paswd", created_at: "2011-02-18 21:18:16", updated_at: "2011-02-18 21:18:16">, #<User id: 2, name: "Petya", login: "petya", password: "34dFg&5$hHWEr@#195-1uT", created_at: "2011-02-18 21:30:08", updated_at: "2011-02-18 21:46:03">]
Также find может принимать значения :first и :last для возвата соответственно первой и последней записи, но по словам разработчиков Rails эти парметры будут удалены из следующей минорной версии — Rails 3.1. Вместо них уже сейчас рекуомендуется использовать методы .first и .last:
User.find :first => #<User id: 1, name: "Vasya", login: "vasya", password: "paswd", created_at: "2011-02-18 21:18:16", updated_at: "2011-02-18 21:18:16"> User.find :last => #<User id: 2, name: "Petya", login: "petya", password: "34dFg&5$hHWEr@#195-1uT", created_at: "2011-02-18 21:30:08", updated_at: "2011-02-18 21:46:03"> # Лучше используйте .first и .last: User.first => #<User id: 1, name: "Vasya", login: "vasya", password: "paswd", created_at: "2011-02-18 21:18:16", updated_at: "2011-02-18 21:18:16"> User.last => #<User id: 2, name: "Petya", login: "petya", password: "34dFg&5$hHWEr@#195-1uT", created_at: "2011-02-18 21:30:08", updated_at: "2011-02-18 21:46:03">
На сегодня все! В следующей статье я постараюсь рассказать об отношения между моделями, либо более поднобно осветить запросы на выборку данных.
P.S. Спасибо Александру за замечание в комментариях по поводу использования .find c опциями :first и :last.
Лучшая благодарность автору - ваши комментарии и популяризация блога.
февраля 19, 2011 at 21:33 (#)
Начинание полезное, думаю многие оценят.
Пара моментов, за которые глаз зацепился:
1. Использование синкасиса специфичного для ruby 1.9 (я про передачу хэшей в методы) — имхо не самая лучшая идея дла цикла статей для начинающих — кто-то может встрять, пытаясь запустить это на 1.8. Или по крайней мере стоит явно сказать об этом вначале статьи.
2. Если я не ошибаюсь, методы типа User.find(:first) depricated в пользу User.first ()
февраля 19, 2011 at 23:02 (#)
1. Спасибо за замечание, стоило добавить предупреждение, что в статье используется 1.9.х версия Ruby, сейчас допишу.
2. И за второе замечание спасибо, совсем не подумал о будущей версии Rails 3.1. Вы правы, там find останется чито для приема id и коллекции id записей. Здесь Rails идет по пути «Есть только один способ сделать это».
февраля 20, 2011 at 16:32 (#)
[...] и записи в таблице. Как это сделать читайте в статье Rails Tutorial: Модели: Введение. Итак, нам необхобима модель Person (id: integer, name: string, last_name: [...]
февраля 26, 2011 at 08:58 (#)
Спасибо за статью! Еслы можно было-бы статью на следующую тему.
У меня возникли проблеми при связке таблиц. Есть таблица Subjects и Pages.
В модели Subject есть строчка has_one :page
В модели page есть строчка belongs_to :subject (я так понимаю что етим я связал)
В Rails console набрал следующее:
subject = Subject.new(параметры);
subject.page — выдает ошибку. (Я так понял наверное или ето устаревший метод, или нет какогото gema)
Помогите разобратся, заранее спасибо!
февраля 26, 2011 at 09:19 (#)
ошыбка method_missing
февраля 26, 2011 at 14:23 (#)
Александр, а у вас столбец subject_id в таблице pages присутствует? Просто введите в консоль Page, чтобы узнать. Если нет — создайте миграцию и впишите туда добавление столбца subject_id, именно в нем хранится связь между моделями Subject и Page. После создания миграции не забудьте выполнить rake:migrate для того, чтобы миграция выполнилась.
августа 8, 2011 at 09:24 (#)
Спасибо за статью, а когда будет продолжение