Rails 3 Tutorial: Модели: Введение

февраля 19, 2011  |  Published in Ruby on Rails, Ruby on Rails 3  |  7 Comments

Ruby on Rails 3По многочисленным просьбам в количестве 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.

Лучшая благодарность автору -  ваши комментарии и популяризация блога.

Tags: , , ,

Responses

  1. says:

    февраля 19, 2011 at 21:33 (#)

    Начинание полезное, думаю многие оценят.
    Пара моментов, за которые глаз зацепился:
    1. Использование синкасиса специфичного для ruby 1.9 (я про передачу хэшей в методы) — имхо не самая лучшая идея дла цикла статей для начинающих — кто-то может встрять, пытаясь запустить это на 1.8. Или по крайней мере стоит явно сказать об этом вначале статьи.
    2. Если я не ошибаюсь, методы типа User.find(:first) depricated в пользу User.first ()

  2. admin says:

    февраля 19, 2011 at 23:02 (#)

    1. Спасибо за замечание, стоило добавить предупреждение, что в статье используется 1.9.х версия Ruby, сейчас допишу.

    2. И за второе замечание спасибо, совсем не подумал о будущей версии Rails 3.1. Вы правы, там find останется чито для приема id и коллекции id записей. Здесь Rails идет по пути «Есть только один способ сделать это».

  3. Rails Tutorial: Модели: Простая выборка данных | Ruby on Rails c нуля! says:

    февраля 20, 2011 at 16:32 (#)

    [...] и записи в таблице. Как это сделать читайте в статье Rails Tutorial: Модели: Введение. Итак, нам необхобима модель Person (id: integer, name: string, last_name: [...]

  4. Александр says:

    февраля 26, 2011 at 08:58 (#)

    Спасибо за статью! Еслы можно было-бы статью на следующую тему.
    У меня возникли проблеми при связке таблиц. Есть таблица Subjects и Pages.
    В модели Subject есть строчка has_one :page
    В модели page есть строчка belongs_to :subject (я так понимаю что етим я связал)
    В Rails console набрал следующее:
    subject = Subject.new(параметры);
    subject.page — выдает ошибку. (Я так понял наверное или ето устаревший метод, или нет какогото gema)
    Помогите разобратся, заранее спасибо!

  5. Александр says:

    февраля 26, 2011 at 09:19 (#)

    ошыбка method_missing

  6. admin says:

    февраля 26, 2011 at 14:23 (#)

    Александр, а у вас столбец subject_id в таблице pages присутствует? Просто введите в консоль Page, чтобы узнать. Если нет — создайте миграцию и впишите туда добавление столбца subject_id, именно в нем хранится связь между моделями Subject и Page. После создания миграции не забудьте выполнить rake:migrate для того, чтобы миграция выполнилась.

  7. Michail says:

    августа 8, 2011 at 09:24 (#)

    Спасибо за статью, а когда будет продолжение

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.