sql >> Database teknologi >  >> RDS >> Database

Sjovt med Djangos nye Postgres-funktioner

Dette blogindlæg dækker, hvordan man bruger de nye PostgreSQL-specifikke ModelFields introduceret i Django 1.8 - ArrayField, HStoreField og Range Fields.

Dette indlæg er dedikeret til de fantastiske bagmænd til denne Kickstarter-kampagne sammensat af Marc Tamlyn, den sande playa, der fik det til at ske.


Playaz Club?

Da jeg er en kæmpe nørd og ikke har nogen chance for nogensinde at komme ind i en rigtig Playaz Club (og fordi dengang 4 Tay var bomben), besluttede jeg at bygge min egen virtuelle online Playaz Club. Hvad er det helt præcist? Et privat socialt netværk, der kun kan inviteres, målrettet en lille gruppe af ligesindede.

Til dette indlæg vil vi fokusere på brugermodellen og undersøge, hvordan Djangos nye PostgreSQL-funktioner understøtter modelleringen. De nye funktioner, vi henviser til, er kun PostgreSQL, så du skal ikke prøve dette, medmindre du har din database ENGINE lig med django.db.backends.postgresql_psycopg2 . Du skal bruge version>=2.5 af psycopg2 . Okay, lad os gøre dette.

Hej, hvis du er med mig! :)



Modellere en Playas repræsentant

Hver playa fik en rep, og de vil have, at hele verden skal vide om deres rep. Så lad os oprette en brugerprofil (alias en "rep"), der gør det muligt for hver af vores playaz at udtrykke deres individualitet.

Her er den grundlæggende model for en playaz-repræsentant:

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()

Intet specifikt til 1.8 ovenfor. Bare en standardmodel til at udvide basis Django-brugeren, fordi en playa stadig har brug for et brugernavn og e-mailadresse, ikke? Derudover har vi tilføjet to nye felter til at gemme playaz-hætten og områdenummeret.



Bankroll og RangeField

Til en playa er det ikke altid nok at gentage din hætte. Playaz kan ofte lide at fremvise deres bankroll, men vil samtidig ikke lade folk vide præcis, hvor stor den bankroll er. Det kan vi modellere med en af ​​de nye Postgres Range Fields. Selvfølgelig vil vi bruge BigIntegerRangeField for bedre at modellere massive cifre, ikke sandt?

bankroll = pgfields.BigIntegerRangeField(default=(10, 100))

Områdefelter er baseret på psycopg2 Range-objekter og kan bruges til Numeriske og Dateintervaller. Med bankroll-feltet migreret til databasen, kan vi interagere med vores range-felter ved at give det et range-objekt, så at oprette vores første playa ville se sådan ud:

>>>
>>> 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()

Bemærk denne linje:calvins_rep.bankroll = (100000000, 150000000) . Her indstiller vi et områdefelt ved at bruge en simpel tupel. Det er også muligt at indstille værdien ved hjælp af en NumericRange objekt som sådan:

from psycopg2.extras import NumericRange
br = NumericRange(lower=100000000, upper=150000000)
calvin.rep.bankroll = br
calvin.rep.save()

Dette er stort set det samme som at bruge tuple. Det er dog vigtigt at vide om NumericRange objekt, som det bruges til at filtrere modellen. For eksempel, hvis vi ønskede at finde alle playas, hvis bankroll var større end 50 millioner (hvilket betyder, at hele bankroll-intervallet er større end 50 millioner):

Rep.objects.filter(bankroll__fully_gt=NumericRange(50000000, 50000000))

Og det vil returnere listen over disse playas. Alternativt, hvis vi ville finde alle playas, hvis bankroll er "et sted omkring 10 til 15 millioner intervallet", kunne vi bruge:

Rep.objects.filter(bankroll__overlap=NumericRange(10000000, 15000000))

Dette ville returnere alle playas, der har en bankroll-rækkevidde, der omfatter mindst en del af intervallet 10 til 15 millioner. En mere absolut forespørgsel ville være alle playas, der har en bankroll helt mellem et interval, dvs. alle, der tjener mindst 10 millioner, men ikke mere end 15 millioner. Den forespørgsel ville se sådan ud:

Rep.objects.filter(bankroll__contained_by=NumericRange(10000000, 15000000))

Flere oplysninger om rækkevidde-baserede forespørgsler kan findes her.



Skillz as ArrayField

Det handler ikke kun om bankrollen, playaz fik skillz, alle slags skillz. Lad os modellere dem med et ArrayField.

skillz = pgfields.ArrayField(
    models.CharField(max_length=100, blank=True),
    blank = True,
    null = True,
)

At erklære ArrayField vi er nødt til at give det et første argument, som er basisfeltet. I modsætning til Python-lister skal ArrayFields erklære hvert element på listen som den samme type. Basefield erklærer, hvilken type dette er, og det kan være enhver af standardmodelfelttyperne. I ovenstående tilfælde har vi netop brugt et CharField som vores basetype, hvilket betyder skillz vil være en række strenge.

Lagring af værdier i ArrayField er præcis som du forventer:

>>>
>>> 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()

Find playas efter skillz

Hvis vi har brug for en bestemt playa med en bestemt færdighed, hvordan finder vi dem så? Brug __contains filter:

Rep.objects.filter(skillz__contains=['rappin'])

For playas, der har nogen af ​​færdighederne ['rappin', 'djing', 'producere'], men ingen andre færdigheder, kan du lave en forespørgsel som sådan:

Rep.objects.filter(skillz__contained_by=['rappin', 'djing', 'producing'])

Eller hvis du vil finde nogen, der har nogle af en bestemt liste over færdigheder:

Rep.objects.filter(skillz__overlap=['rappin', 'djing', 'producing'])

Du kan endda kun finde de personer, der har angivet en færdighed som deres første færdighed (fordi alle angiver deres bedste færdighed først):

Rep.objects.filter(skillz__0='ballin')



Spil som HStore

Spillet kan opfattes som en liste over diverse, tilfældige færdigheder, som en playa kan have. Da Game spænder over alle mulige ting, lad os modellere det som et HStore-felt, hvilket dybest set betyder, at vi kan stikke enhver gammel Python-ordbog derinde:

game = pgfields.HStoreField()

Brug et øjeblik på at tænke over, hvad vi lige har lavet her. HStore er ret stort. Det giver dybest set mulighed for "NoSQL"-type datalagring, lige inde i postgreSQL. Plus, da det er inde i PostgreSQL, kan vi linke (via fremmednøgler), tabeller, der indeholder NoSQL-data, med tabeller, der gemmer almindelige SQL-data. Du kan endda gemme begge i samme tabel på forskellige kolonner, som vi gør her. Måske behøver playas ikke at bruge den jankie, all-talk MongoDB længere...

For at komme tilbage til implementeringsdetaljerne, hvis du prøver at migrere det nye HStore-felt til databasen og ender med denne fejl-

django.db.utils.ProgrammingError: type "hstore" does not exist

- så er din PostgreSQL-database før 8.1 (tid til at opgradere, playa) eller har ikke HStore-udvidelsen installeret. Husk, at i PostgreSQL er HStore-udvidelsen installeret pr. database og ikke hele systemet. For at installere det fra din psql-prompt skal du køre følgende SQL:

CREATE EXTENSION hstore

Eller hvis du vil, kan du gøre det gennem en SQL-migrering med følgende migreringsfil (forudsat at du var forbundet til databasen som superbruger):

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunSQL("CREATE EXTENSION IF NOT EXISTS hstore")
    ]

Endelig skal du også sikre dig, at du har tilføjet 'django.contrib.postgres' til 'settings.INSTALLED_APPS' at gøre brug af HStore-felter.

Med den opsætning kan vi tilføje data til vores HStoreField game ved at smide en ordbog efter det sådan:

>>>
>>> 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()

Husk på, at diktatet kun må bruge strenge for alle nøgler og værdier.

Og nu til nogle flere interessante eksempler...



Propz

Lad os skrive en "vis spil"-funktion for at søge i playaz-spillet og returnere en liste over playaz, der matcher. I nørdede termer søger vi gennem HStore-feltet efter alle nøgler, der er sendt ind i funktionen. Det ser nogenlunde sådan her ud:

def show_game(key):
    return Rep.Objects.filter(game__has_key=key).values('game','playa__username')

Ovenfor har vi brugt has_key filter for HStore-feltet for at returnere et forespørgselssæt, og filtrerede det derefter yderligere med værdifunktionen (hovedsageligt for at vise, at du kan kæde django.contrib.postgres ting med almindelige forespørgselssæt).

Returværdien ville være en liste over ordbøger:

[
  {'playa__username': 'snoop',
  'game': {'twitter_follows': '11000000',
           'youtube-channel': 'https://www.youtube.com/user/westfesttv',
           'best_album': 'Doggy Style'
        }
  }
]

Som de siger, genkender Game Game, og nu kan vi også søge efter spil.



High Rollers

Hvis vi tror på, hvad playaz fortæller os om deres bankrolls, så kan vi bruge det til at rangere dem i kategorier (da det er en rækkevidde). Lad os tilføje en Playa-rangering baseret på bankrollen med følgende niveauer:

  • young buck – bankroll mindre end hundrede grand

  • balla – bankroll mellem 100.000 og 500.000 med evnen 'ballin'

  • playa – bankroll mellem 500.000 og 1.000.000 med to skillz og noget spil

  • high roller – bankroll større end 1.000.000

  • O.G. – har evnen 'gangsta' og spilnøgle "old-school"

Forespørgslen til balla er nedenfor. Dette ville være den strenge fortolkning, som kun ville returnere dem, hvis hele bankroll-interval er inden for de specificerede grænser:

Rep.objects.filter(bankroll__contained_by=[100000, 500000], skillz__contains=['ballin'])

Prøv resten selv for at øve dig. Hvis du har brug for hjælp, kan du læse Docs.



  1. Flere rækker til én kommasepareret værdi i SQL Server

  2. Sådan håndteres booleske værdier i SQLite ved hjælp af JavaScript-proxies

  3. Beregn løbende total / løbende balance

  4. PostgreSQL-udløsere og grundlæggende funktioner i lagrede funktioner