サーチ…


前書き

基本的なケースでは、モデルは単一のデータベーステーブルにマップされる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.9 on_deleteは2番目の位置引数として使用できるので、 django 2では必須の引数であり、直ちにそれを扱うことをお勧めします。古いバージョンでは、デフォルトのCASCADEます)。
  • publish_dateは日付を格納します。 nullblank両方がTrueに設定されて、必須フィールドではないことを示します(つまり、後で追加したり、空にしておくことができます)。

属性と一緒に__str__メソッドを定義すると、これは、デフォルトではなく、必要に応じてstring表現として使用される本のタイトルを返します。

変更をデータベースに適用する(移行)

新しいモデルを作成したり、既存のモデルを変更したりしたら、変更のためにマイグレーションを生成し、指定したデータベースにマイグレーションを適用する必要があります。これは、Djangoに組み込まれている移行システムを使用して行うことができます。プロジェクトのルートディレクトリにあるときにmanage.pyユーティリティを使用する:

python manage.py makemigrations <appname>

上記のコマンドは、アプリケーションのmigrationsサブディレクトリに必要な移行スクリプトを作成します。 <appname>パラメータを省略すると、 settings.pyINSTALLED_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と呼ばれるCustomerPizzaあり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 tablesJoinColumnIntersection 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_nameverbose_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()モデルがテンプレート内で使用される場合など)。ここに例があります:

  1. ブックモデルを作成します。

    # 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)
    
  2. モデルのインスタンスを作成し、データベースに保存します。

    >>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed')
    >>> himu_book.save()
    
  3. インスタンスに対して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は抽象モデル用のテーブルをデータベースに作成しません。ただし、 EnvelopePackageのモデルでは、対応するテーブルがデータベースに作成されます。

さらに、複数のモデルではいくつかのモデル方法が必要になります。したがって、コードの繰り返しを防ぐために、これらのメソッドをミックスインに追加することができます。たとえば、 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テーブルに追加のクエリが必要になることに注意してください。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow