Django
モデル
サーチ…
前書き
基本的なケースでは、モデルは単一のデータベーステーブルにマップされるPythonクラスです。クラスの属性は、表の列にマップされ、クラスのインスタンスは、データベース表の行を表します。モデルはdjango.db.models.Model
から継承され、データベースの結果を追加およびフィルタリングするための豊富なAPIを提供します。
最初のモデルを作成する
モデルは通常、アプリケーションのサブディレクトリのmodels.py
ファイルに定義されています。 django.db.models
モジュールのModel
クラスは、モデルを拡張するのに適した開始クラスです。例えば:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='authored_books')
publish_date = models.DateField(null=True, blank=True)
def __str__(self): # __unicode__ in python 2.*
return self.title
モデル内の各属性は、データベース内の列を表します。
-
title
は最大長100文字のテキストです -
author
は別のモデル/テーブル(この場合はAuthor
)との関係を表すForeignKey
です(例としてのみ使用されます)。on_delete
は、関連するオブジェクト(Author
)が削除された場合に、データベースでオブジェクトのon_delete
指示します。 (django 1.9on_delete
は2番目の位置引数として使用できるので、 django 2では必須の引数であり、直ちにそれを扱うことをお勧めします。古いバージョンでは、デフォルトのCASCADE
ます)。 -
publish_date
は日付を格納します。null
とblank
両方がTrue
に設定されて、必須フィールドではないことを示します(つまり、後で追加したり、空にしておくことができます)。
属性と一緒に__str__
メソッドを定義すると、これは、デフォルトではなく、必要に応じてstring
表現として使用される本のタイトルを返します。
変更をデータベースに適用する(移行)
新しいモデルを作成したり、既存のモデルを変更したりしたら、変更のためにマイグレーションを生成し、指定したデータベースにマイグレーションを適用する必要があります。これは、Djangoに組み込まれている移行システムを使用して行うことができます。プロジェクトのルートディレクトリにあるときにmanage.py
ユーティリティを使用する:
python manage.py makemigrations <appname>
上記のコマンドは、アプリケーションのmigrations
サブディレクトリに必要な移行スクリプトを作成します。 <appname>
パラメータを省略すると、 settings.py
のINSTALLED_APPS
引数に定義されているすべてのアプリケーションが処理されます。必要な場合は、移行を編集できます。
実際に移行を作成せずに必要な移行を確認するには、--dry-runオプションを使用します。
python manage.py makemigrations --dry-run
移行を適用するには:
python manage.py migrate <appname>
上記のコマンドは、最初の手順で生成された移行スクリプトを実行し、物理的にデータベースを更新します。
既存のデータベースのモデルが変更された場合、必要な変更を行うには、次のコマンドが必要です。
python manage.py migrate --run-syncdb
Djangoはデフォルトで<appname>_<classname>
という名前のテーブルを作成します。いつかあなたはそれを使いたくないのです。デフォルトの名前を変更する場合は、 Meta
クラスのdb_table
を設定してテーブル名をアナウンスすることができます。
from django.db import models
class YourModel(models.Model):
parms = models.CharField()
class Meta:
db_table = "custom_table_name"
特定の移行によって実行されるSQLコードを確認するには、次のコマンドを実行します。
python manage.py sqlmigrate <app_label> <migration_number>
Django> 1.10
新しいmakemigrations --check
オプションは、マイグレーションのないモデル変更が検出されたときにコマンドがゼロ以外の状態で終了するようにします。
関係を持つモデルの作成
多対1の関係
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
#Book has a foreignkey (many to one) relationship with author
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publish_date = models.DateField()
最も一般的なオプション。関係を表現したい場所であればどこでも使用できます
多対多の関係
class Topping(models.Model):
name = models.CharField(max_length=50)
# One pizza can have many toppings and same topping can be on many pizzas
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
内部的には、これは別のテーブルを介して表されます。そして、 ManyToManyField
は、フォーム上で編集されるモデルに置く必要があります。例えば: Appointment
ありますManyToManyField
と呼ばれるCustomer
、 Pizza
ありToppings
ようにしています。
Throughクラスを使用した多対多の関係
class Service(models.Model):
name = models.CharField(max_length=35)
class Client(models.Model):
name = models.CharField(max_length=35)
age = models.IntegerField()
services = models.ManyToManyField(Service, through='Subscription')
class Subscription(models.Model):
client = models.ForeignKey(Client)
service = models.ForeignKey(Service)
subscription_type = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
created_at = models.DateTimeField(default=timezone.now)
このようにして、実際には2つのエンティティ間の関係についてより多くのメタデータを保持できます。理解されるように、クライアントは、いくつかのサブスクリプションタイプを介して複数のサービスにサブスクライブすることができます。この場合の唯一の違いは、それがM2M関係に新しいインスタンスを追加することで、一方がショートカット方法は使用できませんpizza.toppings.add(topping)
の代わりに、 貫通クラスの新しいオブジェクトを作成しなければならないが、 Subscription.objects.create(client=client, service=service, subscription_type='p')
他の言語では
through tables
はJoinColumn
、Intersection table
またはmapping table
1対1の関係
class Employee(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
spouse = models.OneToOneField(Spouse)
class Spouse(models.Model):
name = models.CharField(max_length=50)
これらのフィールドは、2つのモデル間の合成関係のみを持つ場合に使用します。
基本的なDjango DBクエリ
Django ORMは強力な抽象で、SQLクエリーを自分で作成することなく、データベースからデータを格納して取り出すことができます。
以下のモデルを仮定しましょう:
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author)
上記のコードをdjangoアプリケーションに追加し、 migrate
コマンドを実行して(データベースが作成されるようにして) Djangoシェルを起動する
python manage.py shell
これは、標準のPythonシェルを開始しますが、関連するDjangoライブラリをインポートすることで、重要な部分に直接焦点を当てることができます。
最初に定義したモデルをインポートすることから始めます(これはファイルmodels.py
行われていると仮定しています)
from .models import Book, Author
最初の選択クエリを実行します。
>>> Author.objects.all()
[]
>>> Book.objects.all()
[]
著者とブックオブジェクトを作成できます:
>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()
またはcreate関数を使用してモデルオブジェクトを作成し、1行のコードで保存する
>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")
これでクエリを実行できるようになりました
>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'
選択クエリにwhere句を追加しましょう
>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]
特定の書籍の著者についての詳細を知るには
>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'
Stephen Hawking(著者のルックアップブック)が出版したすべての書籍を入手するには、
>>> hawking.book_set.all()
[<Book: Book object>]
_set
は、逆引き参照に使用される表記法です。つまり、参照フィールドがBookモデル上にあるbook_set
に、authorオブジェクトのbook_set
を使用してすべての書籍を取得できます。
基本的なアンマネージテーブル。
Djangoを使用しているある時点で、すでに作成されたテーブルやデータベースビューと対話したいと思うかもしれません。このような場合、Djangoは移行によってテーブルを管理する必要はありません。これを設定するには、モデルのMeta
クラスにmanaged = False
変数を1つだけ追加する必要があります。
次に、データベースビューと対話する非管理モデルを作成する方法の例を示します。
class Dummy(models.Model):
something = models.IntegerField()
class Meta:
managed = False
これは、次のようにSQLで定義されたビューにマップできます。
CREATE VIEW myapp_dummy AS
SELECT id, something FROM complicated_table
WHERE some_complicated_condition = True
このモデルを作成したら、他のモデルと同じように使用できます。
>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]
高度なモデル
モデルは、オブジェクトに関するデータだけでなく、より多くの情報を提供することができます。例を見て、それが何の役に立つのかを見てみましょう:
from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Book(models.Model):
slug = models.SlugField()
title = models.CharField(max_length=128)
publish_date = models.DateField()
def get_absolute_url(self):
return reverse('library:book', kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Meta:
ordering = ['publish_date', 'title']
自動主キー
get_absolute_url
メソッドでself.pk
を使用していることに気付くかもしれません。 pk
フィールドは、モデルの主キーへのエイリアスです。また、もしdjangoが見つからなければ、自動的にプライマリキーを追加します。これは心配することが少なく、任意のモデルに外部キーを設定して簡単に取得できるようにします。
絶対URL
定義されている最初の関数はget_absolute_url
です。あなたが本を持っているなら、この方法で、あなたはURLタグ、解決、属性などを弄らずにリンクを得ることができます。単にbook.get_absolute_url
を呼び出すと、正しいリンクが得られます。ボーナスとして、django管理者のあなたのオブジェクトは、ボタン "サイト上のビュー"を取得します。
文字列表現
__str__
メソッドを使用すると、オブジェクトを表示する必要があるときにそのオブジェクトを使用できます。たとえば、以前の方法では、テンプレート内のブックへのリンクを追加することは、 <a href="{{ book.get_absolute_url }}">{{ book }}</a>
簡単です。ポイントまでまっすぐ。このメソッドは、管理者のドロップダウンに表示されているもの(例えば、外部キーなど)も制御します。
クラスデコレータでは、python 2で__str__
と__unicode__
両方__str__
メソッドを一度定義できますが、python 3では問題は発生しません。両方のバージョンでアプリケーションを実行することが期待される場合は、その方法です。
スラッグフィールド
スラッグフィールドはcharフィールドに似ていますが、シンボルは少ないです。デフォルトでは、文字、数字、アンダースコアまたはハイフンのみです。たとえば、urlのような素晴らしい表現を使ってオブジェクトを識別したい場合に便利です。
Metaクラス
Meta
クラスでは、アイテムのコレクション全体についてさらに多くの情報を定義できます。ここでは、既定の順序のみが設定されています。例えば、ListViewオブジェクトで便利です。ソートに使用するフィールドのリストが理想的です。ここでは、書籍は出版日によって最初にソートされ、日付が同じ場合はタイトルによってソートされます。
その他の頻出属性はverbose_name
とverbose_name_plural
です。デフォルトでは、モデルの名前から生成され、うまくいくはずです。しかし、複数形は単なる "s"を単数形に付け加えるだけで素朴で、場合によっては明示的に設定したいかもしれません。
計算値
モデルオブジェクトがフェッチされると、完全に実現されたクラスのインスタンスになります。そのため、フォームやシリアライザ(Django Rest Frameworkなど)で追加のメソッドにアクセスできます。
Pythonプロパティを使用すると、さまざまな状況によってデータベースに格納されない追加の値を表すためのエレガントな方法です。
def expire():
return timezone.now() + timezone.timedelta(days=7)
class Coupon(models.Model):
expiration_date = models.DateField(default=expire)
@property
def is_expired(self):
return timezone.now() > self.expiration_date
ほとんどの場合、クエリセットの注釈でデータを補うことができますが、モデルプロパティとしての計算値は、クエリの範囲内で単純に評価できない計算には理想的です。
さらに、プロパティはPythonクラスで宣言されており、スキーマの一部としては宣言されていないため、照会には使用できません。
モデルの文字列表現を追加する
モデルオブジェクトの人間が読めるプレゼンテーションを作成するには、 Model.__str__()
メソッド(またはPython2ではModel.__unicode__()
を実装する必要がありModel.__str__()
。このメソッドは、モデルのインスタンス上でstr()
を呼び出すたびに呼び出されstr()
モデルがテンプレート内で使用される場合など)。ここに例があります:
ブックモデルを作成します。
# your_app/models.py from django.db import models class Book(models.Model): name = models.CharField(max_length=50) author = models.CharField(max_length=50)
モデルのインスタンスを作成し、データベースに保存します。
>>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed') >>> himu_book.save()
インスタンスに対して
print()
を実行します。>>> print(himu_book) <Book: Book object>
<Book:Bookオブジェクト>は、デフォルトの出力であり、私たちの助けにはならない。これを修正するには、 __str__
メソッドを追加しましょう。
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.CharField(max_length=50)
def __str__(self):
return '{} by {}'.format(self.name, self.author)
注意python_2_unicode_compatible
デコレータはあなたのコードは、このデコレータコピーのpython 2と互換性を持つようにしたい場合にのみ必要です__str__
作成する方法__unicode__
方法を。 django.utils.encoding
からインポートしdjango.utils.encoding
。
print関数をbookインスタンスと呼びます:
>>> print(himu_book)
Himu Mama by Humayun Ahmed
ずっといい!
文字列表現は、モデルがForeignKeyField
およびManyToManyField
フィールドのModelForm
で使用される場合にも使用されます。
モデルミックスイン
同じケースでは、異なるモデルが製品のライフサイクルで同じフィールドと同じ手順を持つことができます。コードの反復継承を持たずにこれらの類似点を扱うことができます。クラス全体を継承する代わりに、 mixinのデザインパターンは、いくつかのメソッドと属性を継承する( または、いくつかのことを述べる )ことができます。例を見てみましょう:
class PostableMixin(models.Model):
class Meta:
abstract=True
sender_name = models.CharField(max_length=128)
sender_address = models.CharField(max_length=255)
receiver_name = models.CharField(max_length=128)
receiver_address = models.CharField(max_length=255)
post_datetime = models.DateTimeField(auto_now_add=True)
delivery_datetime = models.DateTimeField(null=True)
notes = models.TextField(max_length=500)
class Envelope(PostableMixin):
ENVELOPE_COMMERCIAL = 1
ENVELOPE_BOOKLET = 2
ENVELOPE_CATALOG = 3
ENVELOPE_TYPES = (
(ENVELOPE_COMMERCIAL, 'Commercial'),
(ENVELOPE_BOOKLET, 'Booklet'),
(ENVELOPE_CATALOG, 'Catalog'),
)
envelope_type = models.PositiveSmallIntegerField(choices=ENVELOPE_TYPES)
class Package(PostableMixin):
weight = models.DecimalField(max_digits=6, decimal_places=2)
width = models.DecimalField(max_digits=5, decimal_places=2)
height = models.DecimalField(max_digits=5, decimal_places=2)
depth = models.DecimalField(max_digits=5, decimal_places=2)
モデルを抽象クラスにするには、内部のMeta
クラスでabstract=True
と記述する必要があります。 Djangoは抽象モデル用のテーブルをデータベースに作成しません。ただし、 Envelope
とPackage
のモデルでは、対応するテーブルがデータベースに作成されます。
さらに、複数のモデルではいくつかのモデル方法が必要になります。したがって、コードの繰り返しを防ぐために、これらのメソッドをミックスインに追加することができます。たとえば、 PostableMixin
に配信日を設定するメソッドを作成すると、その両方の子からアクセスできます。
class PostableMixin(models.Model):
class Meta:
abstract=True
...
...
def set_delivery_datetime(self, dt=None):
if dt is None:
from django.utils.timezone import now
dt = now()
self.delivery_datetime = dt
self.save()
このメソッドは、子どもに対して以下のように使用できます。
>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()
>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()
UUID主キー
デフォルトでは、モデルは自動インクリメント(整数)主キーを使用します。これは、あなたに一連のキー1,2,3を与えるでしょう。
モデルに小さな変更を加えて、モデルに異なる主キータイプを設定できます。
UUIDはユニバーサルユニークな識別子です。これは32文字のランダム識別子でIDとして使用できます。 PostgreSQLで使用すると、uuidデータ型、それ以外の場合はchar(32)に格納されます。
import uuid
from django.db import models
class ModelUsingUUID(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
生成されたキーは7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47
形式になります
継承
モデル間の継承は2つの方法で行うことができます。
- 共通の抽象クラス(「モデルミックスイン」の例を参照)
- 複数のテーブルを持つ共通のモデル
マルチテーブルの継承は、共通フィールド用に1つのテーブルを作成し、子モデルごとに1つのテーブルを作成します。
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
2つのテーブルを作成します.1つはPlace
、もう1つはRestaurant
で、 OneToOne
フィールドを非表示にして共通フィールドにPlace
します。
これは、レストランオブジェクトを取得するたびにplacesテーブルに追加のクエリが必要になることに注意してください。