Уже давно хотел избавиться от упоминания в запросах sql конструкций.
Но построение через хеши условий для поиска в нынейней версии RoR(2.1) к сожалению не возможно.
Такие запросы типа: Строка по маске, больше чем, меньше чем, больше, находиться в диапазоне, не находиться в диапазоне - построить с помощью хешей невозможно.
Объединение условий по OR опять же сводилось к написанию SQL.
Плагин conditions_fu расширяет возможности ActiveRecord::Base#find методами позволяющими решить вышеуказнные проблемы.
Установка
$ script/plugin install git://github.com/xgamerx/conditions_fu.git Использование
Person.all(:conditions => { :age.gt => 30, :name.like => "%упкин" })
найдет всех кто заканчивается на "упкин" старше 30 лет
Person.any(:conditions => { :age.gt => 30, :name.like => Person.all(:conditions => { :age.gt => 30, :name.like => "%name%" })
})
найдет всех старше 30 лет или чье имя заканчивается на "упкин"
Подробней и перечень операторов можно посомтреть тут rubyhammer.com/articles/2008-08-20-182530-conditions_fu-postroitel-zaprosov
Существует определенный класс задач, когда необходимо реализовать отношение "многие-ко-многим". Довольно интересным являеться случай когда узел может иметь больше одного родителя. Проще говоря, математический граф построенный на одной модели когда его элементы пренадлежат одной сущности.
Ruby CMF Web
\ | /
\_|_/
Rails
/ | \
/ | \
AR AV AC
Создадим миграцию для модели категорий
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.column :name, :string
t.timestamps
end
end
def self.down
drop_table :categories
end
end
Создадим миграцию для таблицы со связями
class Relations < ActiveRecord::Migration
def self.up
create_table :relations, :id => false do |t|
t.column :parent_id, :integer
t.column :child_id, :integer
end
end
def self.down
drop table :relations
end
end
Основной интерес составляет модель категорий и декларация связей has_and_belongs_to_many через вышеупомянутую таблицу.
class Category < ActiveRecord::Base
has_and_belongs_to_many :parents,
:join_table => 'relations',
:foreign_key => 'parent_id',
:association_foreign_key => 'child_id',
:class_name =>'Category'
has_and_belongs_to_many :children,
:join_table => 'relations',
:foreign_key => 'child_id',
:association_foreign_key => 'parent_id',
:name => 'Category'
end
Миграция заполняющая тестовые данные моделирующиее граф указанный на картинке
class TestDataGenerate < ActiveRecord::Migration
def self.up
c_ruby = Category.create :name => 'Ruby'
c_cmf = Category.create :name => 'CMF'
c_web = Category.create :name => 'WEB'
c_rails = Category.create :name => 'Rails'
c_ar = Category.create :name => 'AR'
c_av = Category.create :name => 'AV'
c_ac = Category.create :name => 'AC'
c_rails.parents << [c_ruby, c_cmf, c_web]
c_rails.children << [c_ar, c_av, c_ac]
end
def self.down
end
end
Переходим в консоль и проверяем правильность выполнения поставленной задачи. Находим категорию Rails и запрашиваем у нее имена(для краткости вывода) дочерних и родительских категорий.
>> rs = Category.find_by_name('Rails')
=> #
>> rs.children.find :all, :select => :name
=> [#, #, #]
>> rs.parents.find :all, :select => :name
=> [#, #, #]
Хочу отметить, что такое решение являеться независимым от синтаксиса SQL большенства реляционных БД отвечающим стандарту SQL92.
Другие варианты решения и нюансы описаны в статьях:
* http://szeryf.wordpress.com/2007/06/27/self-referential-many-to-many-relations-in-ruby-on-rails/
* http://wiki.rubyonrails.org/rails/pages/HowToCreateASelfReferentialManyToManyRelationship
Существует решение в виде плагина: http://tammersaleh.com/posts/acts_as_graph
Но как пишет его автор, плагин давно не тестировался.
P.S. acts_as_graph -реализует рекурсивный обход графа.
Немно смешно и грустно. Но иногда встречаются посты на тему "все не работает".
Кот в сапогах попросил людоеда превратиться в мышь.
- Да. Ответил людоед. ОП - и.... превратился в мышь...
- Кот кинулся на мышь .... а мыш взлетела и повисла на потолке... потому как _летучей_ оказалась !
Вывод: если хотите получить правильный результат, то максимально точно формулируйте задачу...
Платформа: win, nix ?
Версия rails ?
Хостинг или локальное использование ?
Версия базы данных ?
Кодировка базы данных ?
Можно прописать это все в коментариях к профайлу и отображать в каждом посте, если вышеуказанные параметры более-менее стабильны.
Каждая мелочь приближает вас с решению вашей проблемы :-)
P.S. "шеф все пропало" - приключения капитана Врунгеля :-)
Все значения колонок в балице автоматически дочтупный через базовые аксесоры для Active Record объекта, но иногда вам необходимо создать какоето свое поведение для этой колонки. Это можно легко осуществить с помощью переписывания дефолтных аксесоров (используя те же самые названия как атрибуты) и вызывая метод read_attribute(attr_name) и write_attribute(attr_name, value) для реального изменения их.
Пример:
class Song < ActiveRecord::Base
# Преобразовываем числов в минуты и обрабоно для хранения в базе
def length=(minutes)
write_attribute(:length, minutes.to_i * 60)
end
def length
read_attribute(:length) / 60
end
end
Вы можете в качестве альтернативы использовать self[:attribute]=(value) и self[:attribute] вместо write_attribute(:attribute, value) и read_attribute(:attribute) как коротку форму записи.
>> Time.now + 11.days
=> Tue Aug 12 11:26:31 0300 2008
>> Time.now + 1.days
=> Sat Aug 02 11:26:35 0300 2008
>> Time.now + 1.day
=> Sat Aug 02 11:26:43 0300 2008
>> Time.now + 11.day
=> Tue Aug 12 11:26:51 0300 2008