Database
 sql >> Base de données >  >> RDS >> Database

Amusez-vous avec les nouvelles fonctionnalités Postgres de Django

Ce billet de blog explique comment utiliser les nouveaux ModelFields spécifiques à PostgreSQL introduits dans Django 1.8 - les champs ArrayField, HStoreField et Range.

Cet article est dédié aux formidables contributeurs de cette campagne Kickstarter mise en place par Marc Tamlyn, le vrai playa qui a rendu cela possible.


Playaz Club ?

Comme je suis un grand geek et que je n'ai aucune chance d'entrer dans un vrai Playaz Club (et parce qu'à l'époque 4 Tay était la bombe), j'ai décidé de créer mon propre Playaz Club virtuel en ligne. Qu'est-ce que c'est exactement ? Un réseau social privé, sur invitation uniquement, destiné à un petit groupe de personnes partageant les mêmes idées.

Pour cet article, nous allons nous concentrer sur le modèle utilisateur et explorer comment les nouvelles fonctionnalités PostgreSQL de Django prennent en charge la modélisation. Les nouvelles fonctionnalités auxquelles nous faisons référence sont uniquement PostgreSQL, alors ne vous embêtez pas à essayer à moins que vous n'ayez votre base de données ENGINE égal à django.db.backends.postgresql_psycopg2 . Vous aurez besoin de la version>=2.5 de psycopg2 . Aight playa, allons-y.

Holla si vous avec moi! :)



Modélisation d'un représentant de Playa

Chaque playa a un représentant, et ils veulent que le monde entier connaisse leur représentant. Créons donc un profil d'utilisateur (alias un "rep") qui permet à chacun de nos playaz d'exprimer son individualité.

Voici le modèle de base pour un représentant 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()

Rien de spécifique à 1.8 ci-dessus. Juste un modèle standard pour étendre l'utilisateur Django de base, car une playa a toujours besoin d'un nom d'utilisateur et d'une adresse e-mail, n'est-ce pas ? De plus, nous avons ajouté deux nouveaux champs pour stocker le capot de playaz et l'indicatif régional.



Bankroll et RangeField

Pour une playa, repeindre votre capot n'est pas toujours suffisant. Playaz aime souvent afficher sa bankroll, mais en même temps, il ne veut pas que les gens sachent exactement à quel point cette bankroll est importante. Nous pouvons modéliser cela avec l'un des nouveaux champs de gamme Postgres. Bien sûr, nous utiliserons le BigIntegerRangeField pour mieux modéliser les chiffres massifs, n'est-ce pas ?

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

Les champs Range sont basés sur les objets Range psycopg2 et peuvent être utilisés pour Numeric et DateRanges. Avec le champ bankroll migré vers la base de données, nous pouvons interagir avec nos champs range en lui passant un objet range, donc la création de notre première playa ressemblerait à ceci :

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

Notez cette ligne :calvins_rep.bankroll = (100000000, 150000000) . Ici, nous définissons un champ de plage en utilisant un simple tuple. Il est également possible de définir la valeur à l'aide d'un NumericRange objet comme ceci :

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

C'est essentiellement la même chose que d'utiliser le tuple. Cependant, il est important de connaître le NumericRange objet car celui-ci est utilisé pour filtrer le modèle. Par exemple, si nous voulions trouver toutes les playas dont la bankroll était supérieure à 50 millions (c'est-à-dire que toute la plage de bankroll est supérieure à 50 millions) :

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

Et cela renverra la liste de ces playas. Alternativement, si nous voulions trouver toutes les playas dont la bankroll est "quelque part autour de 10 à 15 millions", nous pourrions utiliser :

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

Cela renverrait toutes les playas qui ont une fourchette de bankroll qui comprend au moins une partie de la fourchette de 10 à 15 millions. Une requête plus absolue serait toutes les playas qui ont une bankroll entièrement comprise entre une fourchette, c'est-à-dire tout le monde qui gagne au moins 10 millions mais pas plus de 15 millions. Cette requête ressemblerait à :

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

Vous trouverez plus d'informations sur les requêtes basées sur la plage ici.



Skillz en tant que ArrayField

Ce n'est pas qu'une question de bankroll, playaz a des compétences, toutes sortes de compétences. Modélisons-les avec un ArrayField.

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

Pour déclarer le ArrayField nous devons lui donner un premier argument, qui est le champ de base. Contrairement aux listes Python, ArrayFields doit déclarer chaque élément de la liste comme étant du même type. Basefield déclare de quel type il s'agit, et il peut s'agir de n'importe quel type de champ de modèle standard. Dans le cas ci-dessus, nous venons d'utiliser un CharField comme type de base, ce qui signifie skillz sera un tableau de chaînes.

Stockage des valeurs dans le ArrayField correspond exactement à vos attentes :

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

Trouver des playas par skillz

Si nous avons besoin d'un playa particulier avec une compétence particulière, comment allons-nous les trouver ? Utilisez le __contains filtre :

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

Pour les joueurs qui ont l'une des compétences ['rapper', 'djing', 'produire'] mais pas d'autres compétences, vous pouvez faire une requête comme celle-ci :

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

Ou si vous voulez trouver quelqu'un qui possède une certaine liste de compétences :

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

Vous pouvez même trouver uniquement les personnes qui ont indiqué une compétence comme leur première compétence (car tout le monde liste sa meilleure compétence en premier) :

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



Jeu en tant que HStore

Le jeu peut être considéré comme une liste de compétences diverses et aléatoires qu'un playa pourrait avoir. Étant donné que Game couvre toutes sortes de choses, modélisons-le comme un champ HStore, ce qui signifie essentiellement que nous pouvons y coller n'importe quel ancien dictionnaire Python :

game = pgfields.HStoreField()

Prenez une seconde pour réfléchir à ce que nous venons de faire ici. HStore est assez énorme. Il permet essentiellement le stockage de données de type "NoSQL", directement à l'intérieur de postgreSQL. De plus, comme c'est à l'intérieur de PostgreSQL, nous pouvons lier (via des clés étrangères) des tables contenant des données NoSQL avec des tables qui stockent des données de type SQL standard. Vous pouvez même stocker les deux dans la même table sur des colonnes différentes comme nous le faisons ici. Peut-être que les playas n'ont plus besoin d'utiliser ce jankie, MongoDB tout bavard...

Pour en revenir aux détails de mise en œuvre, si vous essayez de migrer le nouveau champ HStore dans la base de données et que vous vous retrouvez avec cette erreur-

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

-alors votre base de données PostgreSQL est pré 8.1 (temps de mise à niveau, playa) ou n'a pas l'extension HStore installée. Gardez à l'esprit que dans PostgreSQL, l'extension HStore est installée par base de données et non à l'échelle du système. Pour l'installer à partir de votre invite psql, exécutez le SQL suivant :

CREATE EXTENSION hstore

Ou si vous le souhaitez, vous pouvez le faire via une migration SQL avec le fichier de migration suivant (en supposant que vous étiez connecté à la base de données en tant que superutilisateur) :

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = []

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

Enfin, vous devrez également vous assurer que vous avez ajouté 'django.contrib.postgres' à 'settings.INSTALLED_APPS' pour utiliser les champs HStore.

Avec cette configuration, nous pouvons ajouter des données à notre HStoreField game en lui lançant un dictionnaire comme ceci :

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

Gardez à l'esprit que le dict ne doit utiliser que des chaînes pour toutes les clés et valeurs.

Et maintenant, quelques exemples plus intéressants…



Propz

Écrivons une fonction "show game" pour rechercher le jeu playaz et renvoyer une liste de playaz qui correspondent. En termes geek, nous recherchons dans le champ HStore toutes les clés transmises à la fonction. Cela ressemble à ceci :

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

Ci-dessus, nous avons utilisé le has_key filtrez le champ HStore pour renvoyer un ensemble de requêtes, puis filtrez-le davantage avec la fonction values ​​(principalement pour montrer que vous pouvez chaîner django.contrib.postgres des trucs avec des trucs de jeu de requêtes normaux).

La valeur de retour serait une liste de dictionnaires :

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

Comme on dit, le jeu reconnaît le jeu, et maintenant nous pouvons aussi rechercher le jeu.



Grands joueurs

Si nous croyons ce que les playaz nous disent sur leur bankroll, nous pouvons l'utiliser pour les classer dans des catégories (puisque c'est une gamme). Ajoutons un classement Playa basé sur la bankroll avec les niveaux suivants :

  • jeune mâle - bankroll moins de cent mille

  • balla - bankroll entre 100 000 et 500 000 avec la compétence 'ballin'

  • playa - bankroll entre 500 000 et 1 000 000 avec deux skillz et du jeu

  • gros parieur – bankroll supérieur à 1 000 000

  • OG – a la compétence "gangsta" et la clé de jeu "old-school"

La requête pour balla est ci-dessous. Ce serait l'interprétation stricte, qui ne renverrait que ceux dont la totalité de la bankroll se situe dans les limites spécifiées :

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

Essayez le reste vous-même pour vous entraîner. Si vous avez besoin d'aide, lisez la documentation.