Norėdami naudoti PostgreSQL adapterį, jums reikia bent 9.3 versijos įdiegti. Senesnės versijos nepalaikomos.
Norėdami pradėti naudoti PostgreSQL, peržiūrėkite Rails konfigūracijos vadovą. Jame aprašoma, kaip tinkamai sukonfigūruoti aktyvų įrašą PostgreSQL.
1 Duomenų tipai
PostgreSQL siūlo keletą specifinių duomenų tipų. Štai sąrašas tipų, kurie yra palaikomi PostgreSQL adapteriu.
1.1 Bytea
# db/migrate/20140207133952_create_documents.rb
create_table :documents do |t|
t.binary 'payload'
end
# app/models/document.rb
class Document < ApplicationRecord
end
# Naudojimas
data = File.read(Rails.root + "tmp/output.pdf")
Document.create payload: data
1.2 Masyvas
# db/migrate/20140207133952_create_books.rb
create_table :books do |t|
t.string 'title'
t.string 'tags', array: true
t.integer 'ratings', array: true
end
add_index :books, :tags, using: 'gin'
add_index :books, :ratings, using: 'gin'
# app/models/book.rb
class Book < ApplicationRecord
end
# Naudojimas
Book.create title: "Brave New World",
tags: ["fantazija", "fikcija"],
ratings: [4, 5]
## Knygos su vienu žyma
Book.where("'fantazija' = ANY (tags)")
## Knygos su keliais žymomis
Book.where("tags @> ARRAY[?]::varchar[]", ["fantazija", "fikcija"])
## Knygos su 3 ar daugiau įvertinimų
Book.where("array_length(ratings, 1) >= 3")
1.3 Hstore
PASTABA: Norėdami naudoti hstore, turite įjungti hstore
plėtinį.
# db/migrate/20131009135255_create_profiles.rb
class CreateProfiles < ActiveRecord::Migration[7.0]
enable_extension 'hstore' unless extension_enabled?('hstore')
create_table :profiles do |t|
t.hstore 'settings'
end
end
# app/models/profile.rb
class Profile < ApplicationRecord
end
irb> Profile.create(settings: { "spalva" => "mėlyna", "skiriamoji geba" => "800x600" })
irb> profile = Profile.first
irb> profile.settings
=> {"spalva"=>"mėlyna", "skiriamoji geba"=>"800x600"}
irb> profile.settings = {"spalva" => "geltona", "skiriamoji geba" => "1280x1024"}
irb> profile.save!
irb> Profile.where("settings->'spalva' = ?", "geltona")
=> #<ActiveRecord::Relation [#<Profile id: 1, settings: {"spalva"=>"geltona", "skiriamoji geba"=>"1280x1024"}>]>
1.4 JSON ir JSONB
# db/migrate/20131220144913_create_events.rb
# ... for json duomenų tipą:
create_table :events do |t|
t.json 'payload'
end
# ... arba jsonb duomenų tipą:
create_table :events do |t|
t.jsonb 'payload'
end
# app/models/event.rb
class Event < ApplicationRecord
end
irb> Event.create(payload: { rūšis: "vartotojo_pervadintas", pakeitimas: ["jack", "john"]})
irb> event = Event.first
irb> event.payload
=> {"rūšis"=>"vartotojo_pervadintas", "pakeitimas"=>["jack", "john"]}
## Užklausa pagal JSON dokumentą
# Operatorius -> grąžina pradinį JSON tipą (kuris gali būti objektas), o ->> grąžina tekstą
irb> Event.where("payload->>'rūšis' = ?", "vartotojo_pervadintas")
1.5 Intervalo tipai
Šis tipas yra susietas su Ruby Interval
objektais.
# db/migrate/20130923065404_create_events.rb
create_table :events do |t|
t.daterange 'trukmė'
end
# app/models/event.rb
class Event < ApplicationRecord
end
irb> Event.create(trukmė: Date.new(2014, 2, 11)..Date.new(2014, 2, 12))
irb> event = Event.first
irb> event.trukmė
=> Tue, 11 Feb 2014...Thu, 13 Feb 2014
## Visi įvykiai tam tikrą dieną
irb> Event.where("trukmė @> ?::date", Date.new(2014, 2, 12))
## Darbas su intervalo ribomis
irb> event = Event.select("lower(trukmė) AS pradžia").select("upper(trukmė) AS pabaiga").first
irb> event.pradžia
=> Tue, 11 Feb 2014
irb> event.pabaiga
=> Thu, 13 Feb 2014
1.6 Sudėtiniai tipai
Šiuo metu nėra specialaus palaikymo sudėtiniams tipams. Jie yra susieti su įprastais teksto stulpeliais:
CREATE TYPE pilnas_adresas AS
(
miestas VARCHAR(90),
gatvė VARCHAR(90)
);
# db/migrate/20140207133952_create_contacts.rb
execute <<-SQL
CREATE TYPE full_address AS
(
city VARCHAR(90),
street VARCHAR(90)
);
SQL
create_table :contacts do |t|
t.column :address, :full_address
end
# app/models/contact.rb
class Contact < ApplicationRecord
end
irb> Contact.create address: "(Paryžius,Champs-Élysées)"
irb> contact = Contact.first
irb> contact.address
=> "(Paryžius,Champs-Élysées)"
irb> contact.address = "(Paryžius,Rue Basse)"
irb> contact.save!
1.7 Enumeruoti tipai
Tipą galima atvaizduoti kaip įprastą teksto stulpelį arba kaip ActiveRecord::Enum
.
# db/migrate/20131220144913_create_articles.rb
def change
create_enum :article_status, ["juodraštis", "paskelbtas", "archyvuotas"]
create_table :articles do |t|
t.enum :status, enum_type: :article_status, default: "juodraštis", null: false
end
end
Taip pat galite sukurti enum tipo ir pridėti enum stulpelį prie esamos lentelės:
# db/migrate/20230113024409_add_status_to_articles.rb
def change
create_enum :article_status, ["juodraštis", "paskelbtas", "archyvuotas"]
add_column :articles, :status, :enum, enum_type: :article_status, default: "juodraštis", null: false
end
Abu migracijos yra atstatomos, tačiau jei reikia, galite apibrėžti atskirus #up
ir #down
metodus. Įsitikinkite, kad pašalinote visus stulpelius arba lentelės, kurie priklauso nuo enum tipo, prieš jį ištrindami:
def down
drop_table :articles
# ARBA: remove_column :articles, :status
drop_enum :article_status
end
Modelyje deklaruojant enum atributą, pridedami pagalbiniai metodai ir užkertamas kelias netinkamų reikšmių priskyrimui klasės egzemplioriams:
# app/models/article.rb
class Article < ApplicationRecord
enum status: {
juodraštis: "juodraštis", paskelbtas: "paskelbtas", archyvuotas: "archyvuotas"
}, _prefix: true
end
irb> article = Article.create
irb> article.status
=> "juodraštis" # numatytoji būsena iš PostgreSQL, kaip apibrėžta migracijoje aukščiau
irb> article.status_paskelbtas!
irb> article.status
=> "paskelbtas"
irb> article.status_archyvuotas?
=> false
irb> article.status = "ištrintas"
ArgumentError: 'ištrintas' nėra galiojanti būsena
Norėdami pervardyti enum, galite naudoti rename_enum
kartu su atnaujinimu bet kokio modelio naudojimo:
# db/migrate/20150718144917_rename_article_status.rb
def change
rename_enum :article_status, to: :article_state
end
Norėdami pridėti naują reikšmę, galite naudoti add_enum_value
:
# db/migrate/20150720144913_add_new_state_to_articles.rb
def up
add_enum_value :article_state, "archyvuotas", # bus pabaigoje po paskelbto
add_enum_value :article_state, "peržiūrimas", before: "paskelbtas"
add_enum_value :article_state, "patvirtintas", after: "peržiūrimas"
end
PASTABA: Enum reikšmių negalima ištrinti, tai taip pat reiškia, kad add_enum_value
yra neatsikratoma. Galite perskaityti kodėl čia.
Norėdami pervardyti reikšmę, galite naudoti rename_enum_value
:
# db/migrate/20150722144915_rename_article_state.rb
def change
rename_enum_value :article_state, from: "archyvuotas", to: "ištrintas"
end
Patarimas: norėdami pamatyti visų jūsų turimų enum reikšmių sąrašą, galite iškviesti šią užklausą bin/rails db
arba psql
konsolėje:
SELECT n.nspname AS enum_schema,
t.typname AS enum_name,
e.enumlabel AS enum_value
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
1.8 UUID
PASTABA: Jei naudojate PostgreSQL versiją ankstesnę nei 13.0, gali prireikti įjungti specialius plėtinius, kad galėtumėte naudoti UUID. Įjunkite pgcrypto
plėtinį (PostgreSQL >= 9.4) arba uuid-ossp
plėtinį (dar ankstesnėms versijoms).
# db/migrate/20131220144913_create_revisions.rb
create_table :revisions do |t|
t.uuid :identifier
end
# app/models/revision.rb
class Revision < ApplicationRecord
end
irb> Revision.create identifier: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"
irb> revision = Revision.first
irb> revision.identifier
=> "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
Migracijose galite naudoti uuid
tipą, kad apibrėžtumėte nuorodas:
# db/migrate/20150418012400_create_blog.rb
enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
create_table :posts, id: :uuid
create_table :comments, id: :uuid do |t|
# t.belongs_to :post, type: :uuid
t.references :post, type: :uuid
end
# app/models/post.rb
class Post < ApplicationRecord
has_many :comments
end
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
end
Daugiau informacijos apie UUID naudojimą kaip pirminį raktą rasite šiame skyriuje.
1.9 Bitų eilutės tipai
# db/migrate/20131220144913_create_users.rb
create_table :users, force: true do |t|
t.column :settings, "bit(8)"
end
# app/models/user.rb
class User < ApplicationRecord
end
irb> User.create settings: "01010011"
irb> user = User.first
irb> user.settings
=> "01010011"
irb> user.settings = "0xAF"
irb> user.settings
=> "10101111"
irb> user.save!
1.10 Tinklo adreso tipai
Tipai inet
ir cidr
yra susiejami su Ruby
IPAddr
objektais. macaddr
tipas yra susiejamas su paprastu tekstu.
# db/migrate/20140508144913_create_devices.rb
create_table(:devices, force: true) do |t|
t.inet 'ip'
t.cidr 'network'
t.macaddr 'address'
end
# app/models/device.rb
class Device < ApplicationRecord
end
irb> macbook = Device.create(ip: "192.168.1.12", network: "192.168.2.0/24", address: "32:01:16:6d:05:ef")
irb> macbook.ip
=> #<IPAddr: IPv4:192.168.1.12/255.255.255.255>
irb> macbook.network
=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
irb> macbook.address
=> "32:01:16:6d:05:ef"
1.11 Geometriniai tipai
Visi geometriniai tipai, išskyrus points
, yra susiejami su paprastu tekstu.
Taškas yra konvertuojamas į masyvą, kuriame yra x
ir y
koordinatės.
1.12 Intervalas
Šis tipas yra susiejamas su ActiveSupport::Duration
objektais.
# db/migrate/20200120000000_create_events.rb
create_table :events do |t|
t.interval 'duration'
end
# app/models/event.rb
class Event < ApplicationRecord
end
irb> Event.create(duration: 2.days)
irb> event = Event.first
irb> event.duration
=> 2 days
2 UUID pagrindiniai raktai
PASTABA: Norint generuoti atsitiktinius UUID, reikia įjungti pgcrypto
(tik PostgreSQL >= 9.4) arba uuid-ossp
plėtinį.
# db/migrate/20131220144913_create_devices.rb
enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
create_table :devices, id: :uuid do |t|
t.string :kind
end
# app/models/device.rb
class Device < ApplicationRecord
end
irb> device = Device.create
irb> device.id
=> "814865cd-5a1d-4771-9306-4268f188fe9e"
PASTABA: Jei :default
parinktis nebuvo perduota create_table
metode, tuomet gen_random_uuid()
(iš pgcrypto
) yra numatytasis variantas.
Norint naudoti Rails modelio generatorių lentelėje, kurioje UUID yra pagrindinis raktas, reikia perduoti --primary-key-type=uuid
modelio generatoriui.
Pavyzdžiui:
$ rails generate model Device --primary-key-type=uuid kind:string
Kuriant modelį su užsienio raktu, kuris rodo į šį UUID, uuid
lauką reikia traktuoti kaip natyvų lauko tipą, pavyzdžiui:
$ rails generate model Case device_id:uuid
3 Indeksavimas
PostgreSQL turi įvairių indekso parinkčių. Šios parinktys yra palaikomos PostgreSQL adapterio, be bendrų indekso parinkčių
3.1 Include
Kuriant naują indeksą, ne-raktiniai stulpeliai gali būti įtraukti naudojant :include
parinktį.
Šie raktai nenaudojami indekso skenavimui ieškant, bet gali būti nuskaityti indekso
tik skenavimo metu, neapsilankant susijusioje lentelėje.
# db/migrate/20131220144913_add_index_users_on_email_include_id.rb
add_index :users, :email, include: :id
Palaikomi keli stulpeliai:
# db/migrate/20131220144913_add_index_users_on_email_include_id_and_created_at.rb
add_index :users, :email, include: [:id, :created_at]
4 Generuojami stulpeliai
PASTABA: Generuojami stulpeliai palaikomi nuo PostgreSQL 12.0 versijos.
# db/migrate/20131220144913_create_users.rb
create_table :users do |t|
t.string :name
t.virtual :name_upcased, type: :string, as: 'upper(name)', stored: true
end
# app/models/user.rb
class User < ApplicationRecord
end
# Naudojimas
user = User.create(name: 'John')
User.last.name_upcased # => "JOHN"
5 Atidėtiniai užsienio raktai
Pagal numatytuosius nustatymus, PostgreSQL tikrina lentelės apribojimus iš karto po kiekvieno užklausos. Ji sąmoningai neleidžia kurti įrašų, kuriuose nėra nuorodos į susijusią lentelę. Tačiau galima paleisti šią vientisumo patikrą vėliau, kai sandoris yra įvykdomas, pridedant DEFERRABLE
prie užsienio rakto apibrėžimo. Norint atidėti visus patikrinimus pagal numatytuosius nustatymus, galima nustatyti DEFERRABLE INITIALLY DEFERRED
. Rails pateikia šią PostgreSQL funkciją, pridedant :deferrable
raktą prie foreign_key
parinkčių add_reference
ir add_foreign_key
metodų.
Vienas pavyzdys yra sukurti ciklinius priklausomybes sandorio metu, net jei sukūrėte užsienio raktus:
add_reference :person, :alias, foreign_key: { deferrable: :deferred }
add_reference :alias, :person, foreign_key: { deferrable: :deferred }
Jei nuoroda buvo sukurta su foreign_key: true
parinktimi, šis sandoris nepavyktų vykdant pirmąjį INSERT
teiginį. Tačiau jis nepavyksta, kai nustatoma deferrable: :deferred
parinktis.
ruby
ActiveRecord::Base.connection.transaction do
person = Person.create(id: SecureRandom.uuid, alias_id: SecureRandom.uuid, name: "John Doe")
Alias.create(id: person.alias_id, person_id: person.id, name: "jaydee")
end
Kai :deferrable
parinktis nustatoma kaip :immediate
, leidžiama užsienio raktams išlaikyti numatytąjį elgesį ir patikrinti apribojimus iš karto, tačiau leidžiama rankiniu būdu atidėti patikrinimus naudojant SET CONSTRAINTS ALL DEFERRED
transakcijoje. Tai sukels užsienio raktų patikrinimą, kai transakcija bus įvykdyta:
ActiveRecord::Base.transaction do
ActiveRecord::Base.connection.execute("SET CONSTRAINTS ALL DEFERRED")
person = Person.create(alias_id: SecureRandom.uuid, name: "John Doe")
Alias.create(id: person.alias_id, person_id: person.id, name: "jaydee")
end
Pagal numatytuosius nustatymus :deferrable
yra false
ir apribojimas visada tikrinamas iš karto.
6 Unikalus apribojimas
# db/migrate/20230422225213_create_items.rb
create_table :items do |t|
t.integer :position, null: false
t.unique_key [:position], deferrable: :immediate
end
Jei norite pakeisti esamą unikalų indeksą į atidėtinį, galite naudoti :using_index
, kad sukurtumėte atidėtinus unikalius apribojimus.
add_unique_key :items, deferrable: :deferred, using_index: "index_items_on_position"
Kaip ir užsienio raktai, unikalūs apribojimai gali būti atidėti, nustatant :deferrable
kaip :immediate
arba :deferred
. Pagal numatytuosius nustatymus :deferrable
yra false
ir apribojimas visada tikrinamas iš karto.
7 Išskirties apribojimai
# db/migrate/20131220144913_create_products.rb
create_table :products do |t|
t.integer :price, null: false
t.daterange :availability_range, null: false
t.exclusion_constraint "price WITH =, availability_range WITH &&", using: :gist, name: "price_check"
end
Kaip ir užsienio raktai, išskirties apribojimai gali būti atidėti, nustatant :deferrable
kaip :immediate
arba :deferred
. Pagal numatytuosius nustatymus :deferrable
yra false
ir apribojimas visada tikrinamas iš karto.
8 Pilno teksto paieška
# db/migrate/20131220144913_create_documents.rb
create_table :documents do |t|
t.string :title
t.string :body
end
add_index :documents, "to_tsvector('english', title || ' ' || body)", using: :gin, name: 'documents_idx'
# app/models/document.rb
class Document < ApplicationRecord
end
# Usage
Document.create(title: "Cats and Dogs", body: "are nice!")
## all documents matching 'cat & dog'
Document.where("to_tsvector('english', title || ' ' || body) @@ to_tsquery(?)",
"cat & dog")
Galite pasirinktinai saugoti vektorių kaip automatiškai sugeneruotą stulpelį (nuo PostgreSQL 12.0):
# db/migrate/20131220144913_create_documents.rb
create_table :documents do |t|
t.string :title
t.string :body
t.virtual :textsearchable_index_col,
type: :tsvector, as: "to_tsvector('english', title || ' ' || body)", stored: true
end
add_index :documents, :textsearchable_index_col, using: :gin, name: 'documents_idx'
# Usage
Document.create(title: "Cats and Dogs", body: "are nice!")
## all documents matching 'cat & dog'
Document.where("textsearchable_index_col @@ to_tsquery(?)", "cat & dog")
9 Duomenų bazės rodiniai
Įsivaizduokite, kad turite dirbti su sena duomenų baze, kurioje yra ši lentelė:
rails_pg_guide=# \d "TBL_ART"
Table "public.TBL_ART"
Column | Type | Modifiers
------------+-----------------------------+------------------------------------------------------------
INT_ID | integer | not null default nextval('"TBL_ART_INT_ID_seq"'::regclass)
STR_TITLE | character varying |
STR_STAT | character varying | default 'draft'::character varying
DT_PUBL_AT | timestamp without time zone |
BL_ARCH | boolean | default false
Indexes:
"TBL_ART_pkey" PRIMARY KEY, btree ("INT_ID")
Ši lentelė visiškai neatitinka „Rails“ konvencijų. Nes paprasti „PostgreSQL“ rodiniai pagal numatytuosius nustatymus gali būti atnaujinami, mes jį apgaubėme taip:
# db/migrate/20131220144913_create_articles_view.rb
execute <<-SQL
CREATE VIEW articles AS
SELECT "INT_ID" AS id,
"STR_TITLE" AS title,
"STR_STAT" AS status,
"DT_PUBL_AT" AS published_at,
"BL_ARCH" AS archived
FROM "TBL_ART"
WHERE "BL_ARCH" = 'f'
SQL
# app/models/article.rb
class Article < ApplicationRecord
self.primary_key = "id"
def archive!
update_attribute :archived, true
end
end
irb> first = Article.create! title: "Winter is coming", status: "published", published_at: 1.year.ago
irb> second = Article.create! title: "Brace yourself", status: "draft", published_at: 1.month.ago
irb> Article.count
=> 2
irb> first.archive!
irb> Article.count
=> 1
PASTABA: Ši programa domina tik nearchyvuotus Articles
. Rodinys taip pat
leidžia nustatyti sąlygas, todėl galime tiesiogiai pašalinti archyvuotus Articles
.
10 Struktūros atvaizdavimas
Jei jūsų config.active_record.schema_format
yra :sql
, „Rails“ iškvies pg_dump
, kad sukurtų
struktūros atvaizdą.
Galite naudoti ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags
konfigūruoti pg_dump
.
Pavyzdžiui, norint neįtraukti komentarų į struktūros iškrovimą, pridėkite tai prie inicializavimo:
ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--no-comments']
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.