Як Rails ActiveRecord поєднує речення “де” без кількох запитів?


75

Я розробник PHP, який вивчає неймовірність Ruby on Rails, я люблю ActiveRecord, і я помітив щось справді цікаве, саме так методи ActiveRecord виявляють кінець ланцюжка методів для виконання запиту.

@person = Person.where(name: 'Jason').where(age: 26)

# In my humble imagination I'd think that each where() executes a database query
# But in reality, it doesn't until the last method in the chain

Як працює це чаклунство?


Це не робить цього за останнім методом. Це ніяк не може цього знати. Поміркуйте x = Person.where(..); @person = x.where(..), який слід виконувати однаково. Це робиться десь пізніше, то що є тригером? ;-)

Відповіді:


157

whereМетод повертає ActiveRecord::Relationоб'єкт, а сам по собі цей об'єкт не видає запит до бази даних. Тут важливо , де ви використовуєте цей об’єкт.

У консолі ви, мабуть, робите це:

@person = Person.where(name: "Jason")

А потім blammo видає запит до бази даних і повертає те, що здається масивом усіх на ім’я Джейсон. Так, активний запис!

Але тоді ви робите щось подібне:

@person = Person.where(name: "Jason").where(age: 26)

І тоді це видає інший запит, але цей - для людей, яких називають Джейсоном, яким 26. Але це лише видача одного запиту, то куди пішов інший запит?


Як припускали інші, це відбувається тому, що whereметод повертає проксі-об'єкт. Він насправді не виконує запит і не повертає набір даних, якщо цього не попросили.

Коли ви запускаєте що- небудь у консолі, він видасть перевірену версію результату незалежно від того, що ви запускали. Якщо ви 1введете в консоль і натиснете Enter, ви 1повернетесь, тому що 1.inspectє 1. Магія! Те саме стосується "1". Безліч інших об'єктів не має inspectметод , певні і тому Рубі повертається до одного на Objectякий повертає що - то жахливе , як <Object#23adbf42560>.

У кожному окремому ActiveRecord::Relationоб'єкті inspectвизначено метод, що викликає запит. Коли ви пишете запит у своїй консолі, IRB викличе inspectзначення, що повертається з цього запиту, і виведе щось майже зрозуміле для людини, таке як масив, який ви побачите.


Якби ви просто видавали це у стандартному скрипті Ruby, тоді жоден запит не виконувався б, поки об’єкт не перевірили (через inspect) або не повторили за допомогою each, або не to_aвикликали до нього метод.

До один з цих трьох речей не відбудеться, ви можете ланцюга , як багато whereзаяви про нього , як вам сподобається , і тоді , коли ви робите виклик inspect, to_aабо eachна нього, то він буде , нарешті , виконати цей запит.


1
Відповідний Railscast, пояснюючи це вихідним кодом Rails.
flexus

8

Існує ряд методів, відомих під назвою "кікери", які насправді запускають запит до бази даних. До цього вони просто створюють AST-вузли, які після натискання генерують фактичний SQL (або мову, до якої компілюється) і запускають запит.

Дивіться цю публікацію в блозі, щоб отримати більш глибоке пояснення того, як це робиться.


4

Ви можете прочитати код, але одна з концепцій тут - шаблон проксі.

Можливо, @person - це не справжній об'єкт, а проксі-сервер для цього об'єкта, і коли вам потрібен якийсь атрибут, активний запис нарешті виконує запит. Зимовий сон має таку ж концепцію.


-2

Можливо, занадто пізно, але ви можете використовувати хеш:

@person = Person.where({name: "Jason", age: 26})

Результат запиту:

SELECT "person".* FROM "person"  WHERE "person"."name" = 'Jason' AND "person"."age" = 26

2
person = Person.where ({name: "Jason", вік: 26}) and person = Person.where (name: "Jason", age: 26) абсолютно однакові - Рубі перетворює аргументи для другого в хеш автоматично
Ghoti
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.