カスタムモデルフィールドの作成—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/howto/custom-model-fields
移動先:案内検索

カスタムモデルフィールドの作成

序章

モデルリファレンスのドキュメントでは、Djangoの標準フィールドクラス( CharFieldDateField など)の使用方法について説明しています。 多くの目的のために、それらのクラスはあなたが必要とするすべてです。 ただし、Djangoバージョンが正確な要件を満たしていない場合や、Djangoに付属しているものとはまったく異なるフィールドを使用したい場合があります。

Djangoの組み込みフィールドタイプは、考えられるすべてのデータベース列タイプをカバーしているわけではなく、VARCHARINTEGERなどの一般的なタイプのみをカバーしています。 地理ポリゴンなどのよりあいまいな列タイプ、または PostgreSQLカスタムタイプなどのユーザー作成タイプについては、独自のDjango Fieldサブクラスを定義できます。

または、標準のデータベース列タイプに収まるようにシリアル化できる複雑なPythonオブジェクトがある場合もあります。 これは、Fieldサブクラスがモデルでオブジェクトを使用するのに役立つもう1つのケースです。

サンプルオブジェクト

カスタムフィールドを作成するには、細部に少し注意を払う必要があります。 わかりやすくするために、このドキュメント全体で一貫した例を使用します。 Bridge の手札でカードの取引を表すPythonオブジェクトをラップします。 心配しないでください。この例に従うためにBridgeのプレイ方法を知る必要はありません。 52枚のカードが、伝統的に西と呼ばれる4人のプレーヤーに均等に配られることを知っておく必要があります。 ]。 私たちのクラスは次のようになります。

class Hand:
    """A hand of cards (bridge style)"""

    def __init__(self, north, east, south, west):
        # Input parameters are lists of cards ('Ah', '9s', etc.)
        self.north = north
        self.east = east
        self.south = south
        self.west = west

    # ... (other possibly useful methods omitted) ...

これは通常のPythonクラスであり、Django固有のものはありません。 モデルでこのようなことを実行できるようにしたいと思います(モデルのhand属性はHandのインスタンスであると想定しています)。

example = MyModel.objects.get(pk=1)
print(example.hand.north)

new_hand = Hand(north, east, south, west)
example.hand = new_hand
example.save()

他のPythonクラスと同じように、モデルのhand属性に割り当てて取得します。 秘訣は、そのようなオブジェクトの保存と読み込みを処理する方法をDjangoに指示することです。

モデルでHandクラスを使用するために、このクラスを変更する必要はありません。 これは、ソースコードを変更できない既存のクラスのモデルサポートを簡単に記述できることを意味するため、理想的です。

ノート

カスタムデータベースの列タイプを利用して、モデルの標準Pythonタイプとしてデータを処理したいだけかもしれません。 たとえば、文字列、またはフロート。 このケースは、Handの例に似ており、違いに注意していきます。


背景理論

データベースストレージ

モデルフィールドから始めましょう。 分解すると、モデルフィールドは、通常のPythonオブジェクト(文字列、ブール値、datetime、またはHandなどのより複雑なもの)を取得して、フォーマットとの間で変換する方法を提供します。これは、データベースを扱うときに役立ちます。 (このような形式はシリアル化にも役立ちますが、後で説明するように、データベース側を制御できるようにすると簡単になります)。

モデル内のフィールドは、既存のデータベース列タイプに適合するように何らかの方法で変換する必要があります。 データベースが異なれば、有効な列タイプのセットも異なりますが、ルールは同じです。操作する必要があるタイプはこれらだけです。 データベースに保存するものはすべて、これらのタイプのいずれかに当てはまる必要があります。

通常、特定のデータベース列タイプに一致するようにDjangoフィールドを作成するか、データを文字列などに変換する方法が必要になります。

Handの例では、すべてのカードを事前に決められた順序で連結することで、カードデータを104文字の文字列に変換できます。たとえば、最初にすべてのカード、次にすべてのカードです。 西カード。 したがって、Handオブジェクトをデータベースのテキスト列または文字列に保存できます。


フィールドクラスは何をしますか?

Djangoのすべてのフィールド(このドキュメントで fields と言うときは、 form fields ではなく常にモデルフィールドを意味します)は django.db.models.Field [のサブクラスです。 X176X]。 Djangoがフィールドに関して記録する情報のほとんどは、名前、ヘルプテキスト、一意性など、すべてのフィールドに共通です。 そのすべての情報の保存はFieldによって処理されます。 Fieldでできることの正確な詳細については後で説明します。 今のところ、すべてがFieldの子孫であり、クラスの動作の重要な部分をカスタマイズしていると言えば十分です。

Djangoフィールドクラスはモデル属性に格納されているものではないことを理解することが重要です。 モデル属性には、通常のPythonオブジェクトが含まれています。 モデルで定義するフィールドクラスは、モデルクラスの作成時に、実際にはMetaクラスに格納されます(これがどのように行われるかについての正確な詳細は、ここでは重要ではありません)。 これは、属性を作成および変更するだけの場合は、フィールドクラスが必要ないためです。 代わりに、属性値とデータベースに格納されているもの、またはシリアライザーに送信されるものとの間で変換するための機構を提供します。

独自のカスタムフィールドを作成するときは、このことに注意してください。 作成したDjango Fieldサブクラスは、Pythonインスタンスとデータベース/シリアライザーの値をさまざまな方法で変換するための機構を提供します(たとえば、値の格納とルックアップに値を使用することには違いがあります)。 これが少しトリッキーに聞こえても、心配しないでください。以下の例でより明確になります。 カスタムフィールドが必要な場合、2つのクラスを作成することになることがよくあることを覚えておいてください。

  • 最初のクラスは、ユーザーが操作するPythonオブジェクトです。 彼らはそれをモデル属性に割り当て、表示目的でそれから読み取ります。 これは、この例のHandクラスです。
  • 2番目のクラスはFieldサブクラスです。 これは、最初のクラスを永続ストレージ形式とPython形式の間で相互に変換する方法を知っているクラスです。


フィールドサブクラスの記述

Field サブクラスを計画するときは、最初に、新しいフィールドが最も類似している既存の Field クラスについて考えてください。 既存のDjangoフィールドをサブクラス化して、作業を節約できますか? そうでない場合は、すべての子孫である Field クラスをサブクラス化する必要があります。

新しいフィールドの初期化は、ケースに固有の引数を共通の引数から分離し、後者を Field (または親クラス)の__init__()メソッドに渡すことです。

この例では、フィールドをHandFieldと呼びます。 ( Field サブクラスを<Something>Fieldと呼ぶことをお勧めします。これにより、 Field サブクラスとして簡単に識別できます。)既存のフィールドのようには動作しません。 フィールドから直接サブクラス化します。

from django.db import models

class HandField(models.Field):

    description = "A hand of cards (bridge style)"

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super().__init__(*args, **kwargs)

HandFieldは、ほとんどの標準フィールドオプションを受け入れますが(以下のリストを参照)、52枚のカード値とそのスーツを保持するだけでよいため、長さが固定されていることを確認します。 合計104文字。

ノート

Djangoのモデルフィールドの多くは、何もしないオプションを受け入れます。 たとえば、 editableauto_now の両方を django.db.models.DateField に渡すことができ、 editable パラメーターは無視されます。 ( auto_now が設定されている場合は、editable=Falseを意味します)。 この場合、エラーは発生しません。

この動作により、フィールドクラスが簡素化されます。これは、フィールドクラスが不要なオプションをチェックする必要がないためです。 それらはすべてのオプションを親クラスに渡し、後でそれらを使用しません。 フィールドで選択するオプションをより厳密にするか、現在のフィールドのより寛容な動作を使用するかは、あなた次第です。


Field.__init__()メソッドは、次のパラメーターを取ります。

上記のリストに説明のないすべてのオプションは、通常のDjangoフィールドの場合と同じ意味を持ちます。 例と詳細については、フィールドドキュメントを参照してください。

フィールドの脱構築

__init__()メソッドを作成することの対位法は、 deconstruct()メソッドを作成することです。 これは、モデルの移行中に、新しいフィールドのインスタンスを取得してシリアル化された形式に縮小する方法、特に__init__()に渡して再作成する方法をDjangoに指示するために使用されます。 。

継承元のフィールドの上にオプションを追加していない場合は、新しいdeconstruct()メソッドを作成する必要はありません。 ただし、__init__()で渡される引数を変更する場合(HandFieldの場合のように)、渡される値を補足する必要があります。

deconstruct()は、フィールドの属性名、フィールドクラスの完全なインポートパス、位置引数(リストとして)、およびキーワード引数(dictとして)の4つの項目のタプルを返します。 これは、3つのタプルを返すカスタムクラスdeconstruct()メソッドとは異なることに注意してください。

カスタムフィールドの作成者は、最初の2つの値を気にする必要はありません。 基本Fieldクラスには、フィールドの属性名とインポートパスを計算するためのすべてのコードが含まれています。 ただし、位置引数とキーワード引数は変更する可能性が高いため、注意する必要があります。

たとえば、HandFieldクラスでは、常に__init__()にmax_lengthを強制的に設定しています。 基本Fieldクラスのdeconstruct()メソッドはこれを確認し、キーワード引数で返そうとします。 したがって、読みやすさのためにキーワード引数から削除できます。

from django.db import models

class HandField(models.Field):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        del kwargs["max_length"]
        return name, path, args, kwargs

新しいキーワード引数を追加する場合は、その値をkwargsに自分で配置するコードをdeconstruct()に記述する必要があります。 また、デフォルト値が使用されている場合など、フィールドの状態を再構築する必要がない場合は、kwargsから値を省略する必要があります。

from django.db import models

class CommaSepField(models.Field):
    "Implements comma-separated storage of lists"

    def __init__(self, separator=",", *args, **kwargs):
        self.separator = separator
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        # Only include kwarg if it's not the default
        if self.separator != ",":
            kwargs['separator'] = self.separator
        return name, path, args, kwargs

より複雑な例はこのドキュメントの範囲を超えていますが、覚えておいてください-Fieldインスタンスの構成では、deconstruct()__init__に渡してその状態を再構築できる引数を返す必要があります。

Fieldスーパークラスの引数に新しいデフォルト値を設定する場合は、特に注意してください。 古いデフォルト値を使用した場合に表示されなくなるのではなく、常に含まれていることを確認する必要があります。

さらに、位置引数として値を返さないようにしてください。 可能な場合は、将来の互換性を最大化するために、キーワード引数として値を返します。 コンストラクターの引数リストでの位置よりも頻繁に名前を変更する場合は、位置を好むかもしれませんが、シリアル化されたバージョンからかなりの期間(場合によっては数年)フィールドを再構築することに注意してください。あなたの移行は生き続けます。

フィールドを含む移行を調べることで分解の結果を確認でき、フィールドを分解して再構築することで単体テストで分解をテストできます。

name, path, args, kwargs = my_field_instance.deconstruct()
new_instance = MyField(*args, **kwargs)
self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)

カスタムフィールドの基本クラスの変更

Djangoは変更を検出して移行しないため、カスタムフィールドの基本クラスを変更することはできません。 たとえば、次のように開始した場合:

class CustomCharField(models.CharField):
    ...

次に、代わりにTextFieldを使用することを決定します。次のように、サブクラスを変更することはできません。

class CustomCharField(models.TextField):
    ...

代わりに、新しいカスタムフィールドクラスを作成し、それを参照するようにモデルを更新する必要があります。

class CustomCharField(models.CharField):
    ...

class CustomTextField(models.TextField):
    ...

フィールドの削除で説明したように、元のCustomCharFieldクラスを参照する移行がある限り、それを保持する必要があります。


カスタムフィールドの文書化

いつものように、フィールドタイプを文書化して、ユーザーがそれが何であるかを理解できるようにする必要があります。 開発者に役立つdocstringを提供することに加えて、管理アプリのユーザーが django.contrib.admindocs アプリケーションを介してフィールドタイプの簡単な説明を表示できるようにすることもできます。 これを行うには、カスタムフィールドの description クラス属性に説明テキストを入力します。 上記の例では、admindocsアプリケーションによってHandFieldに対して表示される説明は、「カードの手(ブリッジスタイル)」になります。

django.contrib.admindocs 表示では、フィールドの説明がfield.__dict__で補間され、説明にフィールドの引数を組み込むことができます。 たとえば、 CharField の説明は次のとおりです。

description = _("String (up to %(max_length)s)")

便利な方法

Field サブクラスを作成したら、フィールドの動作に応じて、いくつかの標準メソッドをオーバーライドすることを検討してください。 以下のメソッドのリストは、重要度の高い順になっているため、上から始めてください。

カスタムデータベースタイプ

mytypeというPostgreSQLカスタムタイプを作成したとします。 次のように、Fieldをサブクラス化し、 db_type()メソッドを実装できます。

from django.db import models

class MytypeField(models.Field):
    def db_type(self, connection):
        return 'mytype'

MytypeFieldを入手したら、他のFieldタイプと同じように、どのモデルでも使用できます。

class Person(models.Model):
    name = models.CharField(max_length=80)
    something_else = MytypeField()

データベースに依存しないアプリケーションを構築することを目的とする場合は、データベースの列タイプの違いを考慮する必要があります。 たとえば、PostgreSQLの日付/時刻列タイプはtimestampと呼ばれ、MySQLの同じ列はdatetimeと呼ばれます。 connection.vendor属性をチェックすることにより、 db_type()メソッドでこれを処理できます。 現在の組み込みベンダー名は、sqlitepostgresqlmysql、およびoracleです。

例えば:

class MyDateField(models.Field):
    def db_type(self, connection):
        if connection.vendor == 'mysql':
            return 'datetime'
        else:
            return 'timestamp'

db_type()メソッドと rel_db_type()メソッドは、フレームワークがアプリケーションのCREATE TABLEステートメントを作成するとき、つまり最初にテーブルを作成するときにDjangoによって呼び出されます。 。 このメソッドは、モデルフィールドを含むWHERE句を作成するとき、つまりget()filter()exclude()そして引数としてモデルフィールドを持っています。 これらは他の時間には呼び出されないため、上記の例のconnection.settings_dictチェックなどの少し複雑なコードを実行する余裕があります。

一部のデータベース列タイプは、CHAR(25)などのパラメーターを受け入れます。ここで、パラメーター25は最大列長を表します。 このような場合、db_type()メソッドでハードコーディングするよりも、モデルでパラメーターを指定する方が柔軟性があります。 たとえば、次のようにCharMaxlength25Fieldを使用してもあまり意味がありません。

# This is a silly example of hard-coded parameters.
class CharMaxlength25Field(models.Field):
    def db_type(self, connection):
        return 'char(25)'

# In the model:
class MyModel(models.Model):
    # ...
    my_field = CharMaxlength25Field()

これを行うためのより良い方法は、実行時、つまりクラスがインスタンス化されるときにパラメーターを指定可能にすることです。 これを行うには、次のようにField.__init__()を実装します。

# This is a much more flexible example.
class BetterCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(*args, **kwargs)

    def db_type(self, connection):
        return 'char(%s)' % self.max_length

# In the model:
class MyModel(models.Model):
    # ...
    my_field = BetterCharField(25)

最後に、列で本当に複雑なSQLセットアップが必要な場合は、 db_type()からNoneを返します。 これにより、DjangoのSQL作成コードがこのフィールドをスキップします。 次に、他の方法で適切なテーブルに列を作成する責任がありますが、これにより、Djangoに邪魔にならないように指示する方法が提供されます。

rel_db_type()メソッドは、データベース列のデータ型を決定するために別のフィールドを指すForeignKeyOneToOneFieldなどのフィールドによって呼び出されます。 たとえば、UnsignedAutoFieldがある場合、同じデータ型を使用するには、そのフィールドを指す外部キーも必要です。

# MySQL unsigned integer (range 0 to 4294967295).
class UnsignedAutoField(models.AutoField):
    def db_type(self, connection):
        return 'integer UNSIGNED AUTO_INCREMENT'

    def rel_db_type(self, connection):
        return 'integer UNSIGNED'

値をPythonオブジェクトに変換する

カスタム Field クラスが文字列、日付、整数、または浮動小数点数よりも複雑なデータ構造を処理する場合は、 from_db_value()および to_python( )

フィールドサブクラスに存在する場合、from_db_value()は、データがデータベースからロードされるときに、集計や values()呼び出しを含め、すべての状況で呼び出されます。

to_python()は、逆シリアル化によって、およびフォームから使用される clean()メソッド中に呼び出されます。

原則として、to_python()は、次の引数のいずれかを適切に処理する必要があります。

  • 正しいタイプのインスタンス(たとえば、進行中の例ではHand)。
  • 文字列
  • None(フィールドでnull=Trueが許可されている場合)

HandFieldクラスでは、データをVARCHARフィールドとしてデータベースに格納しているため、from_db_value()で文字列とNoneを処理できる必要があります。 to_python()では、Handインスタンスも処理する必要があります。

import re

from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

def parse_hand(hand_string):
    """Takes a string of cards and splits into a full hand."""
    p1 = re.compile('.{26}')
    p2 = re.compile('..')
    args = [p2.findall(x) for x in p1.findall(hand_string)]
    if len(args) != 4:
        raise ValidationError(_("Invalid input for a Hand instance"))
    return Hand(*args)

class HandField(models.Field):
    # ...

    def from_db_value(self, value, expression, connection):
        if value is None:
            return value
        return parse_hand(value)

    def to_python(self, value):
        if isinstance(value, Hand):
            return value

        if value is None:
            return value

        return parse_hand(value)

これらのメソッドから常にHandインスタンスを返すことに注意してください。 これが、モデルの属性に格納するPythonオブジェクトタイプです。

to_python()の場合、値の変換中に問題が発生した場合は、 ValidationError 例外を発生させる必要があります。


Pythonオブジェクトをクエリ値に変換する

データベースを使用するには両方の方法で変換する必要があるため、 from_db_value()をオーバーライドする場合は、 get_prep_value()もオーバーライドしてPythonオブジェクトをクエリ値に戻す必要があります。

例えば:

class HandField(models.Field):
    # ...

    def get_prep_value(self, value):
        return ''.join([''.join(l) for l in (value.north,
                value.east, value.south, value.west)])

警告

カスタムフィールドでMySQLのCHARVARCHAR、またはTEXTタイプを使用する場合は、 get_prep_value()が常に文字列タイプを返すようにする必要があります。 MySQLは、これらのタイプでクエリが実行され、提供された値が整数である場合、柔軟で予期しないマッチングを実行します。これにより、クエリの結果に予期しないオブジェクトが含まれる可能性があります。 get_prep_value()から常に文字列型を返す場合、この問題は発生しません。


クエリ値をデータベース値に変換する

一部のデータ型(日付など)は、データベースバックエンドで使用する前に特定の形式にする必要があります。 get_db_prep_value()は、これらの変換を行う必要があるメソッドです。 クエリに使用される特定の接続は、connectionパラメーターとして渡されます。 これにより、必要に応じてバックエンド固有の変換ロジックを使用できます。

たとえば、Djangoは BinaryField に次のメソッドを使用します。

def get_db_prep_value(self, value, connection, prepared=False):
    value = super().get_db_prep_value(value, connection, prepared)
    if value is not None:
        return connection.Database.Binary(value)
    return value

カスタムフィールドを保存するときに、通常のクエリパラメータに使用される変換とは異なる特別な変換が必要な場合は、 get_db_prep_save()をオーバーライドできます。


保存する前に値を前処理する

保存する直前に値を前処理したい場合は、 pre_save()を使用できます。 たとえば、Djangoの DateTimeField は、 auto_now または auto_now_add の場合、このメソッドを使用して属性を正しく設定します。

このメソッドをオーバーライドする場合は、最後に属性の値を返す必要があります。 モデルへの参照を保持するコードが常に正しい値を参照できるように、値に変更を加えた場合は、モデルの属性も更新する必要があります。


モデルフィールドのフォームフィールドの指定

ModelForm で使用されるフォームフィールドをカスタマイズするには、 formfield()をオーバーライドできます。

フォームフィールドクラスは、form_classおよびchoices_form_class引数を介して指定できます。 後者は、フィールドに選択肢が指定されている場合に使用され、前者はそれ以外の場合に使用されます。 これらの引数が指定されていない場合、 CharField または TypedChoiceField が使用されます。

すべてのkwargsディクショナリは、フォームフィールドの__init__()メソッドに直接渡されます。 通常、あなたがする必要があるのは、form_class(そしておそらくchoices_form_class)引数の適切なデフォルトを設定し、それからさらに処理を親クラスに委任することです。 これには、カスタムフォームフィールド(およびフォームウィジェット)を作成する必要がある場合があります。 これについては、フォームのドキュメントを参照してください。

進行中の例を続けると、 formfield()メソッドを次のように記述できます。

class HandField(models.Field):
    # ...

    def formfield(self, **kwargs):
        # This is a fairly standard way to set up some defaults
        # while letting the caller override them.
        defaults = {'form_class': MyFormField}
        defaults.update(kwargs)
        return super().formfield(**defaults)

これは、MyFormFieldフィールドクラス(独自のデフォルトウィジェットがある)をインポートしたことを前提としています。 このドキュメントでは、カスタムフォームフィールドの作成の詳細については説明していません。


組み込みフィールドタイプのエミュレート

db_type()メソッドを作成した場合は、 get_internal_type()について心配する必要はありません。あまり使用されません。 ただし、データベースストレージのタイプが他のフィールドと似ている場合があるため、他のフィールドのロジックを使用して適切な列を作成できます。

例えば:

class HandField(models.Field):
    # ...

    def get_internal_type(self):
        return 'CharField'

使用しているデータベースバックエンドに関係なく、これは:djadmin: `migrate` およびその他のSQLコマンドが文字列を格納するための適切な列タイプを作成することを意味します。

get_internal_type()が、使用しているデータベースバックエンドのDjangoに認識されていない文字列を返した場合、つまりdjango.db.backends.<db_name>.base.DatabaseWrapper.data_typesに表示されない場合、その文字列は引き続きによって使用されます。シリアライザーですが、デフォルトの db_type()メソッドはNoneを返します。 これが役立つ理由については、 db_type()のドキュメントを参照してください。 Django以外の場所でシリアライザーの出力を使用する場合は、シリアライザーのフィールドのタイプとして説明文字列を入力すると便利です。


シリアル化のためのフィールドデータの変換

シリアライザーによる値のシリアル化方法をカスタマイズするには、 value_to_string()をオーバーライドできます。 value_from_object()を使用することは、シリアル化の前にフィールドの値を取得するための最良の方法です。 たとえば、HandFieldはとにかくデータストレージに文字列を使用するため、既存の変換コードを再利用できます。

class HandField(models.Field):
    # ...

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return self.get_prep_value(value)

いくつかの一般的なアドバイス

カスタムフィールドの作成は、特にPythonタイプとデータベースおよびシリアル化形式の間で複雑な変換を行う場合は、注意が必要なプロセスになる可能性があります。 物事をよりスムーズに進めるためのヒントをいくつか紹介します。

  1. インスピレーションを得るために、既存のDjangoフィールド(django/db/models/fields/__init__.py内)を見てください。 まったく新しいフィールドを最初から作成するのではなく、必要なものに類似したフィールドを見つけて少し拡張してみてください。
  2. フィールドとしてラップするクラスに__str__()メソッドを配置します。 フィールドコードのデフォルトの動作が値に対してstr()を呼び出すことである場所はたくさんあります。 (このドキュメントの例では、valueHandFieldではなくHandインスタンスになります)。 したがって、__str__()メソッドがPythonオブジェクトの文字列形式に自動的に変換される場合は、多くの作業を節約できます。


FileFieldサブクラスの作成

上記の方法に加えて、ファイルを処理するフィールドには、考慮しなければならない他のいくつかの特別な要件があります。 データベースのストレージと取得の制御など、FileFieldによって提供されるメカニズムの大部分は変更せずに、特定のタイプのファイルをサポートするという課題に対処するためのサブクラスを残すことができます。

DjangoはFileクラスを提供します。これは、ファイルの内容と操作のプロキシとして使用されます。 これをサブクラス化して、ファイルへのアクセス方法と使用可能なメソッドをカスタマイズできます。 django.db.models.fields.filesにあり、デフォルトの動作はファイルのドキュメントで説明されています。

Fileのサブクラスが作成されたら、新しいFileFieldサブクラスにそれを使用するように指示する必要があります。 これを行うには、新しいFileサブクラスをFileFieldサブクラスの特別なattr_class属性に割り当てます。

いくつかの提案

上記の詳細に加えて、フィールドのコードの効率と読みやすさを大幅に向上させることができるいくつかのガイドラインがあります。

  1. Django独自のImageFielddjango/db/models/fields/files.py内)のソースは、FileFieldをサブクラス化して特定のタイプのファイルをサポートする方法の優れた例です。これには、説明されているすべての手法が組み込まれています。その上。
  2. 可能な限りファイル属性をキャッシュします。 ファイルはリモートストレージシステムに保存される可能性があるため、ファイルの取得には余分な時間や費用がかかる場合がありますが、必ずしも必要なわけではありません。 ファイルを取得してそのコンテンツに関するデータを取得したら、そのデータをできるだけ多くキャッシュして、その情報の後続の呼び出しでファイルを取得する必要がある回数を減らします。