Поиск…


Вступление

ActiveRecord - это M в MVC, который является уровнем системы, отвечающей за представление бизнес-данных и логики. Метод, который соединяет богатые объекты приложения с таблицами в системе управления реляционными базами данных, - это O bject R elational M apper ( ORM ).

ActiveRecord будет выполнять запросы в базе данных для вас и совместим с большинством систем баз данных. Независимо от того, какую систему баз данных вы используете, формат метода ActiveRecord всегда будет таким же.

.где

Метод where доступен для любой модели ActiveRecord и позволяет запрашивать базу данных для набора записей, соответствующих заданным критериям.

Метод where принимает хэш, где ключи соответствуют именам столбцов в таблице, представленной моделью.

В качестве простого примера мы будем использовать следующую модель:

class Person < ActiveRecord::Base
  #attribute :first_name, :string
  #attribute :last_name, :string
end

Чтобы найти всех людей с первым именем Sven :

people = Person.where(first_name: 'Sven')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven'"

Чтобы найти всех людей с именем Sven и фамилией Schrodinger :

people = Person.where(first_name: 'Sven', last_name: 'Schrodinger')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven' AND last_name='Schrodinger'"

В приведенном выше примере вывод sql показывает, что записи будут возвращаться только в том случае, если совпадают имя first_name и last_name .

запрос с условием ИЛИ

Чтобы найти записи с first_name == 'Bruce' OR last_name == 'Wayne'

User.where('first_name = ? or last_name = ?', 'Bruce', 'Wayne')
# SELECT "users".* FROM "users" WHERE (first_name = 'Bruce' or last_name = 'Wayne')

. где с массивом

Метод where для любой модели ActiveRecord может использоваться для генерации SQL формы WHERE column_name IN (a, b, c, ...) . Это достигается передачей массива в качестве аргумента.

В качестве простого примера мы будем использовать следующую модель:

class Person < ActiveRecord::Base
  #attribute :first_name, :string
  #attribute :last_name, :string
end

people = Person.where(first_name: ['Mark', 'Mary'])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary')"

Если массив содержит nil , то SQL будет изменено , чтобы проверить , если столбец является null :

people = Person.where(first_name: ['Mark', 'Mary', nil])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary') OR first_name IS NULL"

Области применения

Области действия действуют как предопределенные фильтры на моделях ActiveRecord .

Область определения определяется с помощью метода класса scope .

В качестве простого примера мы будем использовать следующую модель:

class Person < ActiveRecord::Base
  #attribute :first_name, :string
  #attribute :last_name, :string
  #attribute :age, :integer

  # define a scope to get all people under 17
  scope :minors, -> { where(age: 0..17) }

  # define a scope to search a person by last name
  scope :with_last_name, ->(name) { where(last_name: name) }

end

Scopes можно вызывать непосредственно из класса модели:

minors = Person.minors

Области могут быть связаны:

peters_children = Person.minors.with_last_name('Peters')

Метод where и другие методы типа запроса также могут быть связаны:

mary_smith = Person.with_last_name('Smith').where(first_name: 'Mary')

За кулисами области - это просто синтаксический сахар для стандартного метода класса. Например, эти методы функционально идентичны:

scope :with_last_name, ->(name) { where(name: name) }

# This ^ is the same as this:

def self.with_last_name(name)
  where(name: name)
end

Область по умолчанию

в вашей модели, чтобы установить область по умолчанию для всех операций над моделью.

Существует одна заметная разница между методом scope методом класса: scope -defined scopes всегда возвращают ActiveRecord::Relation , даже если логика внутри возвращает nil. Однако методы класса не имеют такой защитной сетки и могут разрушать цепочки, если они возвращают что-то еще.

where.not

where clauses можно where.not синтаксиса where.not :

class Person < ApplicationRecord
  #attribute :first_name, :string
end

people = Person.where.not(first_name: ['Mark', 'Mary'])
# => SELECT "people".* FROM "people" WHERE "people"."first_name" NOT IN ('Mark', 'Mary')

Поддерживается ActiveRecord 4.0 и более поздних версий.

заказ

Вы можете заказать результаты запроса ActiveRecord с помощью .order :

User.order(:created_at)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]

Если не указано, заказы будут выполняться в порядке возрастания. Вы можете указать его, выполнив:

User.order(created_at: :asc)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]

User.order(created_at: :desc)
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]

.order также принимает строку, поэтому вы также можете сделать

User.order("created_at DESC")
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]

Поскольку строка является сырым SQL, вы также можете указать таблицу, а не только атрибут. Предполагая, что вы хотите заказать users соответствии с их именем role , вы можете сделать это:

Class User < ActiveRecord::Base
    belongs_to :role
end

Class Role < ActiveRecord::Base
  has_many :users
end

User.includes(:role).order("roles.name ASC")

Область order также может принимать узел Arel:

User.includes(:role).order(User.arel_table[:name].asc)

Методы ActiveRecord Bang (!)

Если вам нужен метод ActiveRecord для создания исключения вместо false значения в случае сбоя, вы можете добавить ! им. Это очень важно. Поскольку некоторые исключения / неудачи трудно поймать, если вы не используете! на них. Я рекомендовал сделать это в вашем цикле разработки, чтобы написать весь ваш код ActiveRecord таким образом, чтобы сэкономить ваше время и проблемы.

Class User < ActiveRecord::Base
  validates :last_name, presence: true
end

User.create!(first_name: "John")
#=> ActiveRecord::RecordInvalid: Validation failed: Last name can't be blank

Методами ActiveRecord, которые принимают взрыва ( ! ), Являются:

  • .create!
  • .take!
  • .first!
  • .last!
  • .find_by!
  • .find_or_create_by!
  • #save!
  • #update!
  • все динамические искатели AR

.find_by

Вы можете найти записи по любому полю в своей таблице, используя find_by .

Итак, если у вас есть модель User с атрибутом first_name вы можете:

User.find_by(first_name: "John")
#=> #<User id: 2005, first_name: "John", last_name: "Smith">

find_by что find_by по умолчанию не выбрасывает исключение. Если результатом является пустой набор, он возвращает nil вместо find .

Если это исключение, вы можете использовать find_by! что вызывает ошибку ActiveRecord::RecordNotFound например find .

.удалить все

Если вам нужно быстро удалить много записей, ActiveRecord предоставляет метод .delete_all . для вызова непосредственно на модели, для удаления всех записей в этой таблице или коллекции. Остерегайтесь, хотя, поскольку .delete_all не создает экземпляр какого-либо объекта, следовательно, не предоставляет никакого обратного вызова ( before_* и after_destroy не запускаются).

User.delete_all
#=> 39  <-- .delete_all return the number of rows deleted

User.where(name: "John").delete_all 

Нечувствительный к регистру поиск по регистру ActiveRecord

Если вам нужно искать модель ActiveRecord для аналогичных значений, у вас может возникнуть соблазн использовать LIKE или ILIKE но это не переносится между ILIKE базы данных. Точно так же, прибегая к постоянному снижению или росту, вы можете создавать проблемы с производительностью.

Вы можете использовать метод matches ActiveR для ActiveRecord, чтобы сделать это безопасным способом:

addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))

Arel применит соответствующую конструкцию LIKE или ILIKE для сконфигурированного ядра базы данных.

Получить первую и последнюю запись

У Rails есть очень простой способ получить first и last запись из базы данных.

Чтобы получить first запись из таблицы users нам нужно ввести следующую команду:

User.first

Он будет генерировать следующий запрос sql :

SELECT  `users`.* FROM `users`  ORDER BY `users`.`id` ASC LIMIT 1

И вернется следующая запись:

#<User:0x007f8a6db09920 id: 1, first_name: foo, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >

Чтобы получить last запись из таблицы users нам нужно ввести следующую команду:

User.last

Он будет генерировать следующий запрос sql :

SELECT  `users`.* FROM `users`  ORDER BY `users`.`id` DESC LIMIT 1

И вернется следующая запись:

#<User:0x007f8a6db09920 id: 10, first_name: bar, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >

Передача целочисленного значения первому и последнему методу создает запрос LIMIT и возвращает массив объектов.

User.first(5)

Он будет генерировать следующий запрос sql .

SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 5

А также

User.last(5)

Он будет генерировать следующий запрос sql .

SELECT  "users".* FROM "users"  ORDER BY "users"."id" DESC LIMIT 5

.группа и .count

У нас есть модель Product и мы хотим сгруппировать их по их category .

Product.select(:category).group(:category)

Это запросит базу данных следующим образом:

SELECT "product"."category" FROM "product" GROUP BY "product"."category"

Убедитесь, что поле сгруппировано также выбрано. Группирование особенно полезно для подсчета появления - в данном случае - categories .

Product.select(:category).group(:category).count

Как показывает запрос, он будет использовать базу данных для подсчета, что намного эффективнее, чем сначала получить всю запись, и сделать подсчет в коде:

SELECT COUNT("products"."category") AS count_categories, "products"."category" AS products_category FROM "products" GROUP BY "products"."category"

.distinct (или .uniq)

Если вы хотите удалить дубликаты из результата, вы можете использовать .distinct() :

Customers.select(:country).distinct

Это запрашивает базу данных следующим образом:

SELECT DISTINCT "customers"."country" FROM "customers"

.uniq() имеет тот же эффект. С Rails 5.0 он устарел, и он будет удален из Rails с версией 5.1. Причина в том, что слово unique не имеет того же значения, что и отдельный, и оно может вводить в заблуждение. Кроме того, distinct подход ближе к синтаксису SQL.

присоединяется

joins() позволяет присоединить таблицы к текущей модели. Напр.

User.joins(:posts)

будет вызывать следующий SQL-запрос:

"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""

После соединения таблицы у вас будет доступ к ней:

User.joins(:posts).where(posts: { title: "Hello world" })

Обратите внимание на множественную форму. Если ваше отношение :has_many , то аргумент join joins() должен быть плюрализован. В противном случае используйте единственное число.

Вложенные joins :

User.joins(posts: :images).where(images: { caption: 'First post' })

который будет производить:

"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "images" ON "images"."post_id" = "images"."id""

Включает в себя

ActiveRecord с includes гарантирует , что все указанные ассоциаций загружаются с использованием минимально возможного количества запросов. Поэтому при запросе таблицы для данных со связанной таблицей обе таблицы загружаются в память.

@authors = Author.includes(:books).where(books: { bestseller: true } )

# this will print  results without additional db hitting
@authors.each do |author| 
  author.books.each do |book|
    puts book.title
  end
end

Author.joins(:books).where(books: { bestseller: true } ) загрузит только авторов с условиями в память без загрузки книг . Используйте joins если дополнительная информация о вложенных ассоциациях не требуется.

@authors = Author.joins(:books).where(books: { bestseller: true } )

# this will print results without additional queries
@authors.each { |author| puts author.name }

# this will print results with additional db queries
@authors.each do |author| 
  author.books.each do |book|
    puts book.title
  end
end

Ограничение и смещение

Вы можете использовать limit чтобы указать количество записей, которые нужно извлечь, и использовать offset чтобы сообщить количество пропущенных записей, прежде чем начинать возвращать записи.

Например

User.limit(3) #returns first three records

Он будет генерировать следующий запрос sql.

"SELECT  `users`.* FROM `users` LIMIT 3"

Поскольку смещение не упоминается в вышеуказанном запросе, оно возвращает первые три записи.

User.limit(5).offset(30) #returns 5 records starting from 31th i.e from 31 to 35

Он будет генерировать следующий запрос sql.

"SELECT  `users`.* FROM `users` LIMIT 5 OFFSET 30"


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow