знайти 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Метод зазвичай використовується для вилучення рядка з ID:

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.
Dennis

4
@Dennis, ти прав, що це не застаріло, і це я сказав. Але я міг би бути більш зрозумілим, коли сказав "але синтаксис трохи змінюється". Що я мав на увазі, «але новий синтаксис також доступний». Див. Виправлення MCB для нового синтаксису.
Брайан Мореарті

3
Про це згадується у релізі 4.0 випуску "Усі динамічні методи, за винятком find_by _... та find_by _...! Застарілі", більш детально про edgeguides.rubyonrails.org/…
Mukesh Singh Rathaur

131

де повертає ActiveRecord :: відношення

Тепер подивіться на реалізацію find_by:

def find_by
  where(*args).take
end

Як бачите find_by - це те саме, що і де, але він повертає лише один запис. Цей метод слід використовувати для отримання 1 запису, і де його слід використовувати для отримання всіх записів з деякими умовами.


1
find_by повертає об’єкт, але там, де повертає колекцію.
Kick Buttowski

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

34

Model.find

1- Параметр: ідентифікатор об'єкта, який потрібно знайти.

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поверне нуль.

Іноді це легше читати, якщо у вас є такий метод find_by email: "haha", як на відміну від .where(email: some_params).first.


17

З Rails 4 ви можете:

User.find_by(name: 'Bob')

що еквівалент find_by_nameв рейках 3.

Використовуйте #whereколи #findі #find_byїх недостатньо.


2
Агіс, я згоден з вами, але я шукав в Інтернеті, чому б нам користуватися, find_byа ні find_by_<column_name>. Мені це потрібно, щоб хтось відповів.
КУЛКІНГ

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

Чи є простий спосіб зробити це, але скажіть, що в ім'я є маска, в ньому щось подібнеfind_by(name: "Rob*")
Бетмен

1
@Dennis Можна використовувати обоє, вони дійсні. Я віддаю перевагу новому синтаксису, оскільки API є більш інтуїтивним IMHO. Ось як я би це створив сам :)
Агіс

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») перша - Це дозволяє оновлювати його безпосередньо і безпечніше , так як він вижив старіння.
Пол Brunache

6

Окрім прийнятої відповіді, діє також і наступне

Model.find()може прийняти масив ідентифікаторів і поверне всі записи, які відповідають. Model.find_by_id(123)також приймає масив, але обробляє лише перше значення id, присутнє в масиві

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
чудовий пост не існує.
Нітін


3

Надані відповіді поки що все в порядку.

Однак одна цікава різниця полягає в тому, що Model.findпошук за id; якщо його знайдено, він повертає Modelоб'єкт (лише один запис), але кидає ActiveRecord::RecordNotFoundінакше.

Model.find_byдуже схожий Model.findі дозволяє шукати будь-який стовпчик або групу стовпців у вашій базі даних, але він повертається, nilякщо жодна запис не відповідає пошуку.

Model.whereз іншого боку, повертає Model::ActiveRecord_Relationоб'єкт, подібний до масиву, що містить усі записи, які відповідають запиту . Якщо запису не знайдено, він повертає порожній Model::ActiveRecord_Relationоб’єкт.

Я сподіваюся, що вони допоможуть тобі вирішити, який використовувати в будь-який момент часу.


3

Припустимо, у мене є модель User

  1. User.find(id)

Повертає рядок, де первинний ключ = id. Тип повернення буде Userоб'єктом.

  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::Relationоб’єктом. ActiveRecord::Relationклас включає Enumerableмодуль Ruby, так що ви можете використовувати його об'єкт, як масив, і переходити по ньому.


0

Найкраща частина роботи з будь-якою технологією з відкритим кодом - це те, що ви можете перевірити її довжину та широту. Ознайомтесь із цим посиланням

find_by ~> Знаходить першу запис, що відповідає заданим умовам. Немає загального замовлення, тому якщо замовлення має значення, вам слід вказати його самостійно. Якщо запис не знайдено, повертається нуль.

find ~> Знаходить перший запис, що відповідає заданим умовам, але якщо запис не знайдено, він створює виняток, але це робиться навмисно.

Ознайомтесь з вищевказаним посиланням, воно містить усі пояснення та використання випадків для наступних двох функцій.


-5

Я особисто рекомендую використовувати

where(< columnname> => < columnvalue>)

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