Database
 sql >> Datenbank >  >> RDS >> Database

Spaß mit den neuen Postgres-Funktionen von Django

Dieser Blogbeitrag behandelt die Verwendung der neuen PostgreSQL-spezifischen ModelFields, die in Django 1.8 eingeführt wurden – die Felder ArrayField, HStoreField und Range.

Dieser Beitrag ist den großartigen Unterstützern dieser Kickstarter-Kampagne gewidmet, die von Marc Tamlyn zusammengestellt wurde, dem wahren Spieler, der sie möglich gemacht hat.


Playaz Club?

Da ich ein großer Geek bin und keine Chance habe, jemals in einen echten Playaz Club zu kommen (und weil Tay an Tag 4 die Bombe war), habe ich mich entschlossen, meinen eigenen virtuellen Online-Playaz-Club aufzubauen. Was ist das genau? Ein privates, nur auf Einladung zugängliches soziales Netzwerk, das sich an eine kleine Gruppe gleichgesinnter Personen richtet.

In diesem Beitrag konzentrieren wir uns auf das Benutzermodell und untersuchen, wie die neuen PostgreSQL-Funktionen von Django die Modellierung unterstützen. Die neuen Funktionen, auf die wir uns beziehen, sind nur PostgreSQL, versuchen Sie dies also nicht, es sei denn, Sie haben Ihre Datenbank ENGINE gleich django.db.backends.postgresql_psycopg2 . Sie benötigen Version>=2.5 von psycopg2 . Aight playa, lass uns das tun.

Holla wenn du bei mir bist! :)



Einen Repräsentanten von Playa modellieren

Jeder Playa hat einen Repräsentanten und sie wollen, dass die ganze Welt von ihrem Repräsentanten erfährt. Erstellen wir also ein Benutzerprofil (auch bekannt als „Rep“), das es jedem unserer Playaz ermöglicht, seine Individualität auszudrücken.

Hier ist das Grundmodell für einen Playaz-Repräsentanten:

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

Nichts Spezifisches zu 1.8 oben. Nur ein Standardmodell, um den Basis-Django-Benutzer zu erweitern, da ein Spieler immer noch einen Benutzernamen und eine E-Mail-Adresse benötigt, richtig? Außerdem haben wir zwei neue Felder hinzugefügt, um die Playaz Hood und die Vorwahl zu speichern.



Bankroll und das RangeField

Für einen Playa ist es nicht immer genug, die Kapuze zu erneuern. Playaz stellt oft gerne seine Bankroll zur Schau, möchte aber gleichzeitig die Leute nicht genau wissen lassen, wie groß diese Bankroll ist. Wir können das mit einem der neuen Postgres Range Fields modellieren. Natürlich verwenden wir das BigIntegerRangeField um massive Ziffern besser zu modellieren, nicht wahr?

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

Range-Felder basieren auf den psycopg2-Range-Objekten und können für Numeric und DateRanges verwendet werden. Nachdem das Bankroll-Feld in die Datenbank migriert wurde, können wir mit unseren Range-Feldern interagieren, indem wir ihm ein Range-Objekt übergeben. Das Erstellen unseres ersten Playa würde also etwa so aussehen:

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

Beachten Sie diese Zeile:calvins_rep.bankroll = (100000000, 150000000) . Hier setzen wir ein Bereichsfeld, indem wir ein einfaches Tupel verwenden. Es ist auch möglich, den Wert über einen NumericRange festzulegen Objekt wie folgt:

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

Dies ist im Wesentlichen dasselbe wie die Verwendung des Tupels. Es ist jedoch wichtig, den NumericRange zu kennen Objekt, da dieses zum Filtern des Modells verwendet wird. Wenn wir zum Beispiel alle Playas finden wollten, deren Bankroll größer als 50 Millionen war (was bedeutet, dass der gesamte Bankroll-Bereich größer als 50 Millionen ist):

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

Und das wird die Liste dieser Playas zurückgeben. Wenn wir alternativ alle Playas finden wollten, deren Bankroll „irgendwo im Bereich von 10 bis 15 Millionen“ liegt, könnten wir Folgendes verwenden:

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

Dies würde alle Playas zurückgeben, die einen Bankroll-Bereich haben, der zumindest einen Teil des Bereichs von 10 bis 15 Millionen umfasst. Eine absolutere Abfrage wären alle Playas, die eine Bankroll vollständig zwischen einem Bereich haben, d. h. alle, die mindestens 10 Millionen, aber nicht mehr als 15 Millionen verdienen. Diese Abfrage würde wie folgt aussehen:

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

Weitere Informationen zu bereichsbasierten Abfragen finden Sie hier.



Skillz als ArrayField

Es geht nicht nur um die Bankroll, Playaz hat Skillz, alle Arten von Skillz. Lassen Sie uns diese mit einem ArrayField modellieren.

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

Um das ArrayField zu deklarieren Wir müssen ihm ein erstes Argument geben, das das Basisfeld ist. Im Gegensatz zu Python-Listen müssen ArrayFields jedes Element der Liste als denselben Typ deklarieren. Basefield deklariert, um welchen Typ es sich handelt, und es kann jeder der Standardmodellfeldtypen sein. Im obigen Fall haben wir nur ein CharField verwendet als unser Basistyp, was skillz bedeutet wird ein Array von Strings sein.

Werte im ArrayField speichern ist genau so, wie Sie es erwarten:

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

Playas nach Skillz finden

Wenn wir einen bestimmten Playa mit einer bestimmten Fähigkeit brauchen, wie finden wir sie? Verwenden Sie den __contains filtern:

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

Für Playas, die eine der Fähigkeiten [‚rappin‘, ‚djing‘, ‚producing‘], aber keine anderen Fähigkeiten haben, könnten Sie eine Abfrage wie folgt durchführen:

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

Oder wenn Sie jemanden finden möchten, der über eine bestimmte Liste von Fähigkeiten verfügt:

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

Sie könnten sogar nur die Personen finden, die eine Fertigkeit als ihre erste Fertigkeit angegeben haben (weil jeder zuerst seine beste Fertigkeit auflistet):

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



Spiel als HStore

Das Spiel kann als eine Liste verschiedener, zufälliger Fähigkeiten betrachtet werden, die ein Playa haben könnte. Da Game alle möglichen Dinge umfasst, modellieren wir es als HStore-Feld, was im Grunde bedeutet, dass wir jedes alte Python-Wörterbuch dort einfügen können:

game = pgfields.HStoreField()

Nehmen Sie sich einen Moment Zeit, um darüber nachzudenken, was wir hier gerade getan haben. HStore ist ziemlich groß. Es ermöglicht im Grunde eine Datenspeicherung vom Typ „NoSQL“ direkt in PostgreSQL. Da es sich innerhalb von PostgreSQL befindet, können wir außerdem (über Fremdschlüssel) Tabellen, die NoSQL-Daten enthalten, mit Tabellen verknüpfen, die reguläre SQL-Daten speichern. Sie können sogar beide in derselben Tabelle in verschiedenen Spalten speichern, wie wir es hier tun. Vielleicht müssen Playas diesen jankie, all-talk MongoDB nicht mehr verwenden …

Zurück zu den Implementierungsdetails, wenn Sie versuchen, das neue HStore-Feld in die Datenbank zu migrieren und mit diesem Fehler enden-

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

- dann ist Ihre PostgreSQL-Datenbank vor 8.1 (Zeit für ein Upgrade, playa) oder hat die HStore-Erweiterung nicht installiert. Beachten Sie, dass die HStore-Erweiterung in PostgreSQL pro Datenbank und nicht systemweit installiert wird. Um es von Ihrer psql-Eingabeaufforderung aus zu installieren, führen Sie die folgende SQL aus:

CREATE EXTENSION hstore

Wenn Sie möchten, können Sie dies auch über eine SQL-Migration mit der folgenden Migrationsdatei tun (vorausgesetzt, Sie waren als Superuser mit der Datenbank verbunden):

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = []

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

Schließlich müssen Sie auch sicherstellen, dass Sie 'django.contrib.postgres' hinzugefügt haben zu 'settings.INSTALLED_APPS' um HStore-Felder zu verwenden.

Mit diesem Setup können wir Daten zu unserem HStoreField hinzufügen game indem man ein Wörterbuch darauf wirft:

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

Denken Sie daran, dass das Diktat nur Zeichenfolgen für alle Schlüssel und Werte verwenden darf.

Und nun zu weiteren interessanten Beispielen…



Propz

Lassen Sie uns eine „Spiel anzeigen“-Funktion schreiben, um das Playaz-Spiel zu durchsuchen und eine Liste der übereinstimmenden Playaz zurückzugeben. In geeky Begriffen suchen wir das HStore-Feld nach Schlüsseln, die an die Funktion übergeben wurden. Es sieht in etwa so aus:

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

Oben haben wir den has_key verwendet Filtern Sie nach dem HStore-Feld, um einen Abfragesatz zurückzugeben, und filtern Sie ihn dann weiter mit der Wertefunktion (hauptsächlich, um zu zeigen, dass Sie django.contrib.postgres verketten können Sachen mit regulärem Abfrageset-Zeug).

Der Rückgabewert wäre eine Liste von Wörterbüchern:

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

Wie sie sagen, erkennt Game Game, und jetzt können wir auch nach Game suchen.



High Roller

Wenn wir glauben, was die Playaz uns über ihre Bankrolls erzählen, dann können wir sie verwenden, um sie in Kategorien einzuordnen (da es sich um eine Spanne handelt). Lassen Sie uns ein Playa-Ranking basierend auf der Bankroll mit den folgenden Ebenen hinzufügen:

  • junger Bock – Bankroll weniger als hundert Riesen

  • balla – Bankroll zwischen 100.000 und 500.000 mit der Fertigkeit „Ballin“

  • Playa – Bankroll zwischen 500.000 und 1.000.000 mit zwei Skillz und etwas Spiel

  • High Roller – Bankroll größer als 1.000.000

  • O.G. – hat die Fähigkeit „Gangsta“ und den Spielschlüssel „old-school“

Die Abfrage für balla ist unten. Dies wäre die strenge Interpretation, die nur diejenigen zurückgeben würde, deren gesamte Bankroll-Range innerhalb der angegebenen Grenzen liegt:

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

Probieren Sie den Rest selbst aus, um etwas zu üben. Wenn Sie Hilfe benötigen, lesen Sie die Dokumentation.