Обход валидации при использовании update_attributes
Стандартный метод update_attributes не имеет ключа, позволяющему обойти валидацию, поэтому приходится прибегать к assign_attributes с последующим save
@user = User.find(params[:id]) @user.assign_attributes(:name, "") @user.save(validate: false)
Разумеется – лучше не прибегать к этому способу очень часто 🙂
Разделение на 2 непересекающихся коллекции
Иногда возникает задача разделения выборки объектов на 2 непересекающиеся коллекции. Сделать это можно с помощью такого использования scope.
Article < ActiveRecord::Base scope :unchecked, where(:checked => false) #or this, apologies for somewhat unefficient, but you already seem to have several queries scope :unchecked2, lambda { |checked| where(["id not in (?)", checked.pluck(:id)]) } end
Ну и соответственно доступ к обеим коллекциям можн ополучить с помощью
Article.unchecked Article.unchecked2(@unchecked_articles)
pluck
В предыдущем примере я использовал метод pluck. Наверняка каждый из вас использовал что-то типа
Article.all.select(:title).map(&:title)
или даже
Article.all.map(&:title)
Так вот – pluck позволяет сделать это проще
Article.all.pluck(:title)
Доступ к базовому классу
В процессе работы над одним проектом я столкнулся с большой вложенностью классов моделей и необходимостью добраться до корневого класса. Классы выглядели примерно так:
class Art < ActiveRecord::Base end class Picture < Art end class PlainPicture < Picture end
Для того, чтобы добраться из PlainPicture до Art можно использовать метод becomes
@plain_pictures = PlainPicture.all @plain_pictures.map { |i| if i.class < Art then i.becomes(Art) else i end }.each do |pp| #do something with Art end
first_or_create и first_or_initialize
Еще один замечательный метод – first_or_create. Из названия ясно что он делает, а мы давайте посмотрим как его можно использовать
Art.where(name: "Black square").first_or_create
Также мы его можем использовать в блочной конструкции
Art.where(name: "Black square").first_or_create do |art| art.author = "Malevich" end
А если вы не хотите сохранять – можно использовать first_or_initialize например таким образом
@art = Art.where(name: "Black square").first_or_initialize
scoped и none
Обратите внимание на еще 2 замечательных метода – scoped и none. Как они работают – покажу на примере, при этом хочу отметить, что надо разделять их поведение в rails3 и rails4, так как оно различается.
def filter(filter_name) case filter_name when :all scoped when :published where(:published => true) when :unpublished where(:published => false) else none end end
Как поведет себя метод в случае передачи в него :published и :unpublished я надеюсь вам понятно, различия в версиях rails тут нет.
Использование scoped в нашем примере в случае rails3 позволяет создать анонимный скоп, который может использоваться для сложных составных запросов. Если попытаться его применить в rails4, то можно увидеть сообщение, что метод стал deprecated и вместо него предлагается использовать Model.all. В случае же rails3 Model.all возвращает не ожидаемый нами ActiveRecord::Relation, а Array.
Ситуация с none похожа на scoped с точностью до наоборот 🙂 Этот метод возвращает пустой ActiveRecord::Relation, но работает он только в rails4. Нужен он в том случае, если нужно вернуть нулевые результаты, Для использования в rails3 есть такой workaround:
scope :none, where(:id => nil).where("id IS NOT ?", nil)
или даже такой (например в initializer)
class ActiveRecord::Base def self.none where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil))) end end
find_each
Метод find_each очень удобен для того, чтобы обработать большое количество записей из базы данных. Можно было бы конечно сделать выборку типа
Article.where(published: true).each do |article| #do something end
Но в таком случае нам придется хранить в памяти всю выборку целиком, что в случае большого объема данных очень нерентабельно. В этом случае правильнее будет использовать такой подход
Article.where(published: true).find_each do |article| #do something end
который небольшими выборками (по 1000 объектов за раз по умолчанию) обрабатывает данные.
to_sql и explain
Два метода, которые помогут вам разобраться как работает ваш запрос.
Art.joins(:user).to_sql
вернет вам sql-запрос, который приложение составит для завпроса в базу данных, а
Art.joins(:user).explain
покажет техническую информацию по запросу – примерное количество времени, объем выборки и другие данные.
scoping
Этот метод позволяет сделать выборку внутри выборки, например
Article.where(published: true).scoping do Article.first end
осуществит запрос типа
SELECT * FROM articles WHERE published = true LIMIT 1
merge
Еще один интересный метод, который позволяет пересечь несколько выборок. Например
class Account < ActiveRecord::Base # ... # Returns all the accounts that have unread messages. def self.with_unread_messages joins(:messages).merge( Message.unread ) end end
позволяет сделать выборку из всех аккаунтов, в которых есть непрочитанные собщения.
ссылка на оригинал статьи http://habrahabr.ru/post/181145/
Добавить комментарий