Tento příspěvek na blogu popisuje, jak používat nová pole ModelField specifická pro PostgreSQL uvedená v Django 1.8 – ArrayField, HStoreField a Range Fields.
Tento příspěvek je věnován úžasným podporovatelům této kampaně na Kickstarteru, kterou dal dohromady Marc Tamlyn, skutečný playa, který to umožnil.
Klub Playaz?
Protože jsem velký geek a nemám šanci se někdy dostat do skutečného Playaz Clubu (a protože v den 4 byl Tay bomba), rozhodl jsem se vybudovat svůj vlastní virtuální online Playaz Club. co to přesně je? Soukromá sociální síť pouze pro pozvané cílená na malou skupinu stejně smýšlejících jedinců.
V tomto příspěvku se zaměříme na model uživatele a prozkoumáme, jak nové funkce PostgreSQL společnosti Django podporují modelování. Nové funkce, o kterých se zmiňujeme, jsou pouze PostgreSQL, takže se neobtěžujte zkoušet, pokud nemáte svou databázi ENGINE
rovno django.db.backends.postgresql_psycopg2
. Budete potřebovat verzi>=2.5 psycopg2
. Aight playa, pojďme na to.
Ahoj, když budeš se mnou! :)
Modelování zástupce Playa
Každá hra má zástupce a chtějí, aby o jejich zástupci věděl celý svět. Vytvořme si tedy uživatelský profil (neboli „zástupce“), který umožní každému z našich playaz vyjádřit svou individualitu.
Zde je základní model pro zástupce playaz:
from django.db import models
from django.contrib.auth.models import User
class Rep(models.Model):
playa = models.OneToOneField(User)
hood = models.CharField(max_length=100)
area_code = models.IntegerField()
Nic specifického pro 1.8 výše. Jen standardní model pro rozšíření základního uživatele Django, protože playa stále potřebuje uživatelské jméno a e-mailovou adresu, že? Navíc jsme přidali dvě nová pole pro uložení krytu Playaz a kódu oblasti.
Bankroll a RangeField
Na hraní nestačí jen klepat na kapotu. Playaz se často rád chlubí svým bankrollem, ale zároveň nechce dát lidem vědět, jak velký ten bankroll je. Můžeme to modelovat pomocí jednoho z nových Postgres Range Fields. Samozřejmě použijeme BigIntegerRangeField
lépe modelovat masivní číslice, ne?
bankroll = pgfields.BigIntegerRangeField(default=(10, 100))
Pole rozsahů jsou založena na objektech psycopg2 Range a lze je použít pro Numeric a DateRanges. Po migraci bankrollového pole do databáze můžeme interagovat s našimi poli range tím, že jim předáme objekt range, takže vytvoření naší první hry by vypadalo asi takto:
>>>>>> from playa.models import Rep
>>> from django.contrib.auth.models import User
>>> calvin = User.objects.create_user(username="snoop", password="dogg")
>>> calvins_rep = Rep(hood="Long Beach", area_code=213)
>>> calvins_rep.bankroll = (100000000, 150000000)
>>> calvins_rep.playa = calvin
>>> calvins_rep.save()
Všimněte si tohoto řádku:calvins_rep.bankroll = (100000000, 150000000)
. Zde nastavujeme pole rozsahu pomocí jednoduché n-tice. Hodnotu je také možné nastavit pomocí NumericRange
objekt takto:
from psycopg2.extras import NumericRange
br = NumericRange(lower=100000000, upper=150000000)
calvin.rep.bankroll = br
calvin.rep.save()
To je v podstatě stejné jako použití n-tice. Je však důležité vědět o NumericRange
objekt, který se používá k filtrování modelu. Pokud bychom například chtěli najít všechny hry, jejichž bankroll byl větší než 50 milionů (to znamená, že celý rozsah bankrollu je větší než 50 milionů):
Rep.objects.filter(bankroll__fully_gt=NumericRange(50000000, 50000000))
A to vrátí seznam těch her. Případně, pokud bychom chtěli najít všechny hry, jejichž bankroll je „někde v rozmezí 10 až 15 milionů“, mohli bychom použít:
Rep.objects.filter(bankroll__overlap=NumericRange(10000000, 15000000))
To by vrátilo všechny hry, které mají rozsah bankrollu, který zahrnuje alespoň část z rozsahu 10 až 15 milionů. Absolutnějším dotazem by byly všechny hry, které mají bankroll plně mezi rozsahem, tedy každý, kdo vydělává alespoň 10 milionů, ale ne více než 15 milionů. Tento dotaz by vypadal takto:
Rep.objects.filter(bankroll__contained_by=NumericRange(10000000, 15000000))
Více informací o dotazech založených na rozsahu naleznete zde.
Skillz jako ArrayField
Není to všechno o bankrollu, playaz dostal skillz, všechny druhy skillz. Pojďme je modelovat pomocí pole ArrayField.
skillz = pgfields.ArrayField(
models.CharField(max_length=100, blank=True),
blank = True,
null = True,
)
Chcete-li deklarovat ArrayField
musíme mu dát první argument, kterým je základní pole. Na rozdíl od seznamů Python musí ArrayFields deklarovat každý prvek seznamu jako stejný typ. Basefield deklaruje, o jaký typ se jedná, a může to být kterýkoli ze standardních typů polí modelu. Ve výše uvedeném případě jsme právě použili CharField
jako náš základní typ, což znamená skillz
bude pole řetězců.
Ukládání hodnot do ArrayField
je přesně tak, jak očekáváte:
>>> from django.contrib.auth.models import User
>>> calvin = User.objects.get(username='snoop')
>>> calvin.rep.skillz = ['ballin', 'rappin', 'talk show host', 'merchandizn']
>>> calvin.rep.save()
Hledání her podle skillz
Pokud potřebujeme konkrétní playa s konkrétní dovedností, jak je najdeme? Použijte __contains
filtr:
Rep.objects.filter(skillz__contains=['rappin'])
Pro hráče, kteří mají některou z dovedností [‚rappin‘, ‚djing‘, ‚producing‘], ale žádné jiné dovednosti, můžete zadat dotaz takto:
Rep.objects.filter(skillz__contained_by=['rappin', 'djing', 'producing'])
Nebo pokud chcete najít někoho, kdo má některou z určitých dovedností:
Rep.objects.filter(skillz__overlap=['rappin', 'djing', 'producing'])
Mohli byste dokonce najít pouze ty lidi, kteří uvedli dovednost jako svou první dovednost (protože každý uvádí svou nejlepší dovednost jako první):
Rep.objects.filter(skillz__0='ballin')
Hra jako HStore
Hru lze považovat za seznam různých náhodných dovedností, které může mít hra. Protože hra zahrnuje všemožné věci, modelujme ji jako pole HStore, což v podstatě znamená, že tam můžeme vložit jakýkoli starý slovník Pythonu:
game = pgfields.HStoreField()
Zamyslete se nad tím, co jsme tu právě udělali. HStore je docela velký. V podstatě umožňuje ukládání dat typu „NoSQL“ přímo uvnitř postgreSQL. Navíc, protože je to uvnitř PostgreSQL, můžeme propojit (prostřednictvím cizích klíčů) tabulky, které obsahují data NoSQL, s tabulkami, které ukládají běžná data typu SQL. Obojí můžete dokonce uložit do stejné tabulky v různých sloupcích, jak to děláme zde. Možná, že hry Playas už nemusí používat toho žongléra, všeříkajícího MongoDB…
Vraťme se k podrobnostem implementace, pokud se pokusíte migrovat nové pole HStore do databáze a skončíte s touto chybou-
django.db.utils.ProgrammingError: type "hstore" does not exist
-pak je vaše databáze PostgreSQL starší než 8.1 (čas na upgrade, přehrávání) nebo nemá nainstalované rozšíření HStore. Mějte na paměti, že v PostgreSQL se rozšíření HStore instaluje na databázi a ne na celý systém. Chcete-li jej nainstalovat z příkazového řádku psql, spusťte následující SQL:
CREATE EXTENSION hstore
Nebo pokud chcete, můžete to udělat pomocí SQL Migration s následujícím migračním souborem (za předpokladu, že jste byli připojeni k databázi jako superuživatel):
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunSQL("CREATE EXTENSION IF NOT EXISTS hstore")
]
Nakonec se také budete muset ujistit, že jste přidali 'django.contrib.postgres'
na 'settings.INSTALLED_APPS'
používat pole HStore.
S tímto nastavením můžeme přidávat data do našeho HStoreField
game
tím, že na to hodíte slovník takto:
>>> calvin = User.objects.get(username="snoop")
>>> calvin.rep.game = {'best_album': 'Doggy Style', 'youtube-channel': \
'https://www.youtube.com/user/westfesttv', 'twitter_follows' : '11000000'}
>>> calvin.rep.save()
Mějte na paměti, že diktát musí používat pouze řetězce pro všechny klíče a hodnoty.
A nyní několik dalších zajímavých příkladů…
Propz
Pojďme napsat funkci „ukázat hru“, která prohledá hru playaz a vrátí seznam playaz, které odpovídají. Zcela podivínsky hledáme v poli HStore jakékoli klíče předané do funkce. Vypadá to nějak takto:
def show_game(key):
return Rep.Objects.filter(game__has_key=key).values('game','playa__username')
Výše jsme použili has_key
filtr pro pole HStore, aby vrátil sadu dotazů, pak ji dále filtroval pomocí funkce values (hlavně proto, aby ukázal, že můžete řetězit django.contrib.postgres
věci s běžnou sadou dotazů).
Návratovou hodnotou by byl seznam slovníků:
[
{'playa__username': 'snoop',
'game': {'twitter_follows': '11000000',
'youtube-channel': 'https://www.youtube.com/user/westfesttv',
'best_album': 'Doggy Style'
}
}
]
Jak se říká, Game rozpozná hru a nyní ji můžeme hledat i my.
High Rollers
Pokud věříme tomu, co nám playaz říká o jejich bankrollech, pak to můžeme použít k jejich seřazení do kategorií (protože jde o rozsah). Přidejme hodnocení Playa založené na bankrollu s následujícími úrovněmi:
-
young buck – bankroll méně než sto tisíc
-
balla – bankroll mezi 100 000 a 500 000 s dovedností „ballin“
-
playa – bankroll mezi 500 000 a 1 000 000 se dvěma dovednostmi a nějakou hrou
-
high roller – bankroll vyšší než 1 000 000
-
O.G. – má dovednost „gangsta“ a herní klíč „stará škola“
Dotaz na balla je níže. Toto by byl přísný výklad, který by vrátil pouze ty, jejichž celý rozsah bankrollu je ve stanovených limitech:
Rep.objects.filter(bankroll__contained_by=[100000, 500000], skillz__contains=['ballin'])
Zbytek si vyzkoušejte sami na procvičení. Pokud potřebujete pomoc, přečtěte si Dokumenty.