1 Kodėl asociacijos?
Rails aplinkoje asociacija yra ryšys tarp dviejų aktyviojo įrašo modelių. Kodėl mums reikia asociacijų tarp modelių? Nes jos supaprastina ir palengvina bendrus veiksmus jūsų kode.
Pavyzdžiui, pagalvokite apie paprastą Rails aplikaciją, kurioje yra modelis autoriai ir modelis knygos. Kiekvienas autorius gali turėti daug knygų.
Be asociacijų, modelių deklaracijos atrodytų taip:
class Author < ApplicationRecord
end
class Book < ApplicationRecord
end
Dabar, jei norėtume pridėti naują knygą esamam autorius, turėtume tai padaryti taip:
@book = Book.create(published_at: Time.now, author_id: @author.id)
Arba pagalvokite apie autoriaus ištrynimą ir užtikrinimą, kad visos jo knygos taip pat būtų ištrintos:
@books = Book.where(author_id: @author.id)
@books.each do |book|
book.destroy
end
@author.destroy
Naudojant aktyviojo įrašo asociacijas, galime supaprastinti šiuos - ir kitus - veiksmus, deklaratyviai pranešdami Rails, kad tarp dviejų modelių yra ryšys. Čia yra peržiūrėtas kodas, skirtas autorių ir knygų nustatymui:
class Author < ApplicationRecord
has_many :books, dependent: :destroy
end
class Book < ApplicationRecord
belongs_to :author
end
Padarius šį pakeitimą, naujos knygos sukūrimas tam tikram autorius tampa paprastesnis:
@book = @author.books.create(published_at: Time.now)
Autoriaus ir visų jo knygų ištrynimas yra daug paprastesnis:
author.destroy
Norėdami sužinoti daugiau apie skirtingus asociacijų tipus, perskaitykite šio vadovo kitą skyrių. Toliau pateikiami keletas patarimų ir gudrybių, kaip dirbti su asociacijomis, o po to - išsamus nuorodų ir parinkčių sąrašas asociacijoms Rails.
2 Asociacijų tipai
Rails palaiko šešis asociacijų tipus, kiekvienas su tam tikru naudojimo atveju.
Štai visų palaikomų tipų sąrašas su nuoroda į jų API dokumentaciją, kurioje galite rasti išsamesnės informacijos apie tai, kaip juos naudoti, jų metodo parametrus ir kt.
Asociacijos yra įgyvendinamos naudojant makro stiliaus iškvietimus, todėl galite deklaratyviai pridėti funkcijas prie savo modelių. Pavyzdžiui, deklaruodami, kad vienas modelis priklauso
kitam, jūs nurodote Rails, kad tarp dviejų modelių egzempliorių bus išlaikoma Pagrindinio rakto-Užsienio rakto informacija, ir taip pat gaunate keletą naudingų metodų, pridėtų prie jūsų modelio.
Šio vadovo likusioje dalyje sužinosite, kaip deklaruoti ir naudoti įvairias asociacijų formas. Bet pirmiausia trumpas įvadas į situacijas, kuriose tinkamas kiekvienos asociacijos tipas.
2.1 belongs_to
asociacija
belongs_to
asociacija sukuria ryšį su kitu modeliu, kad kiekvienas deklaruojančio modelio egzempliorius "priklauso" vienam kito modelio egzemplioriui. Pavyzdžiui, jei jūsų aplikacija apima autorius ir knygas, ir kiekviena knyga gali būti priskirta tik vienam autorius, knygos modelį galėtumėte deklaruoti taip:
class Book < ApplicationRecord
belongs_to :author
end
PASTABA: belongs_to
asociacijos turi naudoti vienaskaitos formą. Jei pirmiau pateiktame pavyzdyje Book
modelyje author
asociacijai būtų naudojama daugiskaitos forma ir bandoma sukurti egzempliorių naudojant Book.create(authors: author)
, būtumėte informuotas apie "neinicijuotą konstantą Book::Authors". Tai yra todėl, kad Rails automatiškai nustato klasės pavadinimą iš asociacijos pavadinimo. Jei asociacijos pavadinimas būtų neteisingai daugiskaitinė, tuomet ir nustatytas klasės pavadinimas būtų neteisingai daugiskaitinis.
Atitinkama migracija galėtų atrodyti taip:
class CreateBooks < ActiveRecord::Migration[7.1]
def change
create_table :authors do |t|
t.string :name
t.timestamps
end
create_table :books do |t|
t.belongs_to :author
t.datetime :published_at
t.timestamps
end
end
end
Kai naudojamas vienas, belongs_to
sukuria vienkrypčią vienas-į-vieną sąsają. Todėl kiekviena knyga pirmiau pateiktame pavyzdyje "žino" savo autorių, bet autoriai nežino apie savo knygas.
Norėdami nustatyti abipusį ryšį - naudokite belongs_to
kartu su has_one
arba has_many
kitame modelyje, šiuo atveju Autoriaus modelyje.
belongs_to
, jei nustatoma, kad optional
yra true
, neužtikrina nuorodos nuoseklumo, todėl priklausomai nuo naudojimo atvejo, galbūt taip pat reikės pridėti duomenų bazės lygmens užsienio rakto apribojimą nuorodos stulpelyje, panašiai kaip čia:
ruby
create_table :books do |t|
t.belongs_to :author, foreign_key: true
# ...
end
2.2 has_one
asociacija
has_one
asociacija nurodo, kad vienas kitas modelis turi nuorodą į šį modelį. Šis modelis gali būti gautas per šią asociaciją.
Pavyzdžiui, jei kiekvienas tiekėjas jūsų programoje turi tik vieną sąskaitą, tiekėjo modelį galėtumėte apibrėžti taip:
class Supplier < ApplicationRecord
has_one :account
end
Pagrindinis skirtumas nuo belongs_to
yra tas, kad nuorodos stulpelis supplier_id
yra kitame lauke:
Atitinkanti migracija galėtų atrodyti taip:
class CreateSuppliers < ActiveRecord::Migration[7.1]
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.belongs_to :supplier
t.string :account_number
t.timestamps
end
end
end
Priklausomai nuo naudojimo atvejo, gali prireikti sukurti unikalų indeksą ir/arba užsienio rakto apribojimą tiekėjo stulpeliui sąskaitų lentelėje. Tokiu atveju, stulpelio apibrėžimas galėtų atrodyti taip:
create_table :accounts do |t|
t.belongs_to :supplier, index: { unique: true }, foreign_key: true
# ...
end
Ši sąsaja gali būti dvikryptė, kai naudojama kartu su belongs_to
kitame modelyje.
2.3 has_many
asociacija
has_many
asociacija panaši į has_one
, bet nurodo vieno į daugelį ryšį su kitu modeliu. Dažnai šią asociaciją rasite "kitoje pusėje" esančią belongs_to
asociaciją. Ši asociacija nurodo, kad kiekvienas modelio atvejis turi nulį arba daugiau kitų modelio atvejų. Pavyzdžiui, programoje, kurioje yra autoriai ir knygos, autoriaus modelį galėtumėte apibrėžti taip:
class Author < ApplicationRecord
has_many :books
end
PASTABA: Kito modelio pavadinimas daugiskaitinamas, kai deklaruojama has_many
asociacija.
Atitinkanti migracija galėtų atrodyti taip:
class CreateAuthors < ActiveRecord::Migration[7.1]
def change
create_table :authors do |t|
t.string :name
t.timestamps
end
create_table :books do |t|
t.belongs_to :author
t.datetime :published_at
t.timestamps
end
end
end
Priklausomai nuo naudojimo atvejo, paprastai gerai būtų sukurti neunikalų indeksą ir galbūt užsienio rakto apribojimą autoriaus stulpeliui knygų lentelėje:
create_table :books do |t|
t.belongs_to :author, index: true, foreign_key: true
# ...
end
2.4 has_many :through
asociacija
has_many :through
asociacija dažnai naudojama nustatyti daugybės su kitu modeliu ryšį. Ši asociacija nurodo, kad deklaruojantis modelis gali būti suderintas su nuliu arba daugiau kitų modelio atvejų, einant per trečią modelį. Pavyzdžiui, pagalvokite apie medicinos praktiką, kur pacientai užsiregistruoja pas gydytojus. Atitinkamos asociacijos deklaracijos galėtų atrodyti taip:
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
Atitinkanti migracija galėtų atrodyti taip:
class CreateAppointments < ActiveRecord::Migration[7.1]
def change
create_table :physicians do |t|
t.string :name
t.timestamps
end
create_table :patients do |t|
t.string :name
t.timestamps
end
create_table :appointments do |t|
t.belongs_to :physician
t.belongs_to :patient
t.datetime :appointment_date
t.timestamps
end
end
end
Jungiamų modelių kolekciją galima valdyti naudojant has_many
asociacijos metodus.
Pavyzdžiui, jei priskiriate:
physician.patients = patients
Tada nauji jungiamų modelių objektai automatiškai sukuriami naujai susietiems objektams. Jei kai kurie, kurie anksčiau egzistavo, dabar trūksta, tada jų jungtys automatiškai ištrinamos.
ĮSPĖJIMAS: Jungiamų modelių automatinis trynimas yra tiesioginis, jokie trynimo atgaliniai iškvietimai nėra paleidžiami.
has_many :through
asociacija taip pat naudinga nustatant "trumpinius" perrišimus per įdėtus has_many
asociacijas. Pavyzdžiui, jei dokumentas turi daug skyrių, o skyrius turi daug paragrafų, kartais norėsite gauti paprastą paragrafų kolekciją dokumente. Tai galėtumėte nustatyti taip:
class Document < ApplicationRecord
has_many :sections
has_many :paragraphs, through: :sections
end
class Section < ApplicationRecord
belongs_to :document
has_many :paragraphs
end
class Paragraph < ApplicationRecord
belongs_to :section
end
Nurodžius through: :sections
, „Rails“ dabar supras:
@document.paragraphs
2.5 has_one :through
asociacija
has_one :through
asociacija nustato vieno į vieną ryšį su kitu modeliu. Ši asociacija nurodo, kad deklaruojantis modelis gali būti suderintas su vienu kitu modelio atveju, einant per trečią modelį.
Pavyzdžiui, jei kiekvienas tiekėjas turi vieną sąskaitą, o kiekviena sąskaita yra susijusi su viena sąskaitos istorija, tada tiekėjo modelis galėtų atrodyti taip:
```ruby
class Supplier < ApplicationRecord
has_one :account
has_one :account_history, through: :account
end
class Account < ApplicationRecord belongs_to :supplier has_one :account_history end
class AccountHistory < ApplicationRecord belongs_to :account end ```
Atitinkantis migracijos kodas gali atrodyti taip:
class CreateAccountHistories < ActiveRecord::Migration[7.1]
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.belongs_to :supplier
t.string :account_number
t.timestamps
end
create_table :account_histories do |t|
t.belongs_to :account
t.integer :credit_rating
t.timestamps
end
end
end
2.6 has_and_belongs_to_many
asociacija
has_and_belongs_to_many
asociacija sukuria tiesioginį daugiau į daugiau ryšį su kitu modeliu, be tarpinio modelio.
Ši asociacija nurodo, kad kiekvienas deklaruojančio modelio atvejis nurodo nulį ar daugiau kito modelio atvejų.
Pavyzdžiui, jei jūsų programa apima montavimus ir dalis, kur kiekvienas montavimas turi daug dalies ir kiekviena dalis yra daug montavimų, galite deklaruoti modelius taip:
class Assembly < ApplicationRecord
has_and_belongs_to_many :parts
end
class Part < ApplicationRecord
has_and_belongs_to_many :assemblies
end
Atitinkantis migracijos kodas gali atrodyti taip:
class CreateAssembliesAndParts < ActiveRecord::Migration[7.1]
def change
create_table :assemblies do |t|
t.string :name
t.timestamps
end
create_table :parts do |t|
t.string :part_number
t.timestamps
end
create_table :assemblies_parts, id: false do |t|
t.belongs_to :assembly
t.belongs_to :part
end
end
end
2.7 Pasirinkimas tarp belongs_to
ir has_one
Jei norite nustatyti vieno į vieną ryšį tarp dviejų modelių, turėsite pridėti belongs_to
vienam ir has_one
kitam. Kaip žinote, kuris yra kuris?
Skirtumas yra tai, kur dedate svetimąjį raktą (jis eina į lentelę, skirtą klasės, deklaruojančios belongs_to
asociaciją), tačiau taip pat turėtumėte apgalvoti ir duomenų tikrosios reikšmės. has_one
ryšys sako, kad vienas iš kažko yra jūsų - tai yra, kad kažkas rodo atgal į jus. Pavyzdžiui, daugiau prasmės pasakyti, kad tiekėjas turi sąskaitą, nei kad sąskaita turi tiekėją. Tai rodo, kad teisingi ryšiai yra tokie:
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
Atitinkantis migracijos kodas gali atrodyti taip:
class CreateSuppliers < ActiveRecord::Migration[7.1]
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.bigint :supplier_id
t.string :account_number
t.timestamps
end
add_index :accounts, :supplier_id
end
end
PASTABA: Naudodami t.bigint :supplier_id
padarote aiškų ir aiškų svetimojo rakto pavadinimą. Esamose „Rails“ versijose galite paslėpti šį įgyvendinimo detalių naudojant t.references :supplier
vietoj to.
2.8 Pasirinkimas tarp has_many :through
ir has_and_belongs_to_many
„Rails“ siūlo dvi skirtingas būdas nurodyti daug į daug ryšį tarp modelių. Pirmasis būdas yra naudoti has_and_belongs_to_many
, kuris leidžia jums tiesiogiai nustatyti asociaciją:
class Assembly < ApplicationRecord
has_and_belongs_to_many :parts
end
class Part < ApplicationRecord
has_and_belongs_to_many :assemblies
end
Antrasis būdas nurodyti daug į daug ryšį yra naudoti has_many :through
. Tai sukuria asociaciją netiesiogiai, per jungiamąjį modelį:
class Assembly < ApplicationRecord
has_many :manifests
has_many :parts, through: :manifests
end
class Manifest < ApplicationRecord
belongs_to :assembly
belongs_to :part
end
class Part < ApplicationRecord
has_many :manifests
has_many :assemblies, through: :manifests
end
Paprastiausias taisyklės ženklas yra tai, kad turėtumėte nustatyti has_many :through
ryšį, jei norite dirbti su ryšio modeliu kaip su nepriklausoma vienete. Jei jums nereikia nieko daryti su ryšio modeliu, galbūt paprasčiau nustatyti has_and_belongs_to_many
ryšį (nors turėsite prisiminti sukurti jungiamąją lentelę duomenų bazėje).
Turėtumėte naudoti has_many :through
, jei jums reikia validacijų, atgalinių iškvietimų ar papildomų atributų jungiamajame modelyje.
2.9 Polimorfiniai ryšiai
Šiek tiek pažangesnis asociacijų variantas yra polimorfinis ryšys. Su polimorfiniais ryšiais modelis gali priklausyti daugiau nei vienam kitam modeliui, vienoje asociacijoje. Pavyzdžiui, galite turėti paveikslėlio modelį, kuris priklauso arba darbuotojo modeliui, arba produkto modeliui. Čia pateikiama, kaip tai galėtų būti deklaruota:
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
Galite manyti, kad polimorfinis belongs_to
deklaravimas nustato sąsają, kurią gali naudoti bet kuris kitas modelis. Iš Employee
modelio atvejo galite gauti paveikslėlių kolekciją: @employee.pictures
.
Panašiai, galite gauti @product.pictures
.
Jei turite Picture
modelio egzempliorių, galite pasiekti jo tėvą per @picture.imageable
. Norint tai veiktų, modelyje, kuris deklaruoja polimorfinį sąsają, reikia nurodyti tiek užsienio rakto stulpelį, tiek tipo stulpelį:
class CreatePictures < ActiveRecord::Migration[7.1]
def change
create_table :pictures do |t|
t.string :name
t.bigint :imageable_id
t.string :imageable_type
t.timestamps
end
add_index :pictures, [:imageable_type, :imageable_id]
end
end
Šią migraciją galima supaprastinti naudojant t.references
formą:
class CreatePictures < ActiveRecord::Migration[7.1]
def change
create_table :pictures do |t|
t.string :name
t.references :imageable, polymorphic: true
t.timestamps
end
end
end
2.10 Savęs jungtys
Projektuojant duomenų modelį, kartais rasite modelį, kuris turėtų sąryšį su savimi. Pavyzdžiui, galite norėti saugoti visus darbuotojus viename duomenų bazės modelyje, bet galėti sekti ryšius, pvz., tarp vadovo ir pavaldinių. Tokią situaciją galima modeliuoti naudojant savęs jungtis:
class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
belongs_to :manager, class_name: "Employee", optional: true
end
Naudojant šią konfigūraciją, galite gauti @employee.subordinates
ir @employee.manager
.
Migracijos/schemos metu modeliui patiems pridėsite nuorodų stulpelį.
class CreateEmployees < ActiveRecord::Migration[7.1]
def change
create_table :employees do |t|
t.references :manager, foreign_key: { to_table: :employees }
t.timestamps
end
end
end
PASTABA: to_table
parametras, perduodamas foreign_key
ir kt., paaiškinamas SchemaStatements#add_reference
.
3 Patarimai, gudrybės ir įspėjimai
Štai keletas dalykų, kuriuos turėtumėte žinoti, kad efektyviai naudotumėte Active Record sąsajas savo „Rails“ programose:
- Kešavimo valdymas
- Vengiant pavadinimų konfliktų
- Schemos atnaujinimas
- Sąsajos apimties valdymas
- Dviejų krypčių sąsajos
3.1 Kešavimo valdymas
Visi sąsajų metodai yra pagrįsti kešavimu, kuris leidžia išsaugoti naujausio užklausos rezultatą tolimesnėms operacijoms. Kešas netgi bendrinamas tarp metodų. Pavyzdžiui:
# gauna knygas iš duomenų bazės
author.books.load
# naudoja kešuotą knygų kopiją
author.books.size
# naudoja kešuotą knygų kopiją
author.books.empty?
Bet ką daryti, jei norite atnaujinti kešą, nes duomenys gali būti pakeisti kitos programos dalies? Tiesiog iškvieskite reload
sąsajoje:
# gauna knygas iš duomenų bazės
author.books.load
# naudoja kešuotą knygų kopiją
author.books.size
# atmeta kešuotą knygų kopiją ir grįžta prie duomenų bazės
author.books.reload.empty?
3.2 Vengiant pavadinimų konfliktų
Negalite laisvai naudoti bet kokio pavadinimo savo sąsajoms. Kadangi sąsajos sukūrimas prideda to pavadinimo metodą modelyje, bloga mintis suteikti sąsajai pavadinimą, kuris jau naudojamas ActiveRecord::Base
egzemplioriaus metode. Sąsajos metodas pakeistų pagrindinį metodą ir sugadintų dalykus. Pavyzdžiui, attributes
ar connection
yra blogi pavadinimai sąsajoms.
3.3 Schemos atnaujinimas
Sąsajos yra labai naudingos, bet jos nėra magiškos. Jūs atsakingas už duomenų bazės schemos palaikymą, kad ji atitiktų jūsų sąsajas. Praktiškai tai reiškia dvi dalykas, priklausomai nuo to, kokias sąsajas kuriate. belongs_to
sąsajoms reikia sukurti užsienio raktus, o has_and_belongs_to_many
sąsajoms reikia sukurti atitinkamą jungiamąją lentelę.
3.3.1 Užsienio raktų sukūrimas belongs_to
sąsajoms
Kai deklaruoju belongs_to
sąsają, reikia sukurti užsienio raktus, kaip tinkama. Pavyzdžiui, apsvarstykite šį modelį:
class Book < ApplicationRecord
belongs_to :author
end
Ši deklaracija turi būti palaikyta atitinkamu užsienio rakto stulpeliu knygų lentelėje. Visiškai naujai lentelės migracija galėtų atrodyti taip:
class CreateBooks < ActiveRecord::Migration[7.1]
def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.references :author
end
end
end
O esamai lentelės migracija galėtų atrodyti taip:
class AddAuthorToBooks < ActiveRecord::Migration[7.1]
def change
add_reference :books, :author
end
end
PASTABA: Jei norite užtikrinti referencinį vientisumą duomenų bazės lygmeniu, prie „reference“ stulpelių deklaracijų viršuje pridėkite foreign_key: true
parametrą.
3.3.2 Jungiamųjų lentelių sukūrimas has_and_belongs_to_many
sąsajoms
Jei kuriate has_and_belongs_to_many
sąsają, turite išreiškiai sukurti jungiamąją lentelę. Jei jungiamosios lentelės pavadinimas nėra nurodytas naudojant :join_table
parametrą, „Active Record“ sukuria pavadinimą, naudodamas klasės pavadinimų leksikografinę tvarką. Taigi sąsaja tarp autoriaus ir knygos modelių suteiks numatytąjį jungiamosios lentelės pavadinimą „authors_books“, nes „a“ lenkia „b“ leksikografinėje tvarkoje.
ĮSPĖJIMAS: Modelių pavadinimų pirmenybės nustatomos naudojant String
tipo <=>
operatorių. Tai reiškia, kad jei eilutės yra skirtingo ilgio ir jos yra lygios iki trumpiausio ilgio, ilgesnė eilutė laikoma aukštesne nei trumpesnė pagal leksikografinę tvarką. Pavyzdžiui, tikėtasi, kad lentelės "paper_boxes" ir "papers" generuos jungiamosios lentelės pavadinimą "papers_paper_boxes" dėl pavadinimo "paper_boxes" ilgio, tačiau iš tikrųjų generuojamas jungiamosios lentelės pavadinimas yra "paper_boxes_papers" (nes pabraukimas '_' leksikografiškai mažesnis nei 's' daugelyje koduotės sistemų).
Kokiu būdu būtų pavadintas, jungiamąją lentelę reikia sukurti rankiniu būdu naudojant tinkamą migraciją. Pavyzdžiui, apsvarstykime šiuos ryšius:
class Assembly < ApplicationRecord
has_and_belongs_to_many :parts
end
class Part < ApplicationRecord
has_and_belongs_to_many :assemblies
end
Šiems ryšiams reikia sukurti migraciją, kuri sukuria assemblies_parts
lentelę. Ši lentelė turėtų būti sukurta be pirminio rakto:
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[7.1]
def change
create_table :assemblies_parts, id: false do |t|
t.bigint :assembly_id
t.bigint :part_id
end
add_index :assemblies_parts, :assembly_id
add_index :assemblies_parts, :part_id
end
end
Mes perduodame id: false
į create_table
, nes ši lentelė neatitinka modelio. Tai reikalinga, kad sąryšis veiktų tinkamai. Jei pastebite keistą elgesį has_and_belongs_to_many
sąryšyje, pvz., sugadintus modelio ID ar išimtis dėl konfliktuojančių ID, tikėtina, kad pamiršote šį dalyką.
Paprastumui taip pat galite naudoti metodą create_join_table
:
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[7.1]
def change
create_join_table :assemblies, :parts do |t|
t.index :assembly_id
t.index :part_id
end
end
end
3.4 Valdant sąryšio apimtį
Pagal numatytuosius nustatymus sąryšiai ieško objektų tik esamo modulio apimtyje. Tai gali būti svarbu, kai deklaruoji Active Record modelius modulyje. Pavyzdžiui:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
Tai veiks gerai, nes tiek Supplier
, tiek Account
klasės yra apibrėžtos toje pačioje apimtyje. Tačiau šis pavyzdys neveiks, nes Supplier
ir Account
yra apibrėžti skirtingose apimtyse:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
Norėdami susieti modelį su modeliu kitame vardų erdvėje, turite nurodyti visą klasės pavadinimą sąryšio deklaracijoje:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
end
3.5 Dvigubi sąryšiai
Įprasta, kad sąryšiai veikia dviem kryptimis, reikalaujant deklaracijos dviejuose skirtinguose modeliuose:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
Active Record automatiškai bandys nustatyti, kad šie du modeliai turi dvikryptį sąryšį pagal sąryšio pavadinimą. Ši informacija leidžia Active Record:
Išvengti nereikalingų užklausų jau įkeltiems duomenims:
irb> author = Author.first irb> author.books.all? do |book| irb> book.author.equal?(author) # Papildomos užklausos čia nevykdomos irb> end => true
Išvengti nesuderinamų duomenų (kadangi įkelta tik viena
Author
objekto kopija):irb> author = Author.first irb> book = author.books.first irb> author.name == book.author.name => true irb> author.name = "Pakeistas pavadinimas" irb> author.name == book.author.name => true
Automatiškai išsaugoti sąryšius daugiau atvejų:
irb> author = Author.new irb> book = author.books.new irb> book.save! irb> book.persisted? => true irb> author.persisted? => true
Patikrinti sąryšio buvimą ir nebuvimą daugiau atvejų:
irb> book = Book.new irb> book.valid? => false irb> book.errors.full_messages => ["Author must exist"] irb> author = Author.new irb> book = author.books.new irb> book.valid? => true
Active Record automatiškai atpažįsta daugumą sąryšių su standartiniais pavadinimais. Tačiau dvikryptiems sąryšiams, kuriuose yra :through
arba :foreign_key
parinktys, automatinis atpažinimas neveiks.
Papildomai, automatinis atpažinimas neveiks, jei priešingame sąryšio objekte yra pasirinkti pasirinktiniai ribojimai arba jei pats sąryšis turi pasirinktinius ribojimus, nebent config.active_record.automatic_scope_inversing
nustatymas būtų nustatytas kaip true (numatytasis naujiems programoms).
Pavyzdžiui, apsvarstykime šiuos modelių deklaravimus:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
Dėl :foreign_key
parinkties Active Record nebeautomatiškai atpažins dvikryptį sąryšį. Tai gali sukelti problemų jūsų programoje:
* Vykdant nereikalingus užklausimus tais pačiais duomenimis (šiuo atveju sukeliant N+1 užklausas):
```irb
irb> author = Author.first
irb> author.books.any? do |book|
irb> book.author.equal?(author) # Šis vykdo autoriaus užklausą kiekvienam knygai
irb> end
=> false
```
Nuorodų į modelį, turintį nesuderintus duomenis:
irb> author = Author.first irb> book = author.books.first irb> author.name == book.author.name => true irb> author.name = "Pakeistas vardas" irb> author.name == book.author.name => false
Nepavyksta automatiškai įrašyti susijusių objektų:
irb> author = Author.new irb> book = author.books.new irb> book.save! irb> book.persisted? => true irb> author.persisted? => false
Nepavyksta patikrinti, ar objektas yra privalomas arba nebūtinas:
irb> author = Author.new irb> book = author.books.new irb> book.valid? => false irb> book.errors.full_messages => ["Autorius turi egzistuoti"]
Active Record teikia :inverse_of
parinktį, leidžiančią aiškiai nurodyti abipusius ryšius:
class Author < ApplicationRecord
has_many :books, inverse_of: 'writer'
end
class Book < ApplicationRecord
belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
Įtraukus :inverse_of
parinktį has_many
asociacijos deklaracijoje,
Active Record dabar atpažįsta abipusį ryšį ir elgiasi kaip pirmiau pateiktuose pavyzdžiuose.
4 Išsamus asociacijų aprašymas
Šiose sekcijose pateikiami kiekvieno tipo asociacijos detalės, įskaitant pridedamus metodus ir parinktis, kurias galite naudoti deklaruodami asociaciją.
4.1 belongs_to
asociacijos aprašymas
Duomenų bazės terminais, belongs_to
asociacija reiškia, kad šio modelio lentelėje yra stulpelis, kuris atitinka nuorodą į kitą lentelę.
Tai gali būti naudojama nustatyti vieno į vieną arba vieno į daugelį ryšius, priklausomai nuo nustatymo.
Jei kito klasės lentelėje nuoroda yra vieno į vieną ryšį, tada turėtumėte naudoti has_one
vietoj to.
4.1.1 Metodai, pridedami belongs_to
Kai deklaruoji belongs_to
asociaciją, deklaruojančiajai klasei automatiškai pridedami 8 metodai, susiję su asociacija:
association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
reload_association
reset_association
association_changed?
association_previously_changed?
Visuose šiuose metodųose association
yra pakeičiama simboliu, kuris yra pirmasis argumentas belongs_to
. Pavyzdžiui, turint deklaraciją:
class Book < ApplicationRecord
belongs_to :author
end
Kiekvienas Book
modelio objektas turės šiuos metodus:
author
author=
build_author
create_author
create_author!
reload_author
reset_author
author_changed?
author_previously_changed?
PASTABA: Inicializuojant naują has_one
arba belongs_to
asociaciją, reikia naudoti build_
prefiksą, kad sukurtumėte asociaciją, o ne association.build
metodą, kuris būtų naudojamas has_many
arba has_and_belongs_to_many
asociacijoms. Norint sukurti vieną, naudokite create_
prefiksą.
4.1.1.1 association
association
metodas grąžina susijusį objektą, jei toks yra. Jei susijęs objektas nerastas, grąžinamas nil
.
@author = @book.author
Jei susijęs objektas jau buvo gautas iš duomenų bazės šiam objektui, bus grąžinta talpykloje esanti versija. Norint pakeisti šį elgesį (ir priversti perskaityti iš duomenų bazės), iškviečiama #reload_association
ant pagrindinio objekto.
@author = @book.reload_author
Norint iškrauti talpykloje esančią susijusio objekto versiją - tai sukels naują užklausą į duomenų bazę - iškviečiama #reset_association
ant pagrindinio objekto.
@book.reset_author
4.1.1.2 association=(associate)
association=
metodas priskiria susijusį objektą šiam objektui. Užkulisiuose tai reiškia ištraukti pagrindinės rakto reikšmę iš susijusio objekto ir nustatyti šio objekto užsienio rakto reikšmę ta pačia verte.
@book.author = @author
4.1.1.3 build_association(attributes = {})
build_association
metodas grąžina naują susijusio tipo objektą. Šis objektas bus sukuriamas iš perduotų atributų, ir bus nustatytas ryšys per šio objekto užsienio rakto reikšmę, bet susijęs objektas nebus dar išsaugotas.
@author = @book.build_author(author_number: 123,
author_name: "John Doe")
4.1.1.4 create_association(attributes = {})
create_association
metodas grąžina naują susijusio tipo objektą. Šis objektas bus sukuriamas iš perduotų atributų, bus nustatytas ryšys per šio objekto užsienio rakto reikšmę ir, kai jis praeis visus nurodytus patikrinimus asocijuotame modele, susijęs objektas bus išsaugotas.
@author = @book.create_author(author_number: 123,
author_name: "John Doe")
4.1.1.5 create_association!(attributes = {})
Daro tą patį, kaip ir create_association
aukščiau, bet jei įrašas yra netinkamas, iškelia ActiveRecord::RecordInvalid
išimtį.
4.1.1.6 association_changed?
association_changed?
metodas grąžina true
, jei naujas susijęs objektas buvo priskirtas ir užsienio rakto reikšmė bus atnaujinta kitame įraše.
```ruby
@book.author # => #
@book.author = Author.second # => #
@book.save! @book.author_changed? # => false ```
4.1.1.7 association_previously_changed?
association_previously_changed?
metodas grąžina true
, jei ankstesnis įrašas atnaujintas ir asociacija nukreipiama į naują asocijuotą objektą.
@book.author # => #<Author author_number: 123, author_name: "John Doe">
@book.author_previously_changed? # => false
@book.author = Author.second # => #<Author author_number: 456, author_name: "Jane Smith">
@book.save!
@book.author_previously_changed? # => true
4.1.2 belongs_to
parinktys
Nors „Rails“ naudoja protingus numatytuosius nustatymus, kurie daugeliu atvejų veiks gerai, gali būti situacijų, kai norite pritaikyti belongs_to
asociacijos nuorodos elgesį. Tokias pritaikymus galima lengvai atlikti, perduodant parinktis ir apribojimus, kai kuriate asociaciją. Pavyzdžiui, ši asociacija naudoja dvi tokiąsias parinktis:
class Book < ApplicationRecord
belongs_to :author, touch: :books_updated_at,
counter_cache: true
end
belongs_to
asociacija palaiko šias parinktis:
:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:primary_key
:inverse_of
:polymorphic
:touch
:validate
:optional
4.1.2.1 :autosave
Jei nustatote :autosave
parinktį į true
, „Rails“ išsaugos visus įkeltus asociacijos narius ir sunaikins tuos narius, kurie pažymėti sunaikinimui, kai išsaugosite pagrindinį objektą. Nustatant :autosave
į false
, tai nėra tas pats kaip nustatyti :autosave
parinktį. Jei :autosave
parinktis nenurodyta, tuomet nauji susiję objektai bus išsaugoti, tačiau atnaujinti susiję objektai nebus išsaugoti.
4.1.2.2 :class_name
Jei kitos modelio pavadinimas negali būti išvestas iš asociacijos pavadinimo, galite naudoti :class_name
parinktį, kad nurodytumėte modelio pavadinimą. Pavyzdžiui, jei knyga priklauso autorui, bet autorių turinčio modelio tikras pavadinimas yra Patron
, tai galite nustatyti taip:
class Book < ApplicationRecord
belongs_to :author, class_name: "Patron"
end
4.1.2.3 :counter_cache
:counter_cache
parinktis gali būti naudojama, norint efektyviau rasti priklausančių objektų skaičių. Pagalvokime apie šiuos modelius:
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
has_many :books
end
Su šiais deklaracijomis, @author.books.size
užklausa reikalauja duomenų bazės skambučio, kad būtų atlikta COUNT(*)
užklausa. Norint išvengti šio skambučio, galite pridėti skaitiklio talpyklą prie priklausančio modelio:
class Book < ApplicationRecord
belongs_to :author, counter_cache: true
end
class Author < ApplicationRecord
has_many :books
end
Su šia deklaracija „Rails“ laikys talpyklos reikšmę naujausia ir grąžins ją kaip atsakymą į size
metodą.
Nors :counter_cache
parinktis yra nurodyta modelyje, kuriame yra belongs_to
deklaracija, faktinis stulpelis turi būti pridėtas prie susijusio (has_many
) modelio. Pavyzdžiui, turėtumėte pridėti stulpelį, pavadinimu books_count
, prie Author
modelio.
Galite pakeisti numatytąjį stulpelio pavadinimą, nurodydami pasirinktinį stulpelio pavadinimą counter_cache
deklaracijoje, o ne true
. Pavyzdžiui, norint naudoti count_of_books
vietoje books_count
:
class Book < ApplicationRecord
belongs_to :author, counter_cache: :count_of_books
end
class Author < ApplicationRecord
has_many :books
end
PASTABA: :counter_cache
parinktį reikia nurodyti tik asociacijos belongs_to
pusėje.
Skaitiklio talpyklos stulpeliai pridedami prie savininko modelio sąrašo tik skaitymo tikslais per attr_readonly
.
Jei dėl kokios nors priežasties pakeičiate savininko modelio pirminio rakto reikšmę ir nepakeičiate suskaičiuotų modelių užsienio raktų, tuomet skaitiklio talpykla gali turėti pasenusią informaciją. Kitaip tariant, visi palikti modeliai vis tiek bus skaičiuojami kaip priklausantys skaitikliui. Norėdami ištaisyti pasenusią skaitiklio talpyklą, naudokite reset_counters
.
4.1.2.4 :dependent
Jei nustatote :dependent
parinktį į:
:destroy
, kai objektas yra sunaikinamas,destroy
bus iškviestas jo susijusiems objektams.:delete
, kai objektas yra sunaikinamas, visi jo susiję objektai bus tiesiogiai ištrinti iš duomenų bazės, neskambinant jųdestroy
metode.:destroy_async
: kai objektas yra sunaikinamas, į eilę bus įtrauktasActiveRecord::DestroyAssociationAsyncJob
darbas, kuris iškvies sunaikinimą jo susijusiuose objektuose. Tam veikti turi būti sukonfigūruotas „Active Job“. Nenaudokite šios parinkties, jei asociacija yra remiama užsienio rakto apribojimais jūsų duomenų bazėje. Užsienio rakto apribojimo veiksmai vyks toje pačioje transakcijoje, kurioje bus ištrintas savininkas. ĮSPĖJIMAS: Negalite nurodyti šios parinktiesbelongs_to
asociacijai, kuri yra susijusi suhas_many
asociacija kitoje klasėje. Tai gali sukelti paliktus įrašus duomenų bazėje.
4.1.2.5 :foreign_key
Pagal konvenciją, „Rails“ priima, kad stulpelis, naudojamas šio modelio užsienio rakto laikymui, yra asociacijos pavadinimas su pridėtu priesaga _id
. :foreign_key
parinktis leidžia tiesiogiai nustatyti užsienio rakto pavadinimą:
class Book < ApplicationRecord
belongs_to :author, class_name: "Patron",
foreign_key: "patron_id"
end
PATARIMAS: Nepaisant to, „Rails“ jums nesukurs užsienio rakto stulpelių. Jums reikia aiškiai juos apibrėžti kaip savo migracijos dalį.
4.1.2.6 :primary_key
Pagal konvenciją, „Rails“ priima, kad id
stulpelis naudojamas kaip pagrindinio rakto laikymui savo lentelėse. :primary_key
parinktis leidžia nurodyti kitą stulpelį.
Pavyzdžiui, turime users
lentelę su guid
kaip pagrindiniu raktu. Jei norime atskiro todos
lenteles, kurioje būtų laikomas užsienio raktas user_id
guid
stulpelyje, galime naudoti primary_key
parinktį, kad tai pasiektume taip:
class User < ApplicationRecord
self.primary_key = 'guid' # pagrindinis raktas yra guid, o ne id
end
class Todo < ApplicationRecord
belongs_to :user, primary_key: 'guid'
end
Kai vykdome @user.todos.create
, tada @todo
įrašas turės savo user_id
reikšmę kaip @user
guid
reikšmę.
4.1.2.7 :inverse_of
:inverse_of
parinktis nurodo has_many
arba has_one
asociacijos pavadinimą, kuris yra šios asociacijos atvirkštinis. Daugiau informacijos rasite dvikrypės asociacijos skyriuje.
class Author < ApplicationRecord
has_many :books, inverse_of: :author
end
class Book < ApplicationRecord
belongs_to :author, inverse_of: :books
end
4.1.2.8 :polymorphic
Pereinant true
į :polymorphic
parinktį, nurodoma, kad tai yra polimorfinė asociacija. Polimorfinės asociacijos buvo išsamiai aptartos ankstesniame šio vadovo skyriuje.
4.1.2.9 :touch
Jei nustatote :touch
parinktį kaip true
, tada susijusio objekto updated_at
arba updated_on
laiko žymeklis bus nustatytas į dabartinį laiką, kai šis objektas yra išsaugomas arba naikinamas:
class Book < ApplicationRecord
belongs_to :author, touch: true
end
class Author < ApplicationRecord
has_many :books
end
Šiuo atveju išsaugojus arba sunaikinus knygą, atnaujinamas laiko žymeklis susijusiam autorui. Taip pat galite nurodyti konkretų laiko žymeklio atributą, kurį norite atnaujinti:
class Book < ApplicationRecord
belongs_to :author, touch: :books_updated_at
end
4.1.2.10 :validate
Jei nustatote :validate
parinktį kaip true
, tada nauji susiję objektai bus patikrinami, kai išsaugosite šį objektą. Pagal numatytuosius nustatymus tai yra false
: nauji susiję objektai nebus patikrinami, kai išsaugosite šį objektą.
4.1.2.11 :optional
Jei nustatote :optional
parinktį kaip true
, tada susijusio objekto buvimas nebus patikrinamas. Pagal numatytuosius nustatymus ši parinktis yra nustatyta kaip false
.
4.1.3 belongs_to
užklausos
Gali būti atvejų, kai norite tinkinti belongs_to
užklausą. Tokius tinkinimus galima atlikti naudojant užklausos bloką. Pavyzdžiui:
class Book < ApplicationRecord
belongs_to :author, -> { where active: true }
end
Užklausos bloke galite naudoti bet kurį iš standartinių užklausų metodų. Apie juos bus aptarta žemiau:
where
includes
readonly
select
4.1.3.1 where
where
metodas leidžia nurodyti sąlygas, kurias turi atitikti susijęs objektas.
class Book < ApplicationRecord
belongs_to :author, -> { where active: true }
end
4.1.3.2 includes
Galite naudoti includes
metodą, kad nurodytumėte antrinio lygio asociacijas, kurios turėtų būti įkeltos kartu su šia asociacija. Pavyzdžiui, apsvarstykite šiuos modelius:
class Chapter < ApplicationRecord
belongs_to :book
end
class Book < ApplicationRecord
belongs_to :author
has_many :chapters
end
class Author < ApplicationRecord
has_many :books
end
Jei dažnai gaunate autorius tiesiogiai iš skyrių (@chapter.book.author
), galite padaryti savo kodą šiek tiek efektyvesnį, įtraukdami autorius į asociaciją nuo skyrių iki knygų:
class Chapter < ApplicationRecord
belongs_to :book, -> { includes :author }
end
class Book < ApplicationRecord
belongs_to :author
has_many :chapters
end
class Author < ApplicationRecord
has_many :books
end
PASTABA: Nereikia naudoti includes
tiesioginėms asociacijoms - tai yra, jei turite Book belongs_to :author
, tada autorius automatiškai įkeliamas, kai jis reikalingas.
4.1.3.3 readonly
Jei naudojate readonly
, tada susijęs objektas bus tik skaitymui, kai jis gaunamas per asociaciją.
4.1.3.4 select
select
metodas leidžia perrašyti SQL SELECT
sakinį, kuris naudojamas gauti duomenis apie susijusį objektą. Pagal numatytuosius nustatymus, „Rails“ gauna visus stulpelius.
PATARIMAS: Jei naudojate select
metodą su belongs_to
asociacija, taip pat turėtumėte nustatyti :foreign_key
parinktį, kad būtų garantuojami teisingi rezultatai.
4.1.4 Ar egzistuoja susiję objektai?
Galite patikrinti, ar egzistuoja susiję objektai, naudodami association.nil?
metodą:
if @book.author.nil?
@msg = "Šiai knygai nerastas autorius"
end
4.1.5 Kada objektai yra išsaugomi?
Objekto priskyrimas belongs_to
asociacijai ne automatiškai išsaugo objektą. Taip pat nėra išsaugomas susijęs objektas.
4.2 has_one
asociacijos nuoroda
has_one
asociacija sukuria vieno į vieną atitikmenį su kitu modeliu. Duomenų bazės terminais tai reiškia, kad kitas klasėje yra užsienio raktas. Jei šioje klasėje yra užsienio raktas, tuomet turėtumėte naudoti belongs_to
vietoj to.
4.2.1 has_one
pridėti metodai
Kai deklaruoji has_one
asociaciją, deklaruojančiai klasei automatiškai pridedami 6 su asociacija susiję metodai:
association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
reload_association
reset_association
Visuose šiuose metodųose association
pakeičiamas simboliu, perduotu kaip pirmasis argumentas has_one
. Pavyzdžiui, turint deklaraciją:
class Supplier < ApplicationRecord
has_one :account
end
Kiekvienas Supplier
modelio objektas turės šiuos metodus:
account
account=
build_account
create_account
create_account!
reload_account
reset_account
PASTABA: Inicializuojant naują has_one
ar belongs_to
asociaciją, turite naudoti build_
prefiksą, kad sukurtumėte asociaciją, o ne association.build
metodą, kuris būtų naudojamas has_many
ar has_and_belongs_to_many
asociacijoms. Norėdami sukurti vieną, naudokite create_
prefiksą.
4.2.1.1 association
association
metodas grąžina susijusį objektą, jei toks yra. Jei susijęs objektas nerastas, grąžinamas nil
.
@account = @supplier.account
Jei susijęs objektas jau buvo gautas iš duomenų bazės šiam objektui, grąžinama talpyklos versija. Norėdami pakeisti šį veikimą (ir priversti duomenų bazės skaitymą), iškvieskite #reload_association
ant pagrindinio objekto.
@account = @supplier.reload_account
Norėdami iškrauti talpyklos versiją susijusio objekto - priversti kitą prieigą, jei yra, užklausti ją iš duomenų bazės - iškvieskite #reset_association
ant pagrindinio objekto.
@supplier.reset_account
4.2.1.2 association=(associate)
association=
metodas priskiria susijusį objektą šiam objektui. Užkulisiuose tai reiškia ištraukti pagrindinį raktą iš šio objekto ir nustatyti susijusio objekto užsienio raktą ta pačia reikšme.
@supplier.account = @account
4.2.1.3 build_association(attributes = {})
build_association
metodas grąžina naują susijusio tipo objektą. Šis objektas bus sukuriamas iš perduotų atributų, ir per jo užsienio raktą bus nustatytas ryšys, tačiau susijęs objektas dar nebus išsaugotas.
@account = @supplier.build_account(terms: "Net 30")
4.2.1.4 create_association(attributes = {})
create_association
metodas grąžina naują susijusio tipo objektą. Šis objektas bus sukuriamas iš perduotų atributų, per jo užsienio raktą bus nustatytas ryšys, ir, kai jis praeis visus nurodytus asocijuoto modelio tikrinimus, susijęs objektas bus išsaugotas.
@account = @supplier.create_account(terms: "Net 30")
4.2.1.5 create_association!(attributes = {})
Daro tą patį kaip ir create_association
aukščiau, bet iškelia ActiveRecord::RecordInvalid
išimtį, jei įrašas yra netinkamas.
4.2.2 has_one
parinktys
Nors „Rails“ naudoja protingus numatytuosius nustatymus, kurie gerai veiks daugumoje situacijų, gali būti atvejų, kai norite tinkinti has_one
asociacijos nuorodos veikimą. Tokias tinkinimus galima lengvai atlikti perduodant parinktis, kuriant asociaciją. Pavyzdžiui, ši asociacija naudoja dvi tokiąsias parinktis:
class Supplier < ApplicationRecord
has_one :account, class_name: "Billing", dependent: :nullify
end
has_one
asociacija palaiko šias parinktis:
:as
:autosave
:class_name
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:touch
:validate
4.2.2.1 :as
Nustatant :as
parinktį, nurodoma, kad tai yra polimorfinė asociacija. Polimorfinės asociacijos buvo išsamiai aptartos ankstesniame šio vadovo skyriuje.
4.2.2.2 :autosave
Jei nustatote :autosave
parinktį į true
, „Rails“ išsaugos visus įkeltus asociacijos narius ir sunaikins tuos narius, kurie pažymėti sunaikinimui, kai išsaugosite pagrindinį objektą. Nustatant :autosave
į false
nėra tas pats kaip nepriskirti :autosave
parinkties. Jei :autosave
parinktis nėra nurodyta, tuomet nauji susiję objektai bus išsaugomi, bet atnaujinti susiję objektai nebus išsaugomi.
4.2.2.3 :class_name
Jei kito modelio pavadinimas negali būti išvestas iš asociacijos pavadinimo, galite naudoti :class_name
parinktį, kad pateiktumėte modelio pavadinimą. Pavyzdžiui, jei tiekėjas turi sąskaitą, bet faktinė sąskaitų modelio pavadinimas yra Billing
, tai galėtumėte tai nustatyti taip:
class Supplier < ApplicationRecord
has_one :account, class_name: "Billing"
end
4.2.2.4 :dependent
Valdo, kas nutiks susijusiam objektui, kai jo savininkas bus sunaikintas:
:destroy
sukelia susijusiam objektui taip pat būti sunaikintam:delete
sukelia susijusiam objektui būti ištrintam tiesiogiai iš duomenų bazės (todėl atgaliniai kvietimai nebus vykdomi):destroy_async
: kai objektas yra sunaikinamas, į eilę įdedamasActiveRecord::DestroyAssociationAsyncJob
darbas, kuris iškvies sunaikinimą jo susijusiems objektams. Norint, kad tai veiktų, reikia nustatyti veikimą su Active Job. Nenaudokite šios parinkties, jei asociacija yra pagrįsta užsienio raktų apribojimais jūsų duomenų bazėje. Užsienio raktų apribojimo veiksmai vyks toje pačioje transakcijoje, kuri ištrins savininką.:nullify
sukelia užsienio rakto nustatymą įNULL
. Polimorfinio tipo stulpelis taip pat tampaNULL
polimorfinėse asociacijose. Atgaliniai kvietimai nebus vykdomi.:restrict_with_exception
sukeliaActiveRecord::DeleteRestrictionError
išimtį, jei yra susijęs įrašas:restrict_with_error
sukelia klaidą savininkui, jei yra susijęs objektas
Svarbu nenustatyti arba palikti :nullify
parinktį toms asociacijoms, kurios turi NOT NULL
duomenų bazės apribojimus. Jei nenustatysite dependent
kaip destroy
tokioms asociacijoms, nebegalėsite pakeisti susijusio objekto, nes pradinio susijusio objekto užsienio rakto reikšmė bus nustatyta į neleistiną NULL
reikšmę.
4.2.2.5 :foreign_key
Pagal nutylėjimą, Rails priima, kad stulpelis, naudojamas laikyti užsienio raktą kito modelio, yra šio modelio pavadinimas su pridėtu priesaga _id
. :foreign_key
parinktis leidžia tiesiogiai nustatyti užsienio rakto pavadinimą:
class Supplier < ApplicationRecord
has_one :account, foreign_key: "supp_id"
end
PATARIMAS: Bet kuriuo atveju, Rails jums nesukurs užsienio rakto stulpelių. Jums reikia aiškiai juos apibrėžti kaip savo migracijos dalį.
4.2.2.6 :inverse_of
:inverse_of
parinktis nurodo belongs_to
asociacijos pavadinimą, kuris yra šios asociacijos atvirkštinė. Daugiau informacijos rasite dvikrypės asociacijos skyriuje.
class Supplier < ApplicationRecord
has_one :account, inverse_of: :supplier
end
class Account < ApplicationRecord
belongs_to :supplier, inverse_of: :account
end
4.2.2.7 :primary_key
Pagal nutylėjimą, Rails priima, kad stulpelis, naudojamas laikyti šio modelio pirminį raktą, yra id
. Galite pakeisti tai ir aiškiai nurodyti pirminį raktą naudodami :primary_key
parinktį.
4.2.2.8 :source
:source
parinktis nurodo šaltinio asociacijos pavadinimą has_one :through
asociacijai.
4.2.2.9 :source_type
:source_type
parinktis nurodo šaltinio asociacijos tipą has_one :through
asociacijai, kuri eina per polimorfinę asociaciją.
class Author < ApplicationRecord
has_one :book
has_one :hardback, through: :book, source: :format, source_type: "Hardback"
has_one :dust_jacket, through: :hardback
end
class Book < ApplicationRecord
belongs_to :format, polymorphic: true
end
class Paperback < ApplicationRecord; end
class Hardback < ApplicationRecord
has_one :dust_jacket
end
class DustJacket < ApplicationRecord; end
4.2.2.10 :through
:through
parinktis nurodo sujungimo modelį, per kurį atliekamas užklausos vykdymas. has_one :through
asociacijos buvo išsamiai aptartos ankstesniame šio vadovo skyriuje.
4.2.2.11 :touch
Jei nustatote :touch
parinktį kaip true
, tada susijusio objekto updated_at
arba updated_on
laiko žymeklis bus nustatytas į dabartinį laiką, kai šis objektas yra išsaugomas arba sunaikinamas:
class Supplier < ApplicationRecord
has_one :account, touch: true
end
class Account < ApplicationRecord
belongs_to :supplier
end
Šiuo atveju, išsaugojus arba sunaikinus tiekėją, bus atnaujintas susijusios sąskaitos laiko žymeklis. Taip pat galite nurodyti konkretų laiko žymeklio atributą, kurį norite atnaujinti:
class Supplier < ApplicationRecord
has_one :account, touch: :suppliers_updated_at
end
4.2.2.12 :validate
Jei nustatote :validate
parinktį kaip true
, tada nauji susiję objektai bus patikrinami, kai išsaugosite šį objektą. Pagal nutylėjimą tai yra false
: nauji susiję objektai nebus patikrinami, kai išsaugosite šį objektą.
4.2.3 has_one
užklausoms
Gali būti atvejų, kai norite pritaikyti užklausą, naudojamą has_one
. Tokias individualias pritaikymo galimybes galima pasiekti naudojant užklausos bloką. Pavyzdžiui:
class Supplier < ApplicationRecord
has_one :account, -> { where active: true }
end
Galite naudoti bet kurį iš standartinių užklausos metodų viduje scope
bloko. Žemiau aptariami šie metodai:
where
includes
readonly
select
4.2.3.1 where
where
metodas leidžia nurodyti sąlygas, kurias turi atitikti susijęs objektas.
class Supplier < ApplicationRecord
has_one :account, -> { where "confirmed = 1" }
end
4.2.3.2 includes
Galite naudoti includes
metodą, norėdami nurodyti antrinio lygio susijimus, kurie turėtų būti užkraunami kartu su šiuo susijimu. Pavyzdžiui, apsvarstykite šiuos modelius:
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
belongs_to :representative
end
class Representative < ApplicationRecord
has_many :accounts
end
Jei dažnai gaunate atstovus tiesiogiai iš tiekėjų (@supplier.account.representative
), galite padaryti savo kodą šiek tiek efektyvesnį, įtraukdami atstovus į susijimą nuo tiekėjų iki sąskaitų:
class Supplier < ApplicationRecord
has_one :account, -> { includes :representative }
end
class Account < ApplicationRecord
belongs_to :supplier
belongs_to :representative
end
class Representative < ApplicationRecord
has_many :accounts
end
4.2.3.3 readonly
Jei naudojate readonly
metodą, tada susijęs objektas bus tik skaitymui, kai jis gaunamas per susijimą.
4.2.3.4 select
select
metodas leidžia perrašyti SQL SELECT
sakinį, kuris naudojamas gauti duomenis apie susijusį objektą. Pagal numatytuosius nustatymus, „Rails“ gauna visus stulpelius.
4.2.4 Ar egzistuoja susiję objektai?
Galite patikrinti, ar egzistuoja susiję objektai, naudodami association.nil?
metodą:
if @supplier.account.nil?
@msg = "Šiam tiekėjui nerasta sąskaita"
end
4.2.5 Kada objektai yra išsaugomi?
Kai priskiriate objektą has_one
susijimui, tas objektas automatiškai išsaugomas (norint atnaujinti jo užsienio raktą). Be to, bet koks pakeistas objektas taip pat automatiškai išsaugomas, nes jo užsienio raktas taip pat pasikeis.
Jei vienas iš šių išsaugojimų nepavyksta dėl validacijos klaidų, tuomet priskyrimo sakinys grąžina false
, o pati priskyrimo operacija yra atšaukiama.
Jei pagrindinis objektas (tas, kuris deklaruoja has_one
susijimą) nėra išsaugotas (tai yra, new_record?
grąžina true
), tada vaikiniai objektai nėra išsaugomi. Jie bus automatiškai išsaugomi, kai pagrindinis objektas bus išsaugotas.
Jei norite priskirti objektą has_one
susijimui be išsaugojimo, naudokite build_association
metodą.
4.3 has_many
Susijimo nuoroda
has_many
susijimas sukuria vieno į daugelio santykį su kitu modeliu. Duomenų bazės terminais šis susijimas reiškia, kad kitas klasės objektas turės užsienio raktą, kuris rodo į šios klasės objektus.
4.3.1 has_many
Pridedami metodai
Kai deklaruoja has_many
susijimą, deklaruojančiai klasei automatiškai pridedami 17 susiję metodai:
collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {})
collection.create(attributes = {})
collection.create!(attributes = {})
collection.reload
Visuose šiuose metodųose collection
pakeičiamas simboliu, perduotu kaip pirmasis argumentas has_many
, o collection_singular
pakeičiamas vienaskaitine šio simbolio versija. Pavyzdžiui, turint deklaraciją:
class Author < ApplicationRecord
has_many :books
end
Kiekvienas Author
modelio objektas turės šiuos metodus:
books
books<<(object, ...)
books.delete(object, ...)
books.destroy(object, ...)
books=(objects)
book_ids
book_ids=(ids)
books.clear
books.empty?
books.size
books.find(...)
books.where(...)
books.exists?(...)
books.build(attributes = {}, ...)
books.create(attributes = {})
books.create!(attributes = {})
books.reload
4.3.1.1 collection
collection
metodas grąžina sąryšį su visais susijusiais objektais. Jei nėra susijusių objektų, grąžinamas tuščias sąryšis.
@books = @author.books
4.3.1.2 collection<<(object, ...)
collection<<
metodas prideda vieną arba daugiau objektų į sąryšį, nustatant jų užsienio raktus į skambinančio modelio pirminį raktą.
@author.books << @book1
4.3.1.3 collection.delete(object, ...)
collection.delete
metodas pašalina vieną arba daugiau objektų iš sąryšio, nustatant jų užsienio raktus į NULL
.
@author.books.delete(@book1)
ĮSPĖJIMAS: Be to, objektai bus sunaikinti, jei jie susiję su dependent: :destroy
, ir ištrinti, jei jie susiję su dependent: :delete_all
.
4.3.1.4 collection.destroy(object, ...)
collection.destroy
metodas pašalina vieną arba daugiau objektų iš sąryšio, vykdant destroy
kiekvienam objektui.
@author.books.destroy(@book1)
ĮSPĖJIMAS: Objektai visada bus pašalinti iš duomenų bazės, ignoruojant :dependent
parinktį.
4.3.1.5 collection=(objects)
collection=
metodas padaro sąryšį turintį tik pateiktus objektus, pridedant ir pašalinant, kaip tinkama. Pakeitimai išlieka duomenų bazėje.
4.3.1.6 collection_singular_ids
collection_singular_ids
metodas grąžina objektų kolekcijos id masyvą.
@book_ids = @author.book_ids
4.3.1.7 collection_singular_ids=(ids)
collection_singular_ids=
metodas padaro, kad kolekcija turėtų tik tuos objektus, kurie yra identifikuojami pagal pateiktus pirminio rakto reikšmes, pridedant ir ištrinant pagal poreikį. Pakeitimai yra išsaugomi duomenų bazėje.
4.3.1.8 collection.clear
collection.clear
metodas pašalina visus objektus iš kolekcijos pagal nustatytą strategiją, kurią nurodo dependent
parinktis. Jei parinktis nenurodyta, naudojama numatytoji strategija. Numatytoji strategija has_many :through
asociacijoms yra delete_all
, o has_many
asociacijoms - nustatyti užsienio raktus į NULL
.
@author.books.clear
ĮSPĖJIMAS: Objektai bus ištrinti, jei jie susiję su dependent: :destroy
arba dependent: :destroy_async
, taip pat kaip ir su dependent: :delete_all
.
4.3.1.9 collection.empty?
collection.empty?
metodas grąžina true
, jei kolekcija neapima jokių susijusių objektų.
<% if @author.books.empty? %>
Nerasta jokių knygų
<% end %>
4.3.1.10 collection.size
collection.size
metodas grąžina objektų skaičių kolekcijoje.
@book_count = @author.books.size
4.3.1.11 collection.find(...)
collection.find
metodas randa objektus kolekcijos lentelėje.
@available_book = @author.books.find(1)
4.3.1.12 collection.where(...)
collection.where
metodas randa objektus kolekcijoje pagal pateiktas sąlygas, tačiau objektai yra įkraunami tinginiškai, tai reiškia, kad duomenų bazė yra užklausinėjama tik tada, kai prieinami objektai.
@available_books = author.books.where(available: true) # Dar nėra užklausos
@available_book = @available_books.first # Dabar bus užklausta duomenų bazė
4.3.1.13 collection.exists?(...)
collection.exists?
metodas patikrina, ar kolekcijos lentelėje yra objektas, atitinkantis pateiktas sąlygas.
4.3.1.14 collection.build(attributes = {})
collection.build
metodas grąžina vieną arba masyvą naujų asocijuotos rūšies objektų. Objektai bus sukuriami iš perduotų atributų, ir bus sukurtas ryšys per jų užsienio raktą, tačiau susiję objektai dar nebus išsaugoti.
@book = author.books.build(published_at: Time.now,
book_number: "A12345")
@books = author.books.build([
{ published_at: Time.now, book_number: "A12346" },
{ published_at: Time.now, book_number: "A12347" }
])
4.3.1.15 collection.create(attributes = {})
collection.create
metodas grąžina vieną arba masyvą naujų asocijuotos rūšies objektų. Objektai bus sukuriami iš perduotų atributų, bus sukurtas ryšys per jų užsienio raktą, ir, kai jie praeis visus asocijuoto modelio nurodytus patikrinimus, susijęs objektas bus išsaugotas.
@book = author.books.create(published_at: Time.now,
book_number: "A12345")
@books = author.books.create([
{ published_at: Time.now, book_number: "A12346" },
{ published_at: Time.now, book_number: "A12347" }
])
4.3.1.16 collection.create!(attributes = {})
Daro tą patį, kas ir collection.create
aukščiau, tačiau iškelia ActiveRecord::RecordInvalid
išimtį, jei įrašas yra netinkamas.
4.3.1.17 collection.reload
collection.reload
metodas grąžina visų susijusių objektų sąryšį, priverčiant duomenų bazės skaitymą. Jei nėra susijusių objektų, grąžinamas tuščias sąryšis.
@books = author.books.reload
4.3.2 has_many
parinktys
Nors „Rails“ naudoja protingus numatytuosius nustatymus, kurie daugumai situacijų veiks gerai, gali būti atvejų, kai norite tinkinti has_many
asociacijos elgesį. Tokius tinkinimus galima lengvai atlikti perduodant parinktis kuriant asociaciją. Pavyzdžiui, ši asociacija naudoja dvi tokių parinkčių:
class Author < ApplicationRecord
has_many :books, dependent: :delete_all, validate: false
end
has_many
asociacija palaiko šias parinktis:
:as
:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate
4.3.2.1 :as
Nustatant :as
parinktį, nurodoma, kad tai yra polimorfinė asociacija, kaip aptarta ankstesniame šio vadovo skyriuje.
4.3.2.2 :autosave
Jei nustatote :autosave
parinktį į true
, „Rails“ išsaugos visus įkrautos asociacijos narius ir sunaikins tuos narius, kurie pažymėti sunaikinimui, kai išsaugosite pagrindinį objektą. Nustatant :autosave
į false
nėra tas pats kaip nenurodyti :autosave
parinkties. Jei :autosave
parinktis nėra nurodyta, tuomet nauji susiję objektai bus išsaugoti, tačiau atnaujinti susiję objektai nebus išsaugoti.
4.3.2.3 :class_name
Jei kito modelio pavadinimas negali būti išvestas iš asociacijos pavadinimo, galite naudoti :class_name
parinktį, kad nurodytumėte modelio pavadinimą. Pavyzdžiui, jei autorius turi daug knygų, bet knygų modelio tikras pavadinimas yra Transaction
, tai galėtumėte tai padaryti taip:
class Author < ApplicationRecord
has_many :books, class_name: "Transaction"
end
4.3.2.4 :counter_cache
Šią parinktį galima naudoti, norint konfigūruoti pasirinktiną :counter_cache
pavadinimą. Šią parinktį reikia naudoti tik tada, kai pasirinkote savo :counter_cache
pavadinimą priklauso_sąjungai.
4.3.2.5 :dependent
Valdo, kas vyksta su susijusiais objektais, kai jų savininkas yra sunaikintas:
:destroy
sukelia visus susijusius objektus taip pat būti sunaikinti:delete_all
sukelia visus susijusius objektus būti ištrinti tiesiogiai iš duomenų bazės (todėl grįžtamieji kvietimai nebus vykdomi):destroy_async
: kai objektas yra sunaikintas, į eilę įtraukiamasActiveRecord::DestroyAssociationAsyncJob
darbas, kuris sunaikins susijusius objektus. Tam, kad tai veiktų, reikia nustatyti aktyvų darbą.:nullify
sukelia, kad užsienio raktas būtų nustatytas įNULL
. Polimorfinio tipo stulpelis taip pat tampa nustatytas įNULL
polimorfinėse sąjungose. Grįžtamieji kvietimai nevykdomi.:restrict_with_exception
sukeliaActiveRecord::DeleteRestrictionError
išimtį, jei yra susijusių įrašų:restrict_with_error
sukelia klaidą savininkui, jei yra susijusių objektų
:destroy
ir :delete_all
parinktys taip pat veikia collection.delete
ir collection=
metodų semantiką, sunaikindamos susijusius objektus, kai jie pašalinami iš kolekcijos.
4.3.2.6 :foreign_key
Pagal nutylėjimą, „Rails“ priima, kad stulpelis, naudojamas kitame modelyje esančiam užsienio raktui laikyti, yra šio modelio pavadinimas su pridėtu priesaga _id
. :foreign_key
parinktis leidžia tiesiogiai nustatyti užsienio rakto pavadinimą:
class Author < ApplicationRecord
has_many :books, foreign_key: "cust_id"
end
PATARIMAS: Bet kuriuo atveju „Rails“ jums nesukurs užsienio rakto stulpelių. Jums reikia aiškiai juos apibrėžti kaip savo migracijos dalį.
4.3.2.7 :inverse_of
:inverse_of
parinktis nurodo atvirkštinį šios sąjungos priklaukiančios_sąjungos
pavadinimą. Daugiau informacijos rasite dvikrypčių sąjungų skyriuje.
class Author < ApplicationRecord
has_many :books, inverse_of: :author
end
class Book < ApplicationRecord
belongs_to :author, inverse_of: :books
end
4.3.2.8 :primary_key
Pagal nutylėjimą, „Rails“ priima, kad asociacijos pagrindinio rakto laikyti stulpelis yra id
. Galite perrašyti tai ir aiškiai nurodyti pagrindinį raktą naudodami :primary_key
parinktį.
Tarkime, kad users
lentelėje yra id
kaip pagrindinis raktas, bet taip pat yra guid
stulpelis. Reikalavimas yra tas, kad todos
lentelėje guid
stulpelio reikšmė turėtų būti laikoma užsienio raktu, o ne id
reikšme. Tai galima pasiekti taip:
class User < ApplicationRecord
has_many :todos, primary_key: :guid
end
Dabar, jei vykdysime @todo = @user.todos.create
, tada @todo
įrašo user_id
reikšmė bus @user
guid
reikšmė.
4.3.2.9 :source
:source
parinktis nurodo šaltinio sąjungos pavadinimą has_many :through
sąjungai. Šią parinktį reikia naudoti tik tada, kai šaltinio sąjungos pavadinimas negali būti automatiškai nustatytas iš sąjungos pavadinimo.
4.3.2.10 :source_type
:source_type
parinktis nurodo šaltinio sąjungos tipą has_many :through
sąjungai, kuri eina per polimorfinę sąjungą.
class Author < ApplicationRecord
has_many :books
has_many :paperbacks, through: :books, source: :format, source_type: "Paperback"
end
class Book < ApplicationRecord
belongs_to :format, polymorphic: true
end
class Hardback < ApplicationRecord; end
class Paperback < ApplicationRecord; end
4.3.2.11 :through
:through
parinktis nurodo jungiamą modelį, per kurį atliekamas užklausos vykdymas. has_many :through
sąjungos suteikia būdą įgyvendinti daugybės į daugelį santykių, kaip aptarta šiame gidui, pavyzdžiui.
4.3.2.12 :validate
Jei nustatysite :validate
parinktį į false
, tada nauji susiję objektai nebus tikrinami, kai išsaugosite šį objektą. Pagal nutylėjimą tai yra true
: nauji susiję objektai bus tikrinami, kai išsaugomas šis objektas.
4.3.3 Užklausos has_many
riboms
Gali būti atvejų, kai norite tinkinti užklausą, naudojamą has_many
. Tokias tinkinimus galima atlikti naudojant ribos bloką. Pavyzdžiui:
class Author < ApplicationRecord
has_many :books, -> { where processed: true }
end
Ribų bloke galite naudoti bet kurį iš standartinių užklausų metodų. Apie juos aptariami žemiau:
where
extending
group
includes
limit
offset
order
readonly
select
distinct
4.3.3.1 where
where
metodas leidžia nurodyti sąlygas, kurias turi atitikti susijęs objektas.
class Author < ApplicationRecord
has_many :confirmed_books, -> { where "confirmed = 1" },
class_name: "Book"
end
Taip pat galite nustatyti sąlygas naudodami hash'ą:
class Author < ApplicationRecord
has_many :confirmed_books, -> { where confirmed: true },
class_name: "Book"
end
Jei naudojate hash'o stiliaus where
parinktį, tada įrašų kūrimas per šią asociaciją bus automatiškai apribotas naudojant hash'ą. Šiuo atveju, naudojant author.confirmed_books.create
arba author.confirmed_books.build
, bus sukurti knygos, kuriose patvirtintas stulpelis turi reikšmę true
.
4.3.3.2 extending
extending
metodas nurodo vardintą modulį, kuris išplečia asociacijos tarpininką. Asociacijos plėtinių aprašymas išsamiai aptariamas vėliau šiame vadove.
4.3.3.3 group
group
metodas nurodo atributo pavadinimą, pagal kurį rezultatų rinkinys bus sugrupuotas, naudojant GROUP BY
sąlygą paieškos SQL užklausoje.
class Author < ApplicationRecord
has_many :chapters, -> { group 'books.id' },
through: :books
end
4.3.3.4 includes
Galite naudoti includes
metodą, norėdami nurodyti antrinio lygio asociacijas, kurios turėtų būti iš anksto įkeltos, kai naudojama ši asociacija. Pavyzdžiui, apsvarstykite šiuos modelius:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
has_many :chapters
end
class Chapter < ApplicationRecord
belongs_to :book
end
Jei dažnai gaunate skyrius tiesiogiai iš autorių (author.books.chapters
), galite padaryti savo kodą šiek tiek efektyvesnį, įtraukdami skyrius į asociaciją nuo autorių iki knygų:
class Author < ApplicationRecord
has_many :books, -> { includes :chapters }
end
class Book < ApplicationRecord
belongs_to :author
has_many :chapters
end
class Chapter < ApplicationRecord
belongs_to :book
end
4.3.3.5 limit
limit
metodas leidžia apriboti visų objektų, kurie bus gauti per asociaciją, skaičių.
class Author < ApplicationRecord
has_many :recent_books,
-> { order('published_at desc').limit(100) },
class_name: "Book"
end
4.3.3.6 offset
offset
metodas leidžia nurodyti pradinį poslinkį, skirtą objektų gavimui per asociaciją. Pavyzdžiui, -> { offset(11) }
praleis pirmuosius 11 įrašų.
4.3.3.7 order
order
metodas nurodo tvarką, kuria bus gaunami susiję objektai (naudojant sintaksę, kurią naudoja SQL ORDER BY
sąlyga).
class Author < ApplicationRecord
has_many :books, -> { order "date_confirmed DESC" }
end
4.3.3.8 readonly
Jei naudojate readonly
metodą, tada gauti asocijuoti objektai bus tik skaityti.
4.3.3.9 select
select
metodas leidžia perrašyti SQL SELECT
sąlygą, kurią naudoja duomenų apie susijusius objektus gavimui. Pagal numatytuosius nustatymus, „Rails“ gauna visus stulpelius.
ĮSPĖJIMAS: Jei nurodote savo select
, įsitikinkite, kad įtraukiate pagrindinio rakto ir užsienio rakto stulpelius susijusio modelio. Jei to nepadarysite, „Rails“ išmes klaidą.
4.3.3.10 distinct
Naudokite distinct
metodą, kad kolekcija būtų laisva nuo dublikatų. Tai daugiausia naudinga kartu su :through
parinktimi.
class Person < ApplicationRecord
has_many :readings
has_many :articles, through: :readings
end
irb> person = Person.create(name: 'John')
irb> article = Article.create(name: 'a1')
irb> person.articles << article
irb> person.articles << article
irb> person.articles.to_a
=> [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">]
irb> Reading.all.to_a
=> [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
Pirmiau pateiktu atveju yra du skaitymai, o person.articles
išveda abu, nors šie įrašai rodo į tą pačią straipsnį.
Dabar nustatysime distinct
:
class Person
has_many :readings
has_many :articles, -> { distinct }, through: :readings
end
irb> person = Person.create(name: 'Honda')
irb> article = Article.create(name: 'a1')
irb> person.articles << article
irb> person.articles << article
irb> person.articles.to_a
=> [#<Article id: 7, name: "a1">]
irb> Reading.all.to_a
=> [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
Pirmiau pateiktu atveju vis dar yra du skaitymai. Tačiau person.articles
rodo tik vieną straipsnį, nes kolekcija įkelia tik unikalius įrašus.
Jei norite užtikrinti, kad įterpiant visi įrašai išsaugotame asociacijoje būtų unikalūs (kad galėtumėte būti tikri, kad tikrinant asociaciją niekada nerasite dublikatinių įrašų), turėtumėte pridėti unikalų indeksą paties lentelėje. Pavyzdžiui, jei turite lentelę pavadinimu readings
ir norite užtikrinti, kad straipsniai gali būti pridėti prie asmens tik vieną kartą, galite pridėti šį kodą migracijoje:
add_index :readings, [:person_id, :article_id], unique: true
Kai turite šį unikalų indeksą, bandant pridėti straipsnį prie žmogaus du kartus, bus iškelta ActiveRecord::RecordNotUnique
klaida:
irb> person = Person.create(name: 'Honda')
irb> article = Article.create(name: 'a1')
irb> person.articles << article
irb> person.articles << article
ActiveRecord::RecordNotUnique
Atkreipkite dėmesį, kad patikrinimas dėl unikalumo naudojant kažką panašaus į include?
yra priklausomas nuo varžybų sąlygų. Nenagrinėkite include?
naudoti siekiant užtikrinti asociacijos išskirtinumą. Pavyzdžiui, naudojant aukščiau pateiktą straipsnio pavyzdį, toks kodas būtų varžantis, nes keli naudotojai galėtų tai bandyti tuo pačiu metu:
person.articles << article unless person.articles.include?(article)
4.3.4 Kada objektai yra išsaugomi?
Kai priskiriate objektą has_many
asociacijai, tas objektas automatiškai išsaugomas (norint atnaujinti jo svetainės raktą). Jei priskiriate kelis objektus vienu teiginio, visi jie yra išsaugomi.
Jei bet kuris iš šių išsaugojimų nepavyksta dėl validacijos klaidų, tada priskyrimo teiginys grąžina false
, o pats priskyrimas yra atšaukiamas.
Jei pagrindinis objektas (tas, kuris deklaruoja has_many
asociaciją) yra neišsaugotas (tai yra, new_record?
grąžina true
), tada vaikų objektai nėra išsaugomi, kai jie yra pridedami. Visi neišsaugoti asociacijos nariai automatiškai bus išsaugomi, kai bus išsaugotas pagrindinis objektas.
Jei norite priskirti objektą has_many
asociacijai, nesaugodami objekto, naudokite collection.build
metodą.
4.4 has_and_belongs_to_many
asociacijos nuoroda
has_and_belongs_to_many
asociacija sukuria daugybės su kitu modeliu ryšį. Duomenų bazės sąlygomis tai susieja dvi klases per tarpinę jungiamąją lentelę, kurioje yra svetainės raktai, rodantys į kiekvieną iš klasių.
4.4.1 has_and_belongs_to_many
pridėti metodai
Kai deklaruoja has_and_belongs_to_many
asociaciją, deklaruojančiai klasei automatiškai pridedami keletas su asociacija susijusių metodų:
collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {})
collection.create(attributes = {})
collection.create!(attributes = {})
collection.reload
Visuose šiuose metodųose collection
yra pakeičiama simboliu, perduotu kaip pirmasis argumentas has_and_belongs_to_many
, o collection_singular
yra pakeičiama vienaskaitos versija to simbolio. Pavyzdžiui, turint deklaraciją:
class Part < ApplicationRecord
has_and_belongs_to_many :assemblies
end
Kiekvienas Part
modelio egzempliorius turės šiuos metodus:
assemblies
assemblies<<(object, ...)
assemblies.delete(object, ...)
assemblies.destroy(object, ...)
assemblies=(objects)
assembly_ids
assembly_ids=(ids)
assemblies.clear
assemblies.empty?
assemblies.size
assemblies.find(...)
assemblies.where(...)
assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
assemblies.create!(attributes = {})
assemblies.reload
4.4.1.1 Papildomi stulpelių metodai
Jei has_and_belongs_to_many
asociacijos jungiamoji lentelė turi papildomų stulpelių, virš dviejų svetainių raktų, šie stulpeliai bus pridėti kaip atributai į įrašus, gaunamus per tą asociaciją. Grąžinami įrašai su papildomais atributais visada bus tik skaitymui, nes „Rails“ negali išsaugoti pakeitimų šiems atributams.
ĮSPĖJIMAS: Papildomų atributų naudojimas jungiamosiose lentelėse has_and_belongs_to_many
asociacijoje yra pasenusi. Jei jums reikia tokio sudėtingo elgesio lentelėje, kuri jungia du modelius daugybės su daugybe santykiu, turėtumėte naudoti has_many :through
asociaciją vietoj has_and_belongs_to_many
.
4.4.1.2 collection
collection
metodas grąžina visų susijusių objektų sąryšį. Jei nėra susijusių objektų, grąžinamas tuščias sąryšis.
@assemblies = @part.assemblies
4.4.1.3 collection<<(object, ...)
collection<<
metodas prideda vieną ar daugiau objektų į sąryšį, sukurdamas įrašus jungiamojoje lentelėje.
@part.assemblies << @assembly1
PASTABA: Šis metodas yra sinonimas collection.concat
ir collection.push
.
4.4.1.4 collection.delete(object, ...)
collection.delete
metodas pašalina vieną ar daugiau objektų iš sąryšio, ištrindamas įrašus jungiamojoje lentelėje. Tai neapima objektų naikinimo.
@part.assemblies.delete(@assembly1)
4.4.1.5 collection.destroy(object, ...)
collection.destroy
metodas pašalina vieną ar daugiau objektų iš sąryšio, ištrindamas įrašus jungiamojoje lentelėje. Tai neapima objektų naikinimo.
@part.assemblies.destroy(@assembly1)
4.4.1.6 collection=(objects)
collection=
metodas padaro sąryšį turintį tik pateiktus objektus, pridedant ir pašalinant pagal poreikį. Pakeitimai yra išsaugomi duomenų bazėje.
4.4.1.7 collection_singular_ids
collection_singular_ids
metodas grąžina masyvą, kuriame yra objektų sąryšio identifikatoriai.
@assembly_ids = @part.assembly_ids
4.4.1.8 collection_singular_ids=(ids)
collection_singular_ids=
metodas padaro sąryšį turintį tik objektus, kurie yra nurodyti pagal pateiktus pirminio rakto reikšmes, pridedant ir pašalinant pagal poreikį. Pakeitimai yra išsaugomi duomenų bazėje.
4.4.1.9 collection.clear
Metodas collection.clear
pašalina visus objektus iš kolekcijos, ištrindamas eilutes iš jungiamosios lentelės. Tai nesunaikina susijusių objektų.
4.4.1.10 collection.empty?
Metodas collection.empty?
grąžina true
, jei kolekcija neapima jokių susijusių objektų.
<% if @part.assemblies.empty? %>
Šis komponentas nenaudojamas jokiose montavimuose
<% end %>
4.4.1.11 collection.size
Metodas collection.size
grąžina objektų skaičių kolekcijoje.
@assembly_count = @part.assemblies.size
4.4.1.12 collection.find(...)
Metodas collection.find
randa objektus kolekcijos lentelėje.
@assembly = @part.assemblies.find(1)
4.4.1.13 collection.where(...)
Metodas collection.where
randa objektus kolekcijoje pagal pateiktas sąlygas, tačiau objektai yra įkraunami tinginiu būdu, tai reiškia, kad duomenų bazė yra užklausiama tik tada, kai prieiga prie objektų yra atliekama.
@new_assemblies = @part.assemblies.where("created_at > ?", 2.days.ago)
4.4.1.14 collection.exists?(...)
Metodas collection.exists?
patikrina, ar kolekcijos lentelėje yra objektas, atitinkantis pateiktas sąlygas.
4.4.1.15 collection.build(attributes = {})
Metodas collection.build
grąžina naują susijusio tipo objektą. Šis objektas bus sukurtas iš perduotų atributų, ir bus sukurtas ryšys per jungiamąją lentelę, tačiau susijęs objektas dar nebus išsaugotas.
@assembly = @part.assemblies.build({ assembly_name: "Transmisijos korpusas" })
4.4.1.16 collection.create(attributes = {})
Metodas collection.create
grąžina naują susijusio tipo objektą. Šis objektas bus sukurtas iš perduotų atributų, bus sukurtas ryšys per jungiamąją lentelę ir, kai jis praeis visus susijusio modelio nustatytus patikrinimus, susijęs objektas bus išsaugotas.
@assembly = @part.assemblies.create({ assembly_name: "Transmisijos korpusas" })
4.4.1.17 collection.create!(attributes = {})
Atlieka tą patį kaip ir collection.create
, tačiau iškelia ActiveRecord::RecordInvalid
išimtį, jei įrašas yra netinkamas.
4.4.1.18 collection.reload
Metodas collection.reload
grąžina visus susijusius objektus kaip Reliaciją, priverčiant duomenų bazės skaitymą. Jei nėra susijusių objektų, grąžinama tuščia Reliacija.
@assemblies = @part.assemblies.reload
4.4.2 Galimybės has_and_belongs_to_many
Nors „Rails“ naudoja protingus numatytuosius nustatymus, kurie daugeliu atvejų veiks gerai, gali būti situacijų, kai norite tinkinti has_and_belongs_to_many
asociacijos elgesį. Tokius tinkinimus galima lengvai atlikti, perduodant parinktis kuriant asociaciją. Pavyzdžiui, ši asociacija naudoja dvi tokių parinkčių:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, -> { readonly },
autosave: true
end
has_and_belongs_to_many
asociacija palaiko šias parinktis:
:association_foreign_key
:autosave
:class_name
:foreign_key
:join_table
:validate
4.4.2.1 :association_foreign_key
Pagal nutylėjimą „Rails“ priima, kad stulpelis jungiamojoje lentelėje, naudojamas laikyti užsienio rakto, rodančio į kitą modelį, yra to modelio pavadinimas su pridėtu priesaga _id
. Parinktis :association_foreign_key
leidžia nustatyti užsienio rakto pavadinimą tiesiogiai:
PATARIMAS: Parinktys :foreign_key
ir :association_foreign_key
yra naudingos, kai sukuriamas daug į daug paties sąsajos ryšys. Pavyzdžiui:
class User < ApplicationRecord
has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end
4.4.2.2 :autosave
Jei nustatote parinktį :autosave
į true
, „Rails“ išsaugos visus įkeltus asociacijos narius ir sunaikins narius, kurie pažymėti sunaikinimui, kai išsaugosite pagrindinį objektą. Nustatant :autosave
į false
, tai nėra tas pats kaip nepasirinkus :autosave
parinkties. Jei :autosave
parinktis nėra nurodyta, tada nauji susiję objektai bus išsaugoti, tačiau atnaujinti susiję objektai nebus išsaugoti.
4.4.2.3 :class_name
Jei kito modelio pavadinimas negali būti išvestas iš asociacijos pavadinimo, galite naudoti :class_name
parinktį, kad nurodytumėte modelio pavadinimą. Pavyzdžiui, jei daliui priklauso daug montavimų, bet faktinis modelio, kuriame yra montavimai, pavadinimas yra Gadget
, tai galėtumėte tai nustatyti taip:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, class_name: "Gadget"
end
4.4.2.4 :foreign_key
Pagal nutylėjimą „Rails“ priima, kad stulpelis jungiamojoje lentelėje, naudojamas laikyti užsienio rakto, rodančio į šį modelį, yra šio modelio pavadinimas su pridėta priesaga _id
. Parinktis :foreign_key
leidžia nustatyti užsienio rakto pavadinimą tiesiogiai:
class User < ApplicationRecord
has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end
4.4.2.5 :join_table
Jei numatytasis jungiamojo lenteles pavadinimas, pagrįstas leksikografiniu tvarka, nėra tai, ko norite, galite naudoti :join_table
parinktį, kad pakeistumėte numatytąjį pavadinimą.
4.4.2.6 :validate
Jei nustatote :validate
parinktį kaip false
, tada nauji susiję objektai nebus patikrinami, kai tik išsaugosite šį objektą. Pagal numatytuosius nustatymus tai yra true
: nauji susiję objektai bus patikrinami, kai šis objektas bus išsaugotas.
4.4.3 Scopes for has_and_belongs_to_many
Gali būti atvejų, kai norite tinkinti užklausą, kurią naudoja has_and_belongs_to_many
. Tokias tinkinimus galima pasiekti per rėmų bloką. Pavyzdžiui:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, -> { where active: true }
end
Rėmų bloke galite naudoti bet kurį iš standartinių užklausos metodų. Apie šiuos aptariami žemiau:
where
extending
group
includes
limit
offset
order
readonly
select
distinct
4.4.3.1 where
where
metodas leidžia nurodyti sąlygas, kurias turi atitikti susijęs objektas.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { where "factory = 'Seattle'" }
end
Sąlygas taip pat galite nustatyti naudodami hash'ą:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { where factory: 'Seattle' }
end
Jei naudojate hash'o stiliaus where
, tada įrašų kūrimas per šią asociaciją automatiškai bus apribotas naudojant hash'ą. Šiuo atveju, naudojant @parts.assemblies.create
arba @parts.assemblies.build
, bus sukurtos montavimo vienetų, kurių factory
stulpelis turi reikšmę "Seattle".
4.4.3.2 extending
extending
metodas nurodo vardintą modulį, kuris išplečia asociacijos proxy. Apie asociacijos išplėtimus išsamiau kalbama vėliau šiame vadove.
4.4.3.3 group
group
metodas nurodo atributo pavadinimą, pagal kurį rezultatų rinkinys bus sugrupuotas, naudojant GROUP BY
klauzulę užklausos SQL.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, -> { group "factory" }
end
4.4.3.4 includes
Galite naudoti includes
metodą, norėdami nurodyti antrinio lygio asociacijas, kurios turėtų būti įkeltos iš anksto, kai naudojama ši asociacija.
4.4.3.5 limit
limit
metodas leidžia apriboti visų objektų, kurie bus gauti per asociaciją, skaičių.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { order("created_at DESC").limit(50) }
end
4.4.3.6 offset
offset
metodas leidžia nurodyti pradžios nuokrypį, skirtą objektams gauti per asociaciją. Pavyzdžiui, jei nustatote offset(11)
, tai praleis pirmuosius 11 įrašų.
4.4.3.7 order
order
metodas nurodo tvarką, kuria bus gauti susiję objektai (naudojant sintaksę, kurią naudoja SQL ORDER BY
klauzulė).
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { order "assembly_name ASC" }
end
4.4.3.8 readonly
Jei naudojate readonly
metodą, tada susiję objektai bus tik skaitymui, kai jie bus gaunami per asociaciją.
4.4.3.9 select
select
metodas leidžia perrašyti SQL SELECT
klauzulę, kuri naudojama gauti duomenis apie susijusius objektus. Pagal numatytuosius nustatymus, „Rails“ gauna visus stulpelius.
4.4.3.10 distinct
Naudokite distinct
metodą, norėdami pašalinti dublikatus iš kolekcijos.
4.4.4 Kada objektai yra išsaugomi?
Kai priskiriate objektą has_and_belongs_to_many
asociacijai, tas objektas automatiškai išsaugomas (norint atnaujinti jungiamąją lentelę). Jei vienoje instrukcijoje priskiriate kelis objektus, jie visi išsaugomi.
Jei vienas iš šių išsaugojimų nepavyksta dėl validacijos klaidų, tada priskyrimo instrukcija grąžina false
, o pati priskyrimas yra atšaukiamas.
Jei pagrindinis objektas (tas, kuris deklaruoja has_and_belongs_to_many
asociaciją) nėra išsaugotas (tai yra, new_record?
grąžina true
), tada pridedant juos, vaikiniai objektai nebus išsaugomi. Visi nesaugomi asociacijos nariai automatiškai bus išsaugomi, kai bus išsaugotas pagrindinis objektas.
Jei norite priskirti objektą has_and_belongs_to_many
asociacijai, nesaugodami objekto, naudokite collection.build
metodą.
4.5 Asociacijos atgalinimo iškvietimai
Įprasti atgalinimo iškvietimai susijungia su aktyviųjų įrašų objektų gyvavimo ciklu, leisdami jums dirbti su šiais objektais įvairiais momentais. Pavyzdžiui, galite naudoti :before_save
atgalinimo iškvietimą, kad prieš išsaugant objektą kažkas įvyktų.
Asociacijos atgalinimo iškvietimai panašūs į įprastus atgalinimo iškvietimus, tačiau jie yra suaktyvinti kolekcijos gyvavimo ciklo įvykiais. Yra keturi galimi asociacijos atgalinimo iškvietimai:
before_add
after_add
before_remove
after_remove
Asociacijos atgalinimo iškvietimus apibrėžiate pridedant parinktis prie asociacijos deklaracijos. Pavyzdžiui:
class Author < ApplicationRecord
has_many :books, before_add: :check_credit_limit
def check_credit_limit(book)
# ...
end
end
„Rails“ perduoda pridedamą arba pašalinamą objektą atgalinio iškvietimo funkcijai. Jūs galite sudėti atgalinį iškvietimą vienam įvykiui, perduodant juos kaip masyvą:
class Author < ApplicationRecord
has_many :books,
before_add: [:check_credit_limit, :calculate_shipping_charges]
def check_credit_limit(book)
# ...
end
def calculate_shipping_charges(book)
# ...
end
end
Jei before_add
atgalinis iškvietimas išmeta :abort
, objektas nebus pridėtas prie kolekcijos. Panašiai, jei before_remove
atgalinis iškvietimas išmeta :abort
, objektas nebus pašalintas iš kolekcijos:
# knyga nebus pridėta, jei pasiektas limitas
def check_credit_limit(book)
throw(:abort) if limit_reached?
end
PASTABA: Šie atgaliniai iškvietimai yra iškviesti tik tada, kai susiję objektai yra pridedami arba pašalinami per asociacijos kolekciją:
# Iškviečia `before_add` atgalinį iškvietimą
author.books << book
author.books = [book, book2]
# Neiškviečia `before_add` atgalinio iškvietimo
book.update(author_id: 1)
4.6 Asociacijos plėtra
Jūs nesate apribotas tik tuo, ką „Rails“ automatiškai įtraukia į asociacijos tarpininko objektus. Taip pat galite plėsti šiuos objektus per anonimines modulius, pridedant naujų paieškos, kūrėjų ar kitų metodų. Pavyzdžiui:
class Author < ApplicationRecord
has_many :books do
def find_by_book_prefix(book_number)
find_by(category_id: book_number[0..2])
end
end
end
Jei turite plėtinį, kurį norite naudoti daugelyje asociacijų, galite naudoti pavadintą plėtinio modulį. Pavyzdžiui:
module FindRecentExtension
def find_recent
where("created_at > ?", 5.days.ago)
end
end
class Author < ApplicationRecord
has_many :books, -> { extending FindRecentExtension }
end
class Supplier < ApplicationRecord
has_many :deliveries, -> { extending FindRecentExtension }
end
Plėtiniai gali kreiptis į asociacijos tarpininko vidaus veiksmus naudodami šiuos tris proxy_association
priėjimo atributus:
proxy_association.owner
grąžina objektą, kuris yra dalis asociacijos.proxy_association.reflection
grąžina atspindį, kuris aprašo asociaciją.proxy_association.target
grąžina susijusį objektąbelongs_to
arbahas_one
, arba susijusių objektų kolekcijąhas_many
arbahas_and_belongs_to_many
.
4.7 Asociacijos ribojimas naudojant asociacijos savininką
Asociacijos savininkas gali būti perduotas kaip vienas argumentas į ribos bloką situacijose, kai jums reikia dar didesnės kontrolės per asociacijos ribą. Tačiau, kaip pastaba, asociacijos išankstinis įkėlimas nebebus įmanomas.
class Supplier < ApplicationRecord
has_one :account, ->(supplier) { where active: supplier.active? }
end
5 Vieno lentelės paveldėjimas (STI)
Kartais norite bendrinti laukus ir elgesį tarp skirtingų modelių. Tarkime, turime modelius „Car“, „Motorcycle“ ir „Bicycle“. Norėsime bendrinti „color“ ir „price“ laukus bei kai kuriuos metodus visiems šiems modeliams, tačiau turėti specifinį elgesį kiekvienam iš jų ir atskirus valdiklius.
Pirma, sugeneruokime pagrindinį „Vehicle“ modelį:
$ bin/rails generate model vehicle type:string color:string price:decimal{10.2}
Ar pastebėjote, kad pridedame „type“ lauką? Kadangi visi modeliai bus išsaugomi vienoje duomenų bazės lentelėje, „Rails“ šiame stulpelyje išsaugos modelio, kuris yra išsaugomas, pavadinimą. Mūsų pavyzdyje tai gali būti „Car“, „Motorcycle“ arba „Bicycle“. STI neveiks be „type“ lauko lentelėje.
Toliau sugeneruosime „Car“ modelį, kuris paveldės iš „Vehicle“. Tai galime padaryti naudodami --parent=PARENT
parinktį, kuri sugeneruos modelį, paveldintą iš nurodyto tėvo ir be atitinkamos migracijos (kadangi lentelė jau egzistuoja).
Pavyzdžiui, sugeneruoti „Car“ modelį:
$ bin/rails generate model car --parent=Vehicle
Sugeneruotas modelis atrodys taip:
class Car < Vehicle
end
Tai reiškia, kad visi „Vehicle“ pridėti elgesiai taip pat yra prieinami ir „Car“, kaip asociacijos, vieši metodai ir kt.
Sukūrus automobilį, jis bus išsaugotas „vehicles“ lentelėje su „Car“ kaip „type“ lauku:
Car.create(color: 'Red', price: 10000)
sukurs šį SQL:
INSERT INTO "vehicles" ("type", "color", "price") VALUES ('Car', 'Red', 10000)
Užklausiant automobilių įrašus, bus ieškoma tik automobilių:
Car.all
vykdys užklausą kaip:
SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."type" IN ('Car')
6 Paveldėtų tipų delegavimas
Vieno lentelės paveldėjimas (STI)
geriausiai veikia, kai yra mažai skirtumo tarp po-klasių ir jų atributų, bet apima visus atributus, kuriuos reikia sukurti vienoje lentelėje.
Šio metodo trūkumas yra tai, kad tai sukelia papildomą apkrovą šiai lentelėje. Kadangi ji netgi apima atributus, kurie yra specifiniai po-klasei ir nenaudojami nieko kito.
Šiame pavyzdyje yra du „Active Record“ modeliai, kurie paveldi iš tos pačios „Entry“ klasės, kurioje yra „subject“ atributas. ```ruby
Schema: entries[ id, type, subject, created_at, updated_at]
class Entry < ApplicationRecord end
class Comment < Entry end
class Message < Entry end ```
Deleguotosios rūšys išsprendžia šią problemą, naudojant delegated_type
.
Norint naudoti deleguotas rūšis, turime modeliuoti duomenis tam tikru būdu. Reikalavimai yra šie:
- Yra viršklas, kuri saugo bendras savybes tarp visų po-klasių savo lentelėje.
- Kiekviena po-klasė turi paveldėti iš virš-klasės ir turės atskirą lentelę bet kokioms papildomoms savitoms savybėms.
Tai pašalina poreikį apibrėžti atributus vienoje lentelėje, kurie nenorimai bendrinami tarp visų po-klasių.
Norėdami pritaikyti tai mūsų pavyzdyje aukščiau, turime atnaujinti savo modelius.
Pirma, sugeneruokime pagrindinį Entry
modelį, kuris veiks kaip mūsų virš-klasė:
$ bin/rails generate model entry entryable_type:string entryable_id:integer
Tada sugeneruosime naujus Message
ir Comment
modelius delegavimui:
$ bin/rails generate model message subject:string body:string
$ bin/rails generate model comment content:string
Paleidus generatorius, turėtume gauti modelius, kurie atrodo taip:
# Schema: entries[ id, entryable_type, entryable_id, created_at, updated_at ]
class Entry < ApplicationRecord
end
# Schema: messages[ id, subject, body, created_at, updated_at ]
class Message < ApplicationRecord
end
# Schema: comments[ id, content, created_at, updated_at ]
class Comment < ApplicationRecord
end
6.1 Deklaruokite delegated_type
Pirma, virš-klasėje Entry
deklaruokite delegated_type
.
class Entry < ApplicationRecord
delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
end
Parametras entryable
nurodo lauką, kurį naudosime delegavimui, ir įtraukia rūšis Message
ir Comment
kaip delegato klases.
Klasė Entry
turi laukus entryable_type
ir entryable_id
. Tai yra laukas su _type
, _id
priesaga, pridėta prie pavadinimo entryable
delegated_type
apibrėžime.
entryable_type
saugo delegato po-klasės pavadinimą, o entryable_id
saugo delegato po-klasės įrašo id.
Toliau turime apibrėžti modulį, kad įgyvendintume tuos deleguotus tipus, deklaruodami as: :entryable
parametrą has_one
asociacijai.
module Entryable
extend ActiveSupport::Concern
included do
has_one :entry, as: :entryable, touch: true
end
end
Tada įtraukite sukurtą modulį į savo po-klasę.
class Message < ApplicationRecord
include Entryable
end
class Comment < ApplicationRecord
include Entryable
end
Baigus šią apibrėžimą, mūsų Entry
delegatorius dabar teikia šiuos metodus:
Metodas | Grąžinimas |
---|---|
Entry#entryable_class |
Message arba Comment |
Entry#entryable_name |
"message" arba "comment" |
Entry.messages |
Entry.where(entryable_type: "Message") |
Entry#message? |
Grąžina true, kai entryable_type == "Message" |
Entry#message |
Grąžina message įrašą, kai entryable_type == "Message" , kitu atveju nil |
Entry#message_id |
Grąžina entryable_id , kai entryable_type == "Message" , kitu atveju nil |
Entry.comments |
Entry.where(entryable_type: "Comment") |
Entry#comment? |
Grąžina true, kai entryable_type == "Comment" |
Entry#comment |
Grąžina comment įrašą, kai entryable_type == "Comment" , kitu atveju nil |
Entry#comment_id |
Grąžina entryable_id , kai entryable_type == "Comment" , kitu atveju nil |
6.2 Objekto kūrimas
Kuriant naują Entry
objektą, galime tuo pačiu metu nurodyti entryable
po-klasę.
Entry.create! entryable: Message.new(subject: "hello!")
6.3 Papildomo delegavimo pridėjimas
Galime išplėsti mūsų Entry
delegatorių ir toliau pagerinti, apibrėždami delegates
ir naudodami polimorfizmą po-klasėms.
Pavyzdžiui, deleguoti title
metodą iš Entry
į jo po-klases:
class Entry < ApplicationRecord
delegated_type :entryable, types: %w[ Message Comment ]
delegates :title, to: :entryable
end
class Message < ApplicationRecord
include Entryable
def title
subject
end
end
class Comment < ApplicationRecord
include Entryable
def title
content.truncate(20)
end
end
Atsiliepimai
Jūs esate skatinami padėti pagerinti šio vadovo kokybę.
Prašome prisidėti, jei pastebite rašybos klaidų ar faktinių klaidų. Norėdami pradėti, galite perskaityti mūsų dokumentacijos prisidėjimo skyrių.
Taip pat gali būti nepilnos informacijos arba informacijos, kuri nėra atnaujinta. Prašome pridėti bet kokią trūkstamą dokumentaciją pagrindiniam. Patikrinkite Edge vadovus pirmiausia, ar problemas jau išspręsta arba ne pagrindinėje šakoje. Patikrinkite Ruby on Rails vadovų gaires dėl stiliaus ir konvencijų.
Jei dėl kokios nors priežasties pastebite kažką, ką reikia ištaisyti, bet negalite patys tai pataisyti, prašome pranešti apie problemą.
Ir galiausiai, bet ne mažiau svarbu, bet koks diskusijos dėl Ruby on Rails dokumentacijos yra labai laukiamos oficialiame Ruby on Rails forume.