Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

ActiveRecord Hacks

Anna | 20.06.2014 | нет комментариев

Сегодня я поделюсь своим комплектом не неизменно явственных функций и вероятностей Active Record, с которыми я столкнулся в процессе разработки Ruby on Rails приложений либо обнаружил в чужих блогах.

Обход валидации при применении 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

разрешает сделать выборку из всех аккаунтов, в которых есть непрочитанные собщения.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB