найти vs find_by vs где


127

Я новичок в рельсах. Я вижу, что есть много способов найти запись:

  1. find_by_<columnname>(<columnvalue>)
  2. find(:first, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>).first

И похоже, что все они в конечном итоге генерируют один и тот же SQL. Кроме того, я считаю, что то же самое верно и для поиска нескольких записей:

  1. find_all_by_<columnname>(<columnvalue>)
  2. find(:all, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>)

Есть ли практическое правило или рекомендация, какой из них использовать?

Ответы:


103

Используйте тот, который, по вашему мнению, лучше всего соответствует вашим потребностям.

Этот findметод обычно используется для получения строки по идентификатору:

Model.find(1)

Стоит отметить, что это findвызовет исключение, если элемент не найден по указанному вами атрибуту. Используйте where(как описано ниже, который вернет пустой массив, если атрибут не найден), чтобы избежать возникновения исключения.

Другие варианты использования findобычно заменяются такими вещами:

Model.all
Model.first

find_byиспользуется в качестве помощника при поиске информации в столбце и сопоставляется с ним с помощью соглашений об именах. Например, если у вас есть столбец с именем nameв вашей базе данных, вы должны использовать следующий синтаксис:

Model.find_by(name: "Bob")

.where - это скорее уловка, которая позволяет вам использовать немного более сложную логику, когда обычные помощники не подходят, и возвращает массив элементов, соответствующих вашим условиям (или пустой массив в противном случае).


62
find_byне устарел, но синтаксис немного изменился. От find_by_name("Bob")до find_by(:name, "Bob").
Брайан Морарти,

61
@BrianMorearty, я думаю, вы имели в видуfind_by(name: "Bob")
MCB

1
@BrianMorearty Я не смог найти никаких доказательств того, find_by_...что я устарел, у вас есть источник? Кажется, find_byи find_by_...оба они по-прежнему поддерживаются в Rails 4.
Деннис

4
@ Деннис, вы правы, что он не устарел, и я так сказал. Но я мог бы быть более ясным, когда сказал: «Но синтаксис немного меняется». Я имел в виду, что «но теперь доступен и новый синтаксис». См. Поправку MCB для нового синтаксиса.
Брайан Морарти

3
Это то, что упоминается в выпуске rails 4.0 «Все динамические методы, кроме find_by _... и find_by _...! Устарели» более подробная информация на edgeguides.rubyonrails.org/…
Мукеш Сингх Ратаур 02

131

где возвращает ActiveRecord :: Relation

Теперь посмотрим на реализацию find_by:

def find_by
  where(*args).take
end

Как видите, find_by совпадает с where, но возвращает только одну запись. Этот метод следует использовать для получения 1 записи и where следует использовать для получения всех записей с некоторыми условиями.


1
find_by возвращает объект, а where возвращает коллекцию.
Kick Buttowski

При запросе значения вне диапазона, find_byбудет спасать ::RangeErrorот where(*args) и возвратного ноля.
fangxing

34

Model.find

1- Параметр: ID объекта для поиска.

2- Если найдено: возвращает объект (только один объект).

3- Если не найден: вызывает ActiveRecord::RecordNotFoundисключение.

Model.find_by

1- Параметр: ключ / значение

Пример:

User.find_by name: 'John', email: 'john@doe.com'

2- Если найдено: возвращает объект.

3- Если не найден: возвращается nil.

Примечание: если вы хотите поднять,ActiveRecord::RecordNotFoundиспользуйтеfind_by!

Model.where

1- Параметр: такой же, как find_by

2- Если найдено: он возвращает ActiveRecord::Relationодну или несколько записей, соответствующих параметрам.

3- Если не найден: он возвращает пустой ActiveRecord::Relation.


31

Существует разница между findи find_byв том, что findбудет возвращать ошибку, если она не найдена, тогда как find_byвернет null.

Иногда легче читать, если у вас есть метод вроде find_by email: "haha", а не .where(email: some_params).first.


17

Начиная с Rails 4 вы можете:

User.find_by(name: 'Bob')

что эквивалентно find_by_nameв Rails 3.

Используйте, #whereкогда #findи #find_byэтого недостаточно.


2
Агис, я согласен с вами, но я искал в Интернете, почему мы должны использовать, find_byа почему нет find_by_<column_name>. Мне нужно, чтобы кто-то ответил
KULKING

Агис, есть ли у вас источники, подтверждающие ваше заявление о том, что нам больше не следует использовать find_by_nameв Rails 4? Насколько я знаю, он не устарел .
Деннис

Есть ли простой способ сделать это, но скажем, что в имени есть подстановочный знак, что-то вродеfind_by(name: "Rob*")
Бэтмен

1
@Dennis Можно использовать оба, они действительны. Я предпочитаю новый синтаксис, так как API более интуитивно понятен ИМХО. Это то, как я бы сам это разработал :)
Agis

8

Принятый ответ обычно охватывает все это, но я хотел бы кое-что добавить, на всякий случай, если вы планируете работать с моделью таким образом, как обновление, и вы получаете одну запись (о которой idвы не знаете), Тогда find_byэто путь, потому что он извлекает запись и не помещает ее в массив

irb(main):037:0> @kit = Kit.find_by(number: "3456")
  Kit Load (0.9ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" = 
 '3456' LIMIT 1
=> #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",   
updated_at: "2015-05-12 06:10:56", job_id: nil>

irb(main):038:0> @kit.update(job_id: 2)
(0.2ms)  BEGIN Kit Exists (0.4ms)  SELECT 1 AS one FROM "kits" WHERE  
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE  "kits"."id" = 
1  [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]] 
(0.6ms)  COMMIT => true

но если вы используете whereто вы не можете обновить его напрямую

irb(main):039:0> @kit = Kit.where(number: "3456")
Kit Load (1.2ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" =  
'3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456", 
created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58", 
job_id: 2>]>

irb(main):040:0> @kit.update(job_id: 3)
ArgumentError: wrong number of arguments (1 for 2)

в таком случае вам нужно будет указать это так

irb(main):043:0> @kit[0].update(job_id: 3)
(0.2ms)  BEGIN Kit Exists (0.6ms)  SELECT 1 AS one FROM "kits" WHERE 
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1  
[["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
(0.5ms)  COMMIT => true

именно то, что я искал
Поль Брунаш

2
@kit = Kit.where (номер: "3456"). first - Это позволяет вам обновлять его напрямую, и это безопаснее, так как оно пережило устаревание
Пол Брунаш

6

Помимо принятого ответа, также верно следующее

Model.find()может принимать массив идентификаторов и возвращать все совпадающие записи. Model.find_by_id(123)также принимает массив, но обрабатывает только первое значение идентификатора, присутствующее в массиве

Model.find([1,2,3])
Model.find_by_id([1,2,3])

5

Оба # 2 в ваших списках устарели. Вы все еще можете использовать find(params[:id]).

Как правило, where()работает в большинстве ситуаций.

Вот отличный пост: http://m.onkey.org/active-record-query-interface


1
отличного поста не существует.
Nithin


3

Все ответы, данные до сих пор, в порядке.

Однако одно интересное отличие состоит в том, что Model.findпоиск выполняется по идентификатору; если он найден, он возвращает Modelобъект (только одну запись), но в ActiveRecord::RecordNotFoundпротивном случае выдает .

Model.find_byочень похож на Model.findпоиск в любом столбце или группе столбцов в базе данных и позволяет выполнять поиск, но возвращается, nilесли поиску не соответствует ни одна запись.

Model.whereс другой стороны, возвращает Model::ActiveRecord_Relationобъект, который похож на массив, содержащий все записи, соответствующие поиску . Если запись не найдена, возвращается пустой Model::ActiveRecord_Relationобъект.

Я надеюсь, что они помогут вам решить, что использовать в любой момент времени.


3

Допустим, у меня есть модель User

  1. User.find(id)

Возвращает строку, в которой первичный ключ = id. Тип возврата будет Userobject.

  1. User.find_by(email:"abc@xyz.com")

В этом случае возвращает первую строку с совпадающим атрибутом или адрес электронной почты. Тип возврата снова будет Userобъектом.

Примечание: - User.find_by(email: "abc@xyz.com")аналогичноUser.find_by_email("abc@xyz.com")

  1. User.where(project_id:1)

Возвращает всех пользователей в таблице пользователей, где совпадают атрибуты.

Здесь тип возврата будет ActiveRecord::Relationobject. ActiveRecord::Relationclass включает Enumerableмодуль Ruby, поэтому вы можете использовать его как массив и перемещаться по нему.


0

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

find_by ~> Находит первую запись, соответствующую указанным условиям. Здесь нет подразумеваемого заказа, поэтому, если порядок имеет значение, вы должны указать его самостоятельно. Если запись не найдена, возвращается ноль.

find ~> Находит первую запись, соответствующую указанным условиям, но, если запись не найдена, вызывает исключение, но это делается намеренно.

Проверьте ссылку выше, в ней есть все объяснения и варианты использования для следующих двух функций.


-5

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

where(< columnname> => < columnvalue>)

1
Это может ответить на вопрос. Но попробуйте описать плюсы и минусы использования вашего подхода вместо общепринятого ответа.
Вивек Кумар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.