このブログ投稿では、Django 1.8で導入された新しいPostgreSQL固有のModelFields(ArrayField、HStoreField、およびRange Fields)の使用方法について説明しています。
この投稿は、それを実現させた真のプラヤであるMarcTamlynによってまとめられたこのKickstarterキャンペーンの素晴らしい支援者に捧げられています。
Playaz Club?
私は巨大なオタクであり、本物のPlayaz Clubに入る機会がないため(そして、4日目にはTayが爆弾だったため)、独自の仮想オンラインPlayazClubを構築することにしました。それは正確には何ですか?志を同じくする個人の小グループを対象とした、招待制のプライベートソーシャルネットワーク。
この投稿では、ユーザーモデルに焦点を当て、Djangoの新しいPostgreSQL機能がモデリングをどのようにサポートしているかを探ります。ここで参照している新機能はPostgreSQLのみであるため、データベースの ENGINE
がない限り、わざわざこれを試してはいけません。 django.db.backends.postgresql_psycopg2
に等しい 。 psycopg2
のバージョン>=2.5が必要になります 。プラヤ、これをやってみましょう。
あなたが私と一緒ならホラ! :)
プラヤの担当者のモデリング
すべてのプラヤには担当者がいて、彼らは全世界に担当者について知ってもらいたいと思っています。それでは、各プラヤズがそれぞれの個性を表現できるようにするユーザープロファイル(別名「担当者」)を作成しましょう。
プラヤズ担当者の基本モデルは次のとおりです。
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()
上記1.8に固有のものはありません。基本のDjangoユーザーを拡張するための単なる標準モデルですが、プラヤにはユーザー名とメールアドレスが必要ですよね?さらに、playazフードと市外局番を保存するための2つの新しいフィールドを追加しました。
バンクロールとRangeField
プラヤの場合、フードを担当するだけでは必ずしも十分ではありません。 Playazはバンクロールを誇示するのが好きですが、同時に、そのバンクロールの大きさを正確に人々に知らせたくありません。新しいPostgres範囲フィールドの1つでそれをモデル化できます。もちろん、 BigIntegerRangeField
を使用します 巨大な数字をより適切にモデル化するために、いいですか?
bankroll = pgfields.BigIntegerRangeField(default=(10, 100))
範囲フィールドは、psycopg2 Rangeオブジェクトに基づいており、NumericおよびDateRangesに使用できます。バンクロールフィールドをデータベースに移行すると、範囲オブジェクトを渡すことで範囲フィールドを操作できるため、最初のプラヤを作成すると次のようになります。
>>>>>> 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()
次の行に注意してください: calvins_rep.bankroll =(100000000、150000000)
。ここでは、単純なタプルを使用して範囲フィールドを設定しています。 NumericRange
を使用して値を設定することもできます そのようなオブジェクト:
from psycopg2.extras import NumericRange
br = NumericRange(lower=100000000, upper=150000000)
calvin.rep.bankroll = br
calvin.rep.save()
これは、基本的にタプルを使用するのと同じです。ただし、 NumericRange
について知っておくことが重要です。 モデルをフィルタリングするために使用されるオブジェクト。たとえば、バンクロールが5,000万を超える(つまり、バンクロールの範囲全体が5,000万を超える)すべてのプラヤを検索する場合:
Rep.objects.filter(bankroll__fully_gt=NumericRange(50000000, 50000000))
そして、それはそれらのプラヤのリストを返します。または、バンクロールが「1,000万から1,500万の範囲」であるすべてのプラヤを検索する場合は、次を使用できます。
Rep.objects.filter(bankroll__overlap=NumericRange(10000000, 15000000))
これにより、1,000万から1,500万の範囲の少なくとも一部を含むバンクロール範囲を持つすべてのプラヤが返されます。より絶対的なクエリは、範囲の間に完全にバンクロールがあるすべてのプラヤです。つまり、1,000万以上1500万以下のすべての人が稼いでいます。そのクエリは次のようになります:
Rep.objects.filter(bankroll__contained_by=NumericRange(10000000, 15000000))
範囲ベースのクエリの詳細については、こちらをご覧ください。
ArrayFieldとしてのスキル
それはバンクロールのすべてではありません、playazはskillz、あらゆる種類のskillzを手に入れました。 ArrayFieldでモデル化しましょう。
skillz = pgfields.ArrayField(
models.CharField(max_length=100, blank=True),
blank = True,
null = True,
)
ArrayField
を宣言するには 最初の引数、つまりベースフィールドを指定する必要があります。 Pythonリストとは異なり、ArrayFieldsはリストの各要素を同じ型として宣言する必要があります。 Basefieldは、これがどのタイプであるかを宣言し、標準モデルのフィールドタイプのいずれでもかまいません。上記の場合、 CharField
を使用しました ベースタイプとして、これは killz
を意味します 文字列の配列になります。
ArrayField
への値の保存 期待どおりです:
>>> 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()
skillzによるプラヤの検索
特定のスキルを持つ特定のプラヤが必要な場合、どのようにしてそれらを見つけるのでしょうか? __ contains
を使用します フィルタ:
Rep.objects.filter(skillz__contains=['rappin'])
スキル[「ラピン」、「DJ」、「プロデュース」]のいずれかを持っているが、他のスキルを持っていないプラヤの場合は、次のようなクエリを実行できます。
Rep.objects.filter(skillz__contained_by=['rappin', 'djing', 'producing'])
または、特定のスキルのリストのいずれかを持っている人を見つけたい場合:
Rep.objects.filter(skillz__overlap=['rappin', 'djing', 'producing'])
スキルを最初のスキルとしてリストした人だけを見つけることもできます(誰もが最初に最高のスキルをリストするため):
Rep.objects.filter(skillz__0='ballin')
HStoreとしてのゲーム
ゲームは、プラヤが持つ可能性のあるその他のランダムなスキルのリストと考えることができます。 Gameはさまざまなものにまたがっているので、HStoreフィールドとしてモデル化しましょう。これは、基本的に、古いPython辞書をそこに貼り付けることができることを意味します。
game = pgfields.HStoreField()
ここで何をしたかを考えてみてください。 HStoreはかなり巨大です。基本的に、postgreSQL内で「NoSQL」タイプのデータストレージを使用できます。さらに、PostgreSQL内にあるため、(外部キーを介して)NoSQLデータを含むテーブルと通常のSQLタイプのデータを格納するテーブルをリンクできます。ここで行っているように、両方を同じテーブルの異なる列に格納することもできます。たぶん、プラヤはそのジャンキーを使う必要はなく、もうオールトークのMongoDB…
実装の詳細に戻って、新しいHStoreフィールドをデータベースに移行しようとして、このエラーが発生した場合-
django.db.utils.ProgrammingError: type "hstore" does not exist
-その場合、PostgreSQLデータベースは8.1より前(アップグレードの時間、playa)であるか、HStore拡張機能がインストールされていません。 PostgreSQLでは、HStore拡張機能はデータベースごとにインストールされ、システム全体ではインストールされないことに注意してください。 psqlプロンプトからインストールするには、次のSQLを実行します。
CREATE EXTENSION hstore
または、必要に応じて、次の移行ファイルを使用してSQL移行を行うこともできます(スーパーユーザーとしてデータベースに接続していると仮定します)。
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunSQL("CREATE EXTENSION IF NOT EXISTS hstore")
]
最後に、'django.contrib.postgres'
が追加されていることも確認する必要があります 'settings.INSTALLED_APPS'
へ HStoreフィールドを利用するため。
この設定で、 HStoreField
にデータを追加できます ゲームコード> そのように辞書を投げることによって:
>>> 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()
dictは、すべてのキーと値に文字列のみを使用する必要があることに注意してください。
そして今、いくつかのより興味深い例のために…
Propz
playazゲームを検索し、一致するplayazのリストを返す「showgame」関数を作成してみましょう。こっけいな言葉で言えば、関数に渡されたキーをHStoreフィールドで検索しています。次のようになります:
def show_game(key):
return Rep.Objects.filter(game__has_key=key).values('game','playa__username')
上記では、 has_key
を使用しました HStoreフィールドをフィルター処理してクエリセットを返し、さらにvalues関数でフィルター処理します(主に django.contrib.postgres
をチェーンできることを示します 通常のクエリセットのもの)
戻り値は辞書のリストになります:
[
{'playa__username': 'snoop',
'game': {'twitter_follows': '11000000',
'youtube-channel': 'https://www.youtube.com/user/westfesttv',
'best_album': 'Doggy Style'
}
}
]
彼らが言うように、ゲームはゲームを認識し、今ではゲームも検索できます。
ハイローラー
プラヤズが彼らのバンクロールについて私たちに話していることを信じるなら、それを使って彼らをカテゴリーにランク付けすることができます(それは範囲なので)。次のレベルのバンクロールに基づいてPlayaランキングを追加しましょう:
-
ヤングバック–100グランド未満のバンクロール
-
バラ–スキル「バリン」で100,000から500,000の間のバンクロール
-
プラヤ–2つのスキルといくつかのゲームで500,000から1,000,000の間のバンクロール
-
ハイローラー–バンクロールが1,000,000を超える
-
O.G. –スキル「ギャングスタ」とゲームキー「オールドスクール」を持っている
バラのクエリは以下のとおりです。これは厳密な解釈であり、バンクロール範囲全体が指定された制限内にあるもののみを返します。
Rep.objects.filter(bankroll__contained_by=[100000, 500000], skillz__contains=['ballin'])
残りの部分を自分で試して練習してください。ヘルプが必要な場合は、ドキュメントをお読みください。