モデル
モデルは、データに関する単一の決定的な情報源です。 これには、保存しているデータの重要なフィールドと動作が含まれています。 通常、各モデルは単一のデータベーステーブルにマップされます。
基礎:
- 各モデルは、 django.db.models.Model をサブクラス化するPythonクラスです。
- モデルの各属性は、データベースフィールドを表します。
- これらすべてにより、Djangoは自動生成されたデータベースアクセスAPIを提供します。 クエリの作成を参照してください。
簡単な例
このサンプルモデルは、first_name
とlast_name
を持つPerson
を定義します。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
first_name
とlast_name
は、モデルのフィールドです。 各フィールドはクラス属性として指定され、各属性はデータベース列にマップされます。
上記のPerson
モデルは、次のようなデータベーステーブルを作成します。
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
いくつかのテクニカルノート:
- テーブルの名前
myapp_person
は、一部のモデルメタデータから自動的に取得されますが、オーバーライドできます。 詳細については、テーブル名を参照してください。 id
フィールドは自動的に追加されますが、この動作はオーバーライドできます。 自動主キーフィールドを参照してください。- この例の
CREATE TABLE
SQLはPostgreSQL構文を使用してフォーマットされていますが、Djangoは設定ファイルで指定されたデータベースバックエンドに合わせたSQLを使用していることに注意してください。
モデルの使用
モデルを定義したら、それらのモデルを使用することをDjangoに伝える必要があります。 これを行うには、設定ファイルを編集し、:setting: `INSTALLED_APPS` 設定を変更して、models.py
を含むモジュールの名前を追加します。
たとえば、アプリケーションのモデルがモジュールに存在する場合myapp.models
(によってアプリケーション用に作成されたパッケージ構造 :djadmin: `manage.py startapp ` 脚本)、 :setting: `INSTALLED_APPS` 部分的に読む必要があります:
INSTALLED_APPS = [
#...
'myapp',
#...
]
新しいアプリを追加するとき :setting: `INSTALLED_APPS` 、必ず実行してください :djadmin: `manage.py移行 ` 、オプションで最初にそれらの移行を行う :djadmin: `manage.py makemigrations ` 。
田畑
モデルの最も重要な部分、そしてモデルの唯一の必要な部分は、モデルが定義するデータベースフィールドのリストです。 フィールドはクラス属性によって指定されます。 clean
、save
、delete
などのモデルAPI と競合するフィールド名を選択しないように注意してください。
例:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
フィールドタイプ
モデルの各フィールドは、適切な Field クラスのインスタンスである必要があります。 Djangoは、フィールドクラスタイプを使用していくつかのことを決定します。
- データベースに保存するデータの種類を指示する列タイプ(例:
INTEGER
、VARCHAR
、TEXT
)。 - フォームフィールドをレンダリングするときに使用するデフォルトのHTML widget (例:
<input type="text">
、<select>
)。 - Djangoの管理者および自動生成されたフォームで使用される最小限の検証要件。
Djangoには、数十の組み込みフィールドタイプが付属しています。 完全なリストは、モデルフィールドリファレンスにあります。 Djangoの組み込みフィールドでうまくいかない場合は、独自のフィールドを簡単に作成できます。 カスタムモデルフィールドの書き込みを参照してください。
フィールドオプション
各フィールドは、フィールド固有の引数の特定のセットを取ります(モデルフィールドリファレンスに記載されています)。 たとえば、 CharField (およびそのサブクラス)には、データの格納に使用されるVARCHAR
データベースフィールドのサイズを指定する max_length 引数が必要です。
すべてのフィールドタイプで使用できる一連の共通引数もあります。 すべてオプションです。 それらはリファレンスで完全に説明されていますが、最も頻繁に使用されるものの簡単な要約は次のとおりです。
null
True
の場合、Djangoは空の値をNULL
としてデータベースに保存します。 デフォルトはFalse
です。blank
True
の場合、フィールドは空白にすることができます。 デフォルトはFalse
です。これは null とは異なることに注意してください。 null は純粋にデータベース関連ですが、 blank は検証関連です。 フィールドに blank = True がある場合、フォームの検証では空の値を入力できます。 フィールドに blank = False がある場合、そのフィールドは必須です。
choices
このフィールドの選択肢として使用する2タプルのシーケンス。 これが指定されている場合、デフォルトのフォームウィジェットは、標準のテキストフィールドではなく選択ボックスになり、指定された選択肢に選択肢が制限されます。
選択肢リストは次のようになります。
YEAR_IN_SCHOOL_CHOICES = [ ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), ]
ノート
choices
の順序が変更されるたびに、新しい移行が作成されます。各タプルの最初の要素は、データベースに格納される値です。 2番目の要素は、フィールドのフォームウィジェットによって表示されます。
モデルインスタンスを指定すると、
choices
のフィールドの表示値には、 get_FOO_display()メソッドを使用してアクセスできます。 例えば:from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
列挙型クラスを使用して、
choices
を簡潔に定義することもできます。from django.db import models class Runner(models.Model): MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE') name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
その他の例は、モデルフィールドリファレンスにあります。
default
フィールドのデフォルト値。 これは、値または呼び出し可能オブジェクトにすることができます。 呼び出し可能であれば、新しいオブジェクトが作成されるたびに呼び出されます。
help_text
フォームウィジェットで表示される追加の「ヘルプ」テキスト。 フィールドがフォームで使用されていない場合でも、ドキュメントに役立ちます。
primary_key
True
の場合、このフィールドはモデルの主キーです。モデルのどのフィールドにも primary_key = True を指定しない場合、Djangoは主キーを保持するために IntegerField を自動的に追加するため、を設定する必要はありません。デフォルトの主キーの動作をオーバーライドする場合を除き、任意のフィールドで] primary_key = True 。 詳細については、自動主キーフィールドを参照してください。
主キーフィールドは読み取り専用です。 既存のオブジェクトの主キーの値を変更して保存すると、古いオブジェクトと一緒に新しいオブジェクトが作成されます。 例えば:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple') >>> fruit.name = 'Pear' >>> fruit.save() >>> Fruit.objects.values_list('name', flat=True) <QuerySet ['Apple', 'Pear']>
unique
True
の場合、このフィールドはテーブル全体で一意である必要があります。
繰り返しますが、これらは最も一般的なフィールドオプションの簡単な説明です。 詳細については、共通モデルフィールドオプションリファレンスを参照してください。
自動主キーフィールド
デフォルトでは、Djangoは各モデルに AppConfig.default_auto_field で、またはグローバルに:setting: `DEFAULT_AUTO_FIELD` 設定でアプリごとに指定されたタイプの自動インクリメント主キーを提供します。 例えば:
id = models.BigAutoField(primary_key=True)
カスタム主キーを指定する場合は、フィールドの1つに primary_key = True を指定します。 Djangoは、 Field.primary_key を明示的に設定したことを確認した場合、自動id
列を追加しません。
各モデルには、 primary_key = True (明示的に宣言されているか自動的に追加されている)を持つために正確に1つのフィールドが必要です。
バージョン3.2での変更:古いバージョンでは、自動作成された主キーフィールドは常に AutoField でした。
冗長なフィールド名
ForeignKey 、 ManyToManyField 、 OneToOneField を除く各フィールドタイプは、オプションの最初の位置引数(詳細名)を取ります。 詳細な名前が指定されていない場合、Djangoはフィールドの属性名を使用して自動的に作成し、アンダースコアをスペースに変換します。
この例では、詳細な名前は"person's first name"
です。
first_name = models.CharField("person's first name", max_length=30)
この例では、詳細な名前は"first name"
です。
first_name = models.CharField(max_length=30)
ForeignKey 、 ManyToManyField 、および OneToOneField では、最初の引数がモデルクラスである必要があるため、 verbose_name キーワード引数を使用します。
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)
慣例では、 verbose_name の最初の文字を大文字にすることはありません。 Djangoは、必要な場所で最初の文字を自動的に大文字にします。
関係
明らかに、リレーショナルデータベースの力は、テーブルを相互に関連付けることにあります。 Djangoは、多対1、多対多、1対1の3つの最も一般的なタイプのデータベース関係を定義する方法を提供します。
多対1の関係
多対1の関係を定義するには、 django.db.models.ForeignKey を使用します。 他の Field タイプと同じように、モデルのクラス属性として含めることで使用します。
ForeignKey には、位置引数(モデルが関連するクラス)が必要です。
たとえば、Car
モデルにManufacturer
がある場合、つまり、Manufacturer
は複数の車を製造しますが、各Car
には1つのManufacturer
しかありません。 ] –次の定義を使用します。
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
再帰的関係(それ自体と多対1の関係を持つオブジェクト)およびまだ定義されていないモデルとの関係を作成することもできます。 詳細については、モデルフィールドリファレンスを参照してください。
ForeignKey フィールドの名前(上記の例ではmanufacturer
)は、モデルの名前を小文字にすることをお勧めしますが、必須ではありません。 フィールドは好きなように呼び出すことができます。 例えば:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
も参照してください
ForeignKey フィールドは、モデルフィールドリファレンスで説明されているいくつかの追加の引数を受け入れます。 これらのオプションは、関係がどのように機能するかを定義するのに役立ちます。 すべてオプションです。
後方関連オブジェクトへのアクセスの詳細については、次の関係後方の例を参照してください。
サンプルコードについては、多対1関係モデルの例を参照してください。
多対多の関係
多対多の関係を定義するには、 ManyToManyField を使用します。 他の Field タイプと同じように、モデルのクラス属性として含めることで使用します。
ManyToManyField には、位置引数(モデルが関連するクラス)が必要です。
たとえば、Pizza
に複数のTopping
オブジェクトがある場合、つまりTopping
が複数のピザにあり、各Pizza
に複数のトッピングがある場合-方法は次のとおりです。あなたはそれを表すでしょう:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
ForeignKey と同様に、再帰的関係(それ自体と多対多の関係を持つオブジェクト)およびまだ定義されていないモデルとの関係を作成することもできます。
ManyToManyField (上記の例ではtoppings
)の名前は、関連するモデルオブジェクトのセットを表す複数形にすることをお勧めしますが、必須ではありません。
どのモデルに ManyToManyField があるかは関係ありませんが、両方ではなく、一方のモデルにのみ配置する必要があります。
通常、 ManyToManyField インスタンスは、フォームで編集されるオブジェクトに配置する必要があります。 上記の例では、toppings
はPizza
にあります(Topping
がpizzas
ManyToManyField を持っているのではなく)トッピングが複数のピザにあるよりもトッピングがあるピザについて。 上記の設定方法では、Pizza
フォームでユーザーがトッピングを選択できます。
ManyToManyField フィールドは、モデルフィールドリファレンスで説明されているいくつかの追加の引数も受け入れます。 これらのオプションは、関係がどのように機能するかを定義するのに役立ちます。 すべてオプションです。
多対多の関係に関する追加のフィールド
ピザやトッピングの混合やマッチングなど、多対多の関係のみを扱う場合は、標準の ManyToManyField で十分です。 ただし、データを2つのモデル間の関係に関連付ける必要がある場合があります。
たとえば、ミュージシャンが属する音楽グループを追跡するアプリケーションの場合を考えてみます。 人とそのメンバーであるグループの間には多対多の関係があるため、 ManyToManyField を使用してこの関係を表すことができます。 ただし、その人がグループに参加した日付など、収集する可能性のあるメンバーシップについては多くの詳細があります。
このような状況では、Djangoでは多対多の関係を管理するために使用されるモデルを指定できます。 次に、中間モデルに追加のフィールドを配置できます。 中間モデルは、から引数を使用して、 ManyToManyField に関連付けられ、中間として機能するモデルを指します。 ミュージシャンの例では、コードは次のようになります。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
中間モデルを設定するときは、多対多の関係に関係するモデルに外部キーを明示的に指定します。 この明示的な宣言は、2つのモデルがどのように関連しているかを定義します。
中間モデルにはいくつかの制限があります。
- 中間モデルには、ソースモデルへの外部キーが1つ(のみ)含まれている必要があります(この例では
Group
になります)。または、Djangoが使用する外部キーを明示的に指定する必要があります。 ManyToManyField.through_fields を使用した関係。 複数の外部キーがあり、through_fields
が指定されていない場合、検証エラーが発生します。 同様の制限がターゲットモデルの外部キーに適用されます(この例ではPerson
になります)。 - 中間モデルを介してそれ自体と多対多の関係を持つモデルの場合、同じモデルへの2つの外部キーが許可されますが、それらは多対多の関係の2つの(異なる)側として扱われます。 ただし、2つよりも多いがある場合は、上記のように
through_fields
も指定する必要があります。そうしないと、検証エラーが発生します。
中間モデル(この場合はMembership
)を使用するように ManyToManyField を設定したので、多対多の関係を作成する準備が整いました。 これを行うには、中間モデルのインスタンスを作成します。
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
through_defaults
を指定している限り、 add()、 create()、または set()を使用して関係を作成することもできます。必須フィールド:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
中間モデルのインスタンスを直接作成することをお勧めします。
中間モデルによって定義されたカスタムスルーテーブルが(model1, model2)
ペアに一意性を強制せず、複数の値を許可する場合、 remove()呼び出しはすべての中間モデルインスタンスを削除します。
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
clear()メソッドを使用して、インスタンスのすべての多対多の関係を削除できます。
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
多対多の関係を確立したら、クエリを発行できます。 通常の多対多の関係と同様に、多対多関連モデルの属性を使用してクエリを実行できます。
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>
中間モデルを使用しているので、その属性を照会することもできます。
# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
... group__name='The Beatles',
... membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>
メンバーシップの情報にアクセスする必要がある場合は、Membership
モデルに直接クエリを実行してアクセスできます。
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
同じ情報にアクセスする別の方法は、Person
オブジェクトから多対多の逆の関係を照会することです。
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
1対1の関係
1対1の関係を定義するには、 OneToOneField を使用します。 他のField
タイプと同じように、モデルのクラス属性として含めることで使用します。
これは、オブジェクトが何らかの方法で別のオブジェクトを「拡張」するときに、オブジェクトの主キーで最も役立ちます。
OneToOneField には、位置引数(モデルが関連するクラス)が必要です。
たとえば、「場所」のデータベースを構築する場合、住所、電話番号などのかなり標準的なものを構築します。 データベース内。 次に、場所の上にレストランのデータベースを構築したい場合は、繰り返してRestaurant
モデルでそれらのフィールドを複製する代わりに、Restaurant
に OneToOneFieldを持たせることができます。 からPlace
(レストランは「場所」であるため、実際、これを処理するには、通常、継承を使用します。これには暗黙の1対1の関係が含まれます) 。
ForeignKey と同様に、漸化式を定義でき、まだ定義されていないモデルへの参照を作成できます。
OneToOneField フィールドは、オプションの parent_link 引数も受け入れます。
OneToOneField クラスは、モデルの主キーになるために使用されていました。 これはもはや真実ではありません(ただし、必要に応じて primary_key 引数を手動で渡すことができます)。 したがって、1つのモデルに OneToOneField タイプの複数のフィールドを含めることができるようになりました。
ファイル全体のモデル
モデルを別のアプリのモデルに関連付けることはまったく問題ありません。 これを行うには、モデルが定義されているファイルの先頭にある関連モデルをインポートします。 次に、必要に応じて他のモデルクラスを参照します。 例えば:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
フィールド名の制限
Djangoは、モデルフィールド名にいくつかの制限を課しています。
フィールド名をPythonの予約語にすることはできません。これは、Pythonの構文エラーが発生するためです。 例えば:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
Djangoのクエリルックアップ構文の動作方法により、フィールド名に1行に複数のアンダースコアを含めることはできません。 例えば:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
同様の理由で、フィールド名をアンダースコアで終わらせることはできません。
ただし、フィールド名は必ずしもデータベースの列名と一致する必要はないため、これらの制限は回避できます。 db_column オプションを参照してください。
join
、where
、select
、などのSQL予約語は、モデルフィールド名として許可されます。これは、Djangoがすべてのデータベーステーブル名と列名をエスケープするためです。基礎となるすべてのSQLクエリで。 特定のデータベースエンジンの引用構文を使用します。
カスタムフィールドタイプ
既存のモデルフィールドの1つを目的に合わせて使用できない場合、またはあまり一般的ではないデータベース列タイプを利用したい場合は、独自のフィールドクラスを作成できます。 独自のフィールドの作成に関する完全な説明は、カスタムモデルフィールドの作成で提供されています。
Metaオプション
次のように、内部class Meta
を使用してモデルメタデータを提供します。
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
モデルメタデータは、順序付けオプション(順序付け)、データベーステーブル名( db_table )、人間が読める形式の単数形および複数形の名前()など、「フィールドではないもの」です。 verbose_name および verbose_name_plural )。 何も必要ありません。モデルへのclass Meta
の追加は完全にオプションです。
可能なすべてのMeta
オプションの完全なリストは、モデルオプションリファレンスにあります。
モデル属性
objects
- モデルの最も重要な属性はマネージャーです。 これは、データベースクエリ操作がDjangoモデルに提供されるためのインターフェースであり、データベースからインスタンスを取得するために使用されます。 カスタム
Manager
が定義されていない場合、デフォルト名はオブジェクトです。 マネージャーには、モデルインスタンスではなく、モデルクラスを介してのみアクセスできます。
モデルメソッド
モデルにカスタムメソッドを定義して、カスタムの「行レベル」機能をオブジェクトに追加します。 Manager メソッドは「テーブル全体」の処理を目的としていますが、モデルメソッドは特定のモデルインスタンスに作用する必要があります。
これは、ビジネスロジックを1つの場所(モデル)に保持するための貴重な手法です。
たとえば、このモデルにはいくつかのカスタムメソッドがあります。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
この例の最後のメソッドはプロパティです。
モデルインスタンスリファレンスには、各モデルに自動的に与えられるメソッドの完全なリストがあります。 これらのほとんどをオーバーライドできます-以下の事前定義されたモデルメソッドのオーバーライドを参照してください-しかし、ほとんどの場合、定義したいものがいくつかあります。
__str__()
任意のオブジェクトの文字列表現を返すPythonの「マジックメソッド」。 これは、モデルインスタンスを強制してプレーンな文字列として表示する必要がある場合にPythonとDjangoが使用するものです。 最も注目すべきは、これは、インタラクティブコンソールまたは管理者にオブジェクトを表示するときに発生します。
このメソッドは常に定義する必要があります。 デフォルトはまったく役に立ちません。
get_absolute_url()
これは、オブジェクトのURLを計算する方法をDjangoに指示します。 Djangoはこれを管理インターフェースで使用し、オブジェクトのURLを把握する必要があるときはいつでも使用します。
一意に識別するURLを持つオブジェクトはすべて、このメソッドを定義する必要があります。
事前定義されたモデルメソッドのオーバーライド
カスタマイズしたいデータベースの動作の束をカプセル化するモデルメソッドの別のセットがあります。 特に、 save()と delete()の動作方法を変更したい場合がよくあります。
これらのメソッド(およびその他のモデルメソッド)を自由にオーバーライドして、動作を変更できます。
組み込みメソッドをオーバーライドするための典型的なユースケースは、オブジェクトを保存するたびに何かを発生させたい場合です。 例(受け入れるパラメーターのドキュメントについては、 save()を参照してください):
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
保存を防ぐこともできます。
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(*args, **kwargs) # Call the "real" save() method.
オブジェクトが引き続きデータベースに保存されるようにするには、スーパークラスメソッド(super().save(*args, **kwargs)
ビジネス)を呼び出すことを忘れないでください。 スーパークラスメソッドの呼び出しを忘れた場合、デフォルトの動作は発生せず、データベースは変更されません。
モデルメソッドに渡すことができる引数を渡すことも重要です。これは、*args, **kwargs
ビットが行うことです。 Djangoは、組み込みのモデルメソッドの機能を随時拡張し、新しい引数を追加します。 メソッド定義で*args, **kwargs
を使用すると、それらの引数が追加されたときに、コードがそれらの引数を自動的にサポートすることが保証されます。
オーバーライドされたモデルメソッドは、一括操作では呼び出されません
オブジェクトの delete()メソッドは、 QuerySet を使用してオブジェクトを一括削除する場合、またはカスケード削除の結果として、必ずしも呼び出されるとは限らないことに注意してください。 カスタマイズされた削除ロジックが確実に実行されるようにするには、 pre_delete および/または post_delete シグナルを使用できます。
残念ながら、 save()、 pre_save 、および pre_save のいずれも存在しないため、オブジェクトを一括で作成または更新する場合の回避策はありません。 post_save が呼び出されます。
カスタムSQLの実行
もう1つの一般的なパターンは、モデルメソッドとモジュールレベルのメソッドでカスタムSQLステートメントを作成することです。 raw SQLの使用の詳細については、 raw SQL の使用に関するドキュメントを参照してください。
モデルの継承
Djangoでのモデル継承は、Pythonでの通常のクラス継承とほぼ同じように機能しますが、ページの冒頭にある基本に従う必要があります。 つまり、基本クラスは django.db.models.Model をサブクラス化する必要があります。
あなたがしなければならない唯一の決定は、親モデルをそれ自体で(独自のデータベーステーブルを持つ)モデルにするか、それとも親が子モデルを通してのみ表示される共通情報の単なる保有者であるかどうかです。
Djangoで可能な継承には3つのスタイルがあります。
- 多くの場合、親クラスを使用して、子モデルごとに入力する必要のない情報を保持する必要があります。 このクラスは単独で使用されることはないので、抽象基本クラスが必要です。
- 既存のモデル(おそらく完全に別のアプリケーションからのもの)をサブクラス化し、各モデルに独自のデータベーステーブルを持たせたい場合は、マルチテーブル継承が最適です。
- 最後に、モデルフィールドを変更せずに、モデルのPythonレベルの動作のみを変更する場合は、プロキシモデルを使用できます。
抽象基本クラス
抽象基本クラスは、いくつかの共通情報を他の多くのモデルに入れたい場合に役立ちます。 基本クラスを作成し、abstract=True
を Meta クラスに配置します。 このモデルは、データベーステーブルの作成には使用されません。 代わりに、他のモデルの基本クラスとして使用される場合、そのフィールドは子クラスのフィールドに追加されます。
例:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Student
モデルには、name
、age
、home_group
の3つのフィールドがあります。 CommonInfo
モデルは抽象基本クラスであるため、通常のDjangoモデルとして使用することはできません。 データベーステーブルを生成したり、マネージャを持ったりすることはなく、インスタンス化したり直接保存したりすることはできません。
抽象基本クラスから継承されたフィールドは、別のフィールドまたは値でオーバーライドするか、None
で削除できます。
多くの用途で、このタイプのモデル継承はまさにあなたが望むものになります。 これは、Pythonレベルで一般的な情報を除外する方法を提供しますが、データベースレベルでは子モデルごとに1つのデータベーステーブルのみを作成します。
Meta継承
抽象基本クラスが作成されると、Djangoは基本クラスで宣言した Meta 内部クラスを属性として使用できるようにします。 子クラスが独自の Meta クラスを宣言していない場合、子クラスは親の Meta を継承します。 子が親の Meta クラスを拡張したい場合は、それをサブクラス化できます。 例えば:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
Djangoは、抽象基本クラスの Meta クラスに1つの調整を行います。 Meta 属性をインストールする前に、abstract=False
を設定します。 これは、抽象基本クラスの子が自動的に抽象クラスにならないことを意味します。 別の抽象基本クラスを継承する抽象基本クラスを作成するには、子にabstract=True
を明示的に設定する必要があります。
一部の属性は、抽象基本クラスの Meta クラスに含める意味がありません。 たとえば、db_table
を含めると、すべての子クラス(独自の Meta を指定しないクラス)が同じデータベーステーブルを使用することになりますが、これはほぼ確実に必要なものではありません。 。
Python継承の仕組みにより、子クラスが複数の抽象基本クラスから継承する場合、デフォルトでは、最初にリストされたクラスの Meta オプションのみが継承されます。 複数の抽象基本クラスから Meta オプションを継承するには、 Meta 継承を明示的に宣言する必要があります。 例えば:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ['name']
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
マルチテーブル継承
Djangoでサポートされている2番目のタイプのモデル継承は、階層内の各モデルがそれ自体でモデルである場合です。 各モデルは独自のデータベーステーブルに対応しており、個別にクエリおよび作成できます。 継承関係により、子モデルとその各親の間にリンクが導入されます(自動的に作成された OneToOneField を介して)。 例えば:
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)
Place
のすべてのフィールドは、Restaurant
でも使用できますが、データは別のデータベーステーブルにあります。 したがって、これらは両方とも可能です。
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
Restaurant
でもあるPlace
がある場合は、モデルの小文字バージョンを使用して、Place
オブジェクトからRestaurant
オブジェクトに移動できます。名前:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
ただし、上記の例のp
がではなく Restaurant
の場合(Place
オブジェクトとして直接作成されたか、他のオブジェクトの親でした)クラス)、p.restaurant
を参照すると、Restaurant.DoesNotExist
例外が発生します。
Restaurant
に自動的に作成された OneToOneField は、Place
にリンクしています。次のようになります。
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Restaurant
で parent_link = True を使用して、独自の OneToOneField を宣言することにより、そのフィールドをオーバーライドできます。
Metaおよびマルチテーブル継承
マルチテーブル継承の状況では、子クラスがその親の Meta クラスから継承することは意味がありません。 すべての Meta オプションはすでに親クラスに適用されており、それらを再度適用すると、通常は矛盾した動作になります(これは、基本クラスがに存在しない抽象基本クラスの場合とは対照的です。それ自体の権利)。
したがって、子モデルはその親の Meta クラスにアクセスできません。 ただし、子が親から動作を継承する場合がいくつかあります。子が ordering 属性または get_latest_by 属性を指定しない場合、子はこれらを親から継承します。 。
親に順序があり、子に自然順序を付けたくない場合は、明示的に無効にすることができます。
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
相続と逆の関係
マルチテーブル継承は暗黙の OneToOneField を使用して子と親をリンクするため、上記の例のように、親から子に移動することができます。 ただし、これにより、 ForeignKey および ManyToManyField リレーションのデフォルトの related_name 値である名前が使用されます。 これらのタイプのリレーションを親モデルのサブクラスに配置する場合は、そのような各フィールドに related_name 属性を指定する必要があります。 忘れると、Djangoは検証エラーを発生させます。
たとえば、上記のPlace
クラスを再度使用して、 ManyToManyField を使用して別のサブクラスを作成しましょう。
class Supplier(Place):
customers = models.ManyToManyField(Place)
これにより、エラーが発生します。
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.
HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
次のようにrelated_name
をcustomers
フィールドに追加すると、エラーが解決されます:models.ManyToManyField(Place, related_name='provider')
。
親リンクフィールドの指定
前述のように、Djangoは自動的に OneToOneField を作成し、子クラスを非抽象親モデルにリンクします。 親にリンクしている属性の名前を制御する場合は、独自の OneToOneField を作成し、 parent_link = True を設定して、フィールドが親クラス。
プロキシモデル
マルチテーブル継承を使用すると、モデルのサブクラスごとに新しいデータベーステーブルが作成されます。 サブクラスには、基本クラスに存在しない追加のデータフィールドを格納する場所が必要なため、これは通常、望ましい動作です。 ただし、モデルのPythonの動作のみを変更したい場合もあります。おそらく、デフォルトのマネージャーを変更したり、新しいメソッドを追加したりするためです。
これがプロキシモデルの継承の目的です。元のモデルのプロキシを作成します。 プロキシモデルのインスタンスを作成、削除、更新できます。すべてのデータは、元の(プロキシされていない)モデルを使用しているかのように保存されます。 違いは、元のモデルを変更することなく、プロキシ内のデフォルトのモデルの順序やデフォルトのマネージャーなどを変更できることです。
プロキシモデルは、通常のモデルと同様に宣言されます。 Meta
クラスの proxy 属性をTrue
に設定することで、プロキシモデルであることをDjangoに伝えます。
たとえば、Person
モデルにメソッドを追加するとします。 あなたはこのようにそれを行うことができます:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
MyPerson
クラスは、その親Person
クラスと同じデータベーステーブルで動作します。 特に、Person
の新しいインスタンスには、MyPerson
からもアクセスでき、その逆も同様です。
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
プロキシモデルを使用して、モデルに異なるデフォルトの順序を定義することもできます。 Person
モデルを常に注文する必要はないかもしれませんが、プロキシを使用する場合は、last_name
属性で定期的に注文してください。
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
これで、通常のPerson
クエリは順序付けられなくなり、OrderedPerson
クエリはlast_name
によって順序付けられます。
プロキシモデルは、通常のモデルと同じ方法で、Meta
属性を継承します。
QuerySetは引き続き要求されたモデルを返します
たとえば、Person
オブジェクトをクエリするたびに、DjangoがMyPerson
オブジェクトを返すようにする方法はありません。 Person
オブジェクトのクエリセットは、これらのタイプのオブジェクトを返します。 プロキシオブジェクトの要点は、元のPerson
に依存するコードがそれらを使用し、独自のコードが含まれている拡張機能を使用できることです(他のコードはとにかく依存していません)。 Person
(またはその他の)モデルをどこでも自分で作成したものに置き換える方法ではありません。
基本クラスの制限
プロキシモデルは、1つの非抽象モデルクラスから継承する必要があります。 プロキシモデルは異なるデータベーステーブルの行間の接続を提供しないため、複数の非抽象モデルから継承することはできません。 プロキシモデルは、モデルフィールドを定義しない場合に限り、任意の数の抽象モデルクラスから継承できます。 プロキシモデルは、共通の非抽象親クラスを共有する任意の数のプロキシモデルから継承することもできます。
プロキシモデルマネージャー
プロキシモデルでモデルマネージャーを指定しない場合、モデルの親からマネージャーを継承します。 プロキシモデルでマネージャーを定義すると、それがデフォルトになりますが、親クラスで定義されたマネージャーは引き続き使用できます。
上記の例を続けると、Person
モデルをクエリするときに使用されるデフォルトのマネージャーを次のように変更できます。
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
既存のデフォルトを置き換えずに新しいマネージャーをプロキシに追加する場合は、カスタムマネージャーのドキュメントで説明されている手法を使用できます。新しいマネージャーを含む基本クラスを作成し、プライマリの後に継承します。基本クラス:
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
おそらくこれを頻繁に行う必要はないでしょうが、そうすれば可能です。
プロキシ継承とアンマネージモデルの違い
プロキシモデルの継承は、モデルのMeta
クラスで managed 属性を使用して、アンマネージモデルを作成するのとかなり似ている場合があります。
Meta.db_table を注意深く設定すると、既存のモデルをシャドウイングしてPythonメソッドを追加するアンマネージドモデルを作成できます。 ただし、変更を加えた場合は両方のコピーの同期を維持する必要があるため、これは非常に反復的で脆弱です。
一方、プロキシモデルは、プロキシするモデルとまったく同じように動作することを目的としています。 それらはフィールドとマネージャーを直接継承するため、常に親モデルと同期しています。
一般的なルールは次のとおりです。
- 既存のモデルまたはデータベーステーブルをミラーリングしていて、元のデータベーステーブルの列をすべて必要としない場合は、
Meta.managed=False
を使用します。 このオプションは通常、Djangoの制御下にないデータベースビューとテーブルのモデリングに役立ちます。 - モデルのPythonのみの動作を変更したいが、元のフィールドと同じフィールドをすべて保持したい場合は、
Meta.proxy=True
を使用します。 これにより、データが保存されるときにプロキシモデルが元のモデルのストレージ構造の正確なコピーになるように設定されます。
多重継承
Pythonのサブクラス化と同様に、Djangoモデルが複数の親モデルから継承する可能性があります。 通常のPython名前解決ルールが適用されることに注意してください。 特定の名前を持つ最初の基本クラス(例: Meta )が表示され、使用されるものになります。 たとえば、これは、複数の親に Meta クラスが含まれている場合、最初のクラスのみが使用され、他のすべては無視されることを意味します。
通常、複数の親から継承する必要はありません。 これが役立つ主なユースケースは、「ミックスイン」クラスの場合です。つまり、ミックスインを継承するすべてのクラスに特定のフィールドまたはメソッドを追加します。 特定の情報がどこから来ているのかを理解するのに苦労する必要がないように、継承階層をできるだけ単純でわかりやすいものに保つようにしてください。
共通のid
主キーフィールドを持つ複数のモデルから継承すると、エラーが発生することに注意してください。 多重継承を適切に使用するには、基本モデルで明示的な AutoField を使用できます。
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
...
class BookReview(Book, Article):
pass
または、共通の祖先を使用して AutoField を保持します。 これには、子によって自動的に生成および継承されるフィールド間の衝突を回避するために、各親モデルから共通の祖先への明示的な OneToOneField を使用する必要があります。
class Piece(models.Model):
pass
class Article(Piece):
article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
...
class Book(Piece):
book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
...
class BookReview(Book, Article):
pass
フィールド名「非表示」は許可されていません
通常のPythonクラスの継承では、子クラスが親クラスの属性をオーバーライドすることが許可されています。 Djangoでは、これは通常、モデルフィールドでは許可されていません。 非抽象モデルの基本クラスにauthor
というフィールドがある場合、その基本クラスから継承するクラスで、別のモデルフィールドを作成したり、author
という属性を定義したりすることはできません。
この制限は、抽象モデルから継承されたモデルフィールドには適用されません。 このようなフィールドは、別のフィールドまたは値でオーバーライドするか、field_name = None
を設定することで削除できます。
警告
モデルマネージャは、抽象基本クラスから継承されます。 継承された Manager によって参照される継承されたフィールドをオーバーライドすると、微妙なバグが発生する可能性があります。 カスタムマネージャーとモデル継承を参照してください。
ノート
一部のフィールドは、モデルに追加の属性を定義します。 ForeignKey は、フィールド名に_id
が追加された追加の属性と、外部モデルのrelated_name
およびrelated_query_name
を定義します。
これらの追加の属性は、それを定義するフィールドが変更または削除されて、追加の属性が定義されないようにしない限り、オーバーライドできません。
親モデルのフィールドをオーバーライドすると、新しいインスタンスの初期化(Model.__init__
で初期化されるフィールドの指定)やシリアル化などの領域で問題が発生します。 これらは通常のPythonクラス継承がまったく同じ方法で処理する必要がない機能であるため、Djangoモデル継承とPythonクラス継承の違いは恣意的ではありません。
この制限は、フィールドインスタンスである属性にのみ適用されます。 必要に応じて、通常のPython属性をオーバーライドできます。 また、Pythonが認識している属性の名前にのみ適用されます。データベースの列名を手動で指定している場合、マルチテーブル継承の子モデルと祖先モデルの両方に同じ列名を表示できます(これらは列です)。 2つの異なるデータベーステーブル内)。
祖先モデルのモデルフィールドをオーバーライドすると、Djangoは FieldError を発生させます。
パッケージ内のモデルの整理
NS :djadmin: `manage.py startapp ` コマンドは、を含むアプリケーション構造を作成しますmodels.py
ファイル。 多くのモデルがある場合は、それらを別々のファイルに整理すると便利な場合があります。
これを行うには、models
パッケージを作成します。 models.py
を削除し、__init__.py
ファイルとモデルを保存するファイルを含むmyapp/models/
ディレクトリを作成します。 __init__.py
ファイルにモデルをインポートする必要があります。
たとえば、models
ディレクトリにorganic.py
とsynthetic.py
がある場合:
from .organic import Person
from .synthetic import Robot
from .models import *
を使用せずに各モデルを明示的にインポートすると、名前空間が乱雑にならず、コードが読みやすくなり、コード分析ツールが便利になるという利点があります。