Apgūstiet SQLAlchemy hibrīdās īpašības, lai veidotu aprēķinātus atribūtus izteiksmīgākiem un uzturamākiem datu modeļiem. Mācieties ar praktiskiem piemēriem.
Python SQLAlchemy hibrīdās īpašības: aprēķinātie atribūti jaudīgai datu modelēšanai
SQLAlchemy, jaudīgs un elastīgs Python SQL rīku komplekts un objektu relāciju kartētājs (ORM), piedāvā bagātīgu funkciju kopumu darbam ar datubāzēm. Starp tām hibrīdās īpašības (Hybrid Properties) izceļas kā īpaši noderīgs rīks aprēķināto atribūtu veidošanai jūsu datu modeļos. Šis raksts sniedz visaptverošu ceļvedi SQLAlchemy hibrīdo īpašību izpratnei un izmantošanai, ļaujot jums veidot izteiksmīgākas, vieglāk uzturamas un efektīvākas lietojumprogrammas.
Kas ir SQLAlchemy hibrīdās īpašības?
Hibrīdā īpašība, kā norāda nosaukums, ir īpašs īpašības veids SQLAlchemy, kas var uzvesties atšķirīgi atkarībā no konteksta, kurā tai piekļūst. Tā ļauj definēt atribūtu, kuram var piekļūt tieši jūsu klases instancē (kā parastai īpašībai) vai izmantot SQL izteiksmēs (kā kolonnai). Tas tiek panākts, definējot atsevišķas funkcijas gan instances līmeņa, gan klases līmeņa piekļuvei.
Būtībā hibrīdās īpašības nodrošina veidu, kā definēt aprēķinātus atribūtus, kas tiek atvasināti no citiem jūsu modeļa atribūtiem. Šos aprēķinātos atribūtus var izmantot vaicājumos, un tiem var arī piekļūt tieši jūsu modeļa instancēs, nodrošinot konsekventu un intuitīvu saskarni.
Kāpēc izmantot hibrīdās īpašības?
Hibrīdo īpašību izmantošana sniedz vairākas priekšrocības:
- Izteiksmīgums: Tās ļauj izteikt sarežģītas attiecības un aprēķinus tieši jūsu modelī, padarot kodu lasāmāku un vieglāk saprotamu.
- Uzturamība: Iekapsulējot sarežģītu loģiku hibrīdajās īpašībās, jūs samazināt koda dublēšanos un uzlabojat lietojumprogrammas uzturamību.
- Efektivitāte: Hibrīdās īpašības ļauj veikt aprēķinus tieši datubāzē, samazinot datu apjomu, kas jāpārsūta starp lietojumprogrammu un datubāzes serveri.
- Konsekvence: Tās nodrošina konsekventu saskarni aprēķināto atribūtu piekļuvei neatkarīgi no tā, vai strādājat ar modeļa instancēm vai rakstāt SQL vaicājumus.
Pamata piemērs: Pilns vārds
Sāksim ar vienkāršu piemēru: personas pilnā vārda aprēķināšana no vārda un uzvārda.
Modeļa definēšana
Vispirms definēsim vienkāršu `Person` modeli ar `first_name` un `last_name` kolonnām.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.hybrid import hybrid_property
Base = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
def __repr__(self):
return f""
engine = create_engine('sqlite:///:memory:') # Atmiņā esoša datubāze piemēram
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
Hibrīdās īpašības izveide
Tagad pievienosim `full_name` hibrīdo īpašību, kas savieno vārdu un uzvārdu.
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
@hybrid_property
def full_name(self):
return f"{self.first_name} {self.last_name}"
def __repr__(self):
return f""
Šajā piemērā `@hybrid_property` dekorators pārvērš `full_name` metodi par hibrīdo īpašību. Kad jūs piekļūstat `person.full_name`, tiks izpildīts kods šajā metodē.
Piekļuve hibrīdajai īpašībai
Izveidosim dažus datus un apskatīsim, kā piekļūt `full_name` īpašībai.
person1 = Person(first_name='Alice', last_name='Smith')
person2 = Person(first_name='Bob', last_name='Johnson')
session.add_all([person1, person2])
session.commit()
print(person1.full_name) # Izvade: Alice Smith
print(person2.full_name) # Izvade: Bob Johnson
Hibrīdās īpašības izmantošana vaicājumos
Hibrīdo īpašību patiesais spēks parādās, kad tās izmanto vaicājumos. Mēs varam filtrēt pēc `full_name`, it kā tā būtu parasta kolonna.
people_with_smith = session.query(Person).filter(Person.full_name == 'Alice Smith').all()
print(people_with_smith) # Izvade: []
Tomēr iepriekš minētais piemērs darbosies tikai vienkāršām vienādības pārbaudēm. Sarežģītākām operācijām vaicājumos (piemēram, `LIKE`) mums ir jādefinē izteiksmes funkcija.
Izteiksmes funkciju definēšana
Lai izmantotu hibrīdās īpašības sarežģītākās SQL izteiksmēs, jums ir jādefinē izteiksmes funkcija. Šī funkcija norāda SQLAlchemy, kā pārtulkot hibrīdo īpašību SQL izteiksmē.
Pārveidosim iepriekšējo piemēru, lai atbalstītu `LIKE` vaicājumus `full_name` īpašībai.
from sqlalchemy import func
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
@hybrid_property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@full_name.expression
def full_name(cls):
return func.concat(cls.first_name, ' ', cls.last_name)
def __repr__(self):
return f""
Šeit mēs pievienojām `@full_name.expression` dekoratoru. Tas definē funkciju, kas kā argumentu saņem klasi (`cls`) un atgriež SQL izteiksmi, kas savieno vārdu un uzvārdu, izmantojot `func.concat` funkciju. `func.concat` ir SQLAlchemy funkcija, kas attēlo datubāzes savienošanas funkciju (piemēram, `||` SQLite, `CONCAT` MySQL un PostgreSQL).
Tagad mēs varam izmantot `LIKE` vaicājumus:
people_with_smith = session.query(Person).filter(Person.full_name.like('%Smith%')).all()
print(people_with_smith) # Izvade: []
Vērtību iestatīšana: Setteris
Hibrīdajām īpašībām var būt arī setteri, kas ļauj atjaunināt pamatā esošos atribūtus, pamatojoties uz jaunu vērtību. To dara, izmantojot `@full_name.setter` dekoratoru.
Pievienosim mūsu `full_name` īpašībai setteri, kas sadala pilno vārdu vārdā un uzvārdā.
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
@hybrid_property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@full_name.expression
def full_name(cls):
return func.concat(cls.first_name, ' ', cls.last_name)
@full_name.setter
def full_name(self, full_name):
parts = full_name.split()
self.first_name = parts[0]
self.last_name = ' '.join(parts[1:]) if len(parts) > 1 else ''
def __repr__(self):
return f""
Tagad jūs varat iestatīt `full_name` īpašību, un tā atjauninās `first_name` un `last_name` atribūtus.
person = Person(first_name='Alice', last_name='Smith')
session.add(person)
session.commit()
person.full_name = 'Charlie Brown'
print(person.first_name) # Izvade: Charlie
print(person.last_name) # Izvade: Brown
session.commit()
Deleteri
Līdzīgi kā setteri, jūs varat definēt arī deleteri hibrīdajai īpašībai, izmantojot `@full_name.deleter` dekoratoru. Tas ļauj definēt, kas notiek, kad mēģināt izmantot `del person.full_name`.
Mūsu piemērā, padarīsim tā, lai, dzēšot pilno vārdu, tiktu notīrīts gan vārds, gan uzvārds.
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
@hybrid_property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@full_name.expression
def full_name(cls):
return func.concat(cls.first_name, ' ', cls.last_name)
@full_name.setter
def full_name(self, full_name):
parts = full_name.split()
self.first_name = parts[0]
self.last_name = ' '.join(parts[1:]) if len(parts) > 1 else ''
@full_name.deleter
def full_name(self):
self.first_name = None
self.last_name = None
def __repr__(self):
return f""
person = Person(first_name='Charlie', last_name='Brown')
session.add(person)
session.commit()
del person.full_name
print(person.first_name) # Izvade: None
print(person.last_name) # Izvade: None
session.commit()
Paplašināts piemērs: Vecuma aprēķināšana no dzimšanas datuma
Apskatīsim sarežģītāku piemēru: personas vecuma aprēķināšana no dzimšanas datuma. Tas parāda hibrīdo īpašību spēku, strādājot ar datumiem un veicot aprēķinus.
Dzimšanas datuma kolonnas pievienošana
Vispirms pievienosim `date_of_birth` kolonnu mūsu `Person` modelim.
from sqlalchemy import Date
import datetime
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
date_of_birth = Column(Date)
# ... (iepriekšējais kods)
Vecuma aprēķināšana ar hibrīdo īpašību
Tagad izveidosim `age` hibrīdo īpašību. Šī īpašība aprēķina vecumu, pamatojoties uz `date_of_birth` kolonnu. Mums būs jāapstrādā gadījums, kad `date_of_birth` ir `None`.
from sqlalchemy import Date
import datetime
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
date_of_birth = Column(Date)
@hybrid_property
def age(self):
if self.date_of_birth:
today = datetime.date.today()
age = today.year - self.date_of_birth.year - ((today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day))
return age
return None # Vai cita noklusējuma vērtība
@age.expression
def age(cls):
today = datetime.date.today()
return func.cast(func.strftime('%Y', 'now') - func.strftime('%Y', cls.date_of_birth) - (func.strftime('%m-%d', 'now') < func.strftime('%m-%d', cls.date_of_birth)), Integer)
# ... (iepriekšējais kods)
Svarīgi apsvērumi:
- Datubāzei specifiskas datuma funkcijas: Izteiksmes funkcija izmanto `func.strftime` datuma aprēķiniem. Šī funkcija ir specifiska SQLite. Citām datubāzēm (piemēram, PostgreSQL vai MySQL) jums būs jāizmanto atbilstošās datubāzei specifiskās datuma funkcijas (piemēram, `EXTRACT` PostgreSQL, `YEAR` un `MAKEDATE` MySQL).
- Tipu pārveidošana: Mēs izmantojam `func.cast`, lai pārveidotu datuma aprēķina rezultātu par veselu skaitli. Tas nodrošina, ka `age` īpašība atgriež vesela skaitļa vērtību.
- Laika joslas: Strādājot ar datumiem, esiet uzmanīgi ar laika joslām. Pārliecinieties, ka jūsu datumi tiek glabāti un salīdzināti konsekventā laika joslā.
- `None` vērtību apstrāde: Īpašībai jāapstrādā gadījumi, kad `date_of_birth` ir `None`, lai novērstu kļūdas.
Vecuma īpašības izmantošana
person1 = Person(first_name='Alice', last_name='Smith', date_of_birth=datetime.date(1990, 1, 1))
person2 = Person(first_name='Bob', last_name='Johnson', date_of_birth=datetime.date(1985, 5, 10))
session.add_all([person1, person2])
session.commit()
print(person1.age) # Izvade: (Balstīts uz pašreizējo datumu un dzimšanas datumu)
print(person2.age) # Izvade: (Balstīts uz pašreizējo datumu un dzimšanas datumu)
people_over_30 = session.query(Person).filter(Person.age > 30).all()
print(people_over_30) # Izvade: (Personas, kas vecākas par 30 gadiem, pamatojoties uz pašreizējo datumu)
Sarežģītāki piemēri un lietošanas gadījumi
Pasūtījumu kopsummas aprēķināšana e-komercijas lietojumprogrammā
E-komercijas lietojumprogrammā jums varētu būt `Order` modelis ar saistību ar `OrderItem` modeļiem. Jūs varētu izmantot hibrīdo īpašību, lai aprēķinātu pasūtījuma kopējo vērtību.
from sqlalchemy import ForeignKey, Float
from sqlalchemy.orm import relationship
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
items = relationship("OrderItem", back_populates="order")
@hybrid_property
def total(self):
return sum(item.price * item.quantity for item in self.items)
@total.expression
def total(cls):
return session.query(func.sum(OrderItem.price * OrderItem.quantity)).\
filter(OrderItem.order_id == cls.id).scalar_subquery()
class OrderItem(Base):
__tablename__ = 'order_items'
id = Column(Integer, primary_key=True)
order_id = Column(Integer, ForeignKey('orders.id'))
order = relationship("Order", back_populates="items")
price = Column(Float)
quantity = Column(Integer)
Šis piemērs demonstrē sarežģītāku izteiksmes funkciju, kas izmanto apakšvaicājumu, lai aprēķinātu kopsummu tieši datubāzē.
Ģeogrāfiskie aprēķini
Ja strādājat ar ģeogrāfiskiem datiem, jūs varētu izmantot hibrīdās īpašības, lai aprēķinātu attālumus starp punktiem vai noteiktu, vai punkts atrodas noteiktā reģionā. Tas bieži ietver datubāzei specifisku ģeogrāfisko funkciju izmantošanu (piemēram, PostGIS funkcijas PostgreSQL).
from geoalchemy2 import Geometry
from sqlalchemy import cast
class Location(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key=True)
name = Column(String)
coordinates = Column(Geometry(geometry_type='POINT', srid=4326))
@hybrid_property
def latitude(self):
if self.coordinates:
return self.coordinates.x
return None
@latitude.expression
def latitude(cls):
return cast(func.ST_X(cls.coordinates), Float)
@hybrid_property
def longitude(self):
if self.coordinates:
return self.coordinates.y
return None
@longitude.expression
def longitude(cls):
return cast(func.ST_Y(cls.coordinates), Float)
Šim piemēram ir nepieciešams `geoalchemy2` paplašinājums un tiek pieņemts, ka jūs izmantojat datubāzi ar iespējotu PostGIS.
Labākās prakses hibrīdo īpašību izmantošanai
- Vienkāršība: Izmantojiet hibrīdās īpašības salīdzinoši vienkāršiem aprēķiniem. Sarežģītākai loģikai apsveriet iespēju izmantot atsevišķas funkcijas vai metodes.
- Izmantojiet atbilstošus datu tipus: Pārliecinieties, ka jūsu hibrīdajās īpašībās izmantotie datu tipi ir saderīgi gan ar Python, gan ar SQL.
- Apsveriet veiktspēju: Lai gan hibrīdās īpašības var uzlabot veiktspēju, veicot aprēķinus datubāzē, ir svarīgi uzraudzīt jūsu vaicājumu veiktspēju un optimizēt tos pēc nepieciešamības.
- Rūpīgi testējiet: Rūpīgi testējiet savas hibrīdās īpašības, lai nodrošinātu, ka tās sniedz pareizus rezultātus visos kontekstos.
- Dokumentējiet savu kodu: Skaidri dokumentējiet savas hibrīdās īpašības, lai izskaidrotu, ko tās dara un kā tās darbojas.
Biežākās kļūdas un kā no tām izvairīties
- Datubāzei specifiskas funkcijas: Pārliecinieties, ka jūsu izteiksmes funkcijas izmanto no datubāzes neatkarīgas funkcijas vai nodrošina datubāzei specifiskas implementācijas, lai izvairītos no saderības problēmām.
- Nepareizas izteiksmes funkcijas: Pārbaudiet, vai jūsu izteiksmes funkcijas pareizi pārtulko jūsu hibrīdo īpašību derīgā SQL izteiksmē.
- Veiktspējas problēmas: Izvairieties no hibrīdo īpašību izmantošanas aprēķiniem, kas ir pārāk sarežģīti vai resursietilpīgi, jo tas var radīt veiktspējas problēmas.
- Konfliktējoši nosaukumi: Izvairieties no viena un tā paša nosaukuma izmantošanas jūsu hibrīdajai īpašībai un kolonnai modelī, jo tas var radīt neskaidrības un kļūdas.
Internacionalizācijas apsvērumi
Strādājot ar hibrīdajām īpašībām internacionalizētās lietojumprogrammās, ņemiet vērā sekojošo:
- Datuma un laika formāti: Izmantojiet atbilstošus datuma un laika formātus dažādām lokalizācijām.
- Skaitļu formāti: Izmantojiet atbilstošus skaitļu formātus dažādām lokalizācijām, ieskaitot decimālo atdalītāju un tūkstošu atdalītāju.
- Valūtu formāti: Izmantojiet atbilstošus valūtu formātus dažādām lokalizācijām, ieskaitot valūtas simbolus un decimāldaļu vietas.
- Virkņu salīdzināšana: Izmantojiet lokalizācijai atbilstošas virkņu salīdzināšanas funkcijas, lai nodrošinātu, ka virknes tiek pareizi salīdzinātas dažādās valodās.
Piemēram, aprēķinot vecumu, ņemiet vērā dažādos datuma formātus, kas tiek izmantoti visā pasaulē. Dažos reģionos datums tiek rakstīts kā `MM/DD/YYYY`, bet citos – `DD/MM/YYYY` vai `YYYY-MM-DD`. Pārliecinieties, ka jūsu kods pareizi apstrādā datumus visos formātos.
Savienojot virknes (kā `full_name` piemērā), apzinieties kultūras atšķirības vārdu secībā. Dažās kultūrās uzvārds tiek likts pirms vārda. Apsveriet iespēju nodrošināt lietotājiem iespējas pielāgot vārda attēlošanas formātu.
Noslēgums
SQLAlchemy hibrīdās īpašības ir spēcīgs rīks aprēķināto atribūtu veidošanai jūsu datu modeļos. Tās ļauj izteikt sarežģītas attiecības un aprēķinus tieši jūsu modeļos, uzlabojot koda lasāmību, uzturamību un efektivitāti. Izprotot, kā definēt hibrīdās īpašības, izteiksmes funkcijas, setterus un deleterus, jūs varat izmantot šo funkciju, lai veidotu sarežģītākas un robustākas lietojumprogrammas.
Ievērojot šajā rakstā izklāstītās labākās prakses un izvairoties no biežākajām kļūdām, jūs varat efektīvi izmantot hibrīdās īpašības, lai uzlabotu savus SQLAlchemy modeļus un vienkāršotu datu piekļuves loģiku. Atcerieties ņemt vērā internacionalizācijas aspektus, lai nodrošinātu, ka jūsu lietojumprogramma pareizi darbojas lietotājiem visā pasaulē. Ar rūpīgu plānošanu un implementāciju hibrīdās īpašības var kļūt par nenovērtējamu daļu no jūsu SQLAlchemy rīku komplekta.