Spec-Zone.ru › Django 5.0

Как создать пользовательские поля модели

Введение

Документация по моделям базы данных объясняет, как использовать стандартные классы полей Django — CharField, DateField и т.д. Для многих целей этих классов достаточно. Однако иногда версии Django не удовлетворяют вашим конкретным потребностям, или вам нужно поле, которое полностью отличается от тех, что поставляются с Django.

Встроенные типы полей Django не охватывают все возможные типы столбцов базы данных — только распространённые, такие как VARCHAR и INTEGER. Для более редких типов столбцов, таких как географические полигоны или даже созданные пользователем типы, такие как пользовательские типы PostgreSQL, вы можете определить свои собственные подклассы Django Field.

Кроме того, у вас может быть сложный объект Python, который каким-то образом можно сериализовать, чтобы он соответствовал стандартному типу столбца базы данных. Это ещё один случай, когда подкласс Field поможет вам использовать ваш объект в ваших моделях.

Пример объекта

Создание пользовательских полей требует некоторого внимания к деталям. Чтобы упростить понимание, мы будем использовать последовательный пример на протяжении всего документа: оборачивание объекта Python, представляющего раздачу карт в партии в бридже. Не беспокойтесь, вам не нужно знать, как играть в бридж, чтобы понять этот пример. Вам нужно только знать, что 52 карты раздаются равномерно четырём игрокам, которые традиционно называются север, восток, юг и запад. Наш класс выглядит примерно так:

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()

Мы записываем и извлекаем значения из атрибута hand в нашей модели так же, как и из любого другого класса Python. Секрет в том, чтобы сообщить Django, как обрабатывать сохранение и загрузку такого объекта.

Для использования класса Hand в наших моделях нам не нужно изменять этот класс вообще. Это идеально, потому что это означает, что вы можете легко написать поддержку модели для существующих классов, где вы не можете изменить исходный код.

Примечание

Возможно, вы хотите воспользоваться только пользовательскими типами столбцов базы данных и работать с данными как со стандартными типами Python в своих моделях; строки или числа с плавающей точкой, например. Этот случай аналогичен нашему примеру Hand и мы отметим любые различия по ходу дела.

Теоретическая справка

Хранение в базе данных

Начнём с полей модели. Если упростить, поле модели предоставляет способ взять обычный объект Python — строку, булево значение, datetime, или что-то более сложное, например, Hand — и преобразовать его в формат, полезный при работе с базой данных. (Такой формат также полезен для сериализации, но, как мы увидим позже, это проще, когда вы контролируете базу данных).

Поля в модели каким-то образом должны быть преобразованы для соответствия существующему типу столбца базы данных. Разные базы данных предоставляют разные наборы допустимых типов столбцов, но правило остаётся неизменным: это единственные типы, с которыми вам нужно работать. Всё, что вы хотите хранить в базе данных, должно соответствовать одному из этих типов.

Обычно вы либо пишете поле Django, соответствующее определённому типу столбца базы данных, либо вам нужно преобразовать данные, например, в строку.

Для нашего примера Hand мы можем преобразовать данные карт в строку из 104 символов, объединив все карты в определённом порядке — например, сначала все карты севера, затем востока, юга и запада. Таким образом, объекты Hand могут храниться в текстовых или символьных столбцах базы данных.

Что делает класс поля?

Все поля Django (и когда в этом документе мы говорим о полях, мы всегда имеем в виду поля модели, а не поля форм) являются подклассами django.db.models.Field. Большая часть информации, которую Django записывает о поле, является общей для всех полей — имя, подсказка, уникальность и так далее. Хранение всей этой информации обрабатывается Field. Мы разберёмся с точными деталями того, что Field может делать позже; пока достаточно сказать, что всё наследуется от Field и затем настраивает ключевые части поведения класса.

Важно понимать, что класс поля Django — это не то, что хранится в атрибутах вашей модели. Атрибуты модели содержат обычные объекты Python. Классы полей, которые вы определяете в модели, фактически хранятся в классе Meta при создании класса модели (точные детали того, как это делается, здесь не важны). Это потому, что классы полей не нужны, когда вы просто создаёте и изменяете атрибуты. Вместо этого они предоставляют механизм преобразования между значением атрибута и тем, что хранится в базе данных или отправляется в сериализатор.

Помните об этом, создавая собственные пользовательские поля. Подкласс Django Field, который вы пишете, предоставляет механизм преобразования между вашими экземплярами Python и значениями базы данных/сериализатора различными способами (например, есть различия между хранением значения и использованием значения для поиска). Если это звучит немного сложно, не беспокойтесь — всё станет яснее в примерах ниже. Просто помните, что вы часто будете создавать два класса, когда вам нужно пользовательское поле:

  • Первый класс — это объект Python, с которым будут работать ваши пользователи. Они будут назначать его в атрибут модели, читать его для отображения и так далее. Это класс Hand в нашем примере.
  • Второй класс — это подкласс Field. Это класс, который знает, как преобразовывать ваш первый класс взад и вперёд между его постоянной формой хранения и формой Python.

Создание подкласса поля

При планировании подкласса Field сначала подумайте, какой существующий класс Field наиболее похож на ваше новое поле. Можете ли вы создать подкласс существующего поля Django и сэкономить себе время? Если нет, вы должны создать подкласс класса Field, от которого всё наследуется.

Инициализация нового поля сводится к разделению аргументов, специфичных для вашего случая, от общих аргументов и передаче последних методу __init__() класса Field (или вашего родительского класса).

В нашем примере мы назовём наше поле HandField. (Хорошая идея — называть свой подкласс Field подклассом <Something>Field, чтобы его легко было распознать как подкласс 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 принимают параметры, которые они не используют. Например, вы можете передать как editable, так и auto_now в поле django.db.models.DateField, и оно проигнорирует параметр editable (auto_now подразумевает editable=False). В этом случае ошибка не возникает.

Это поведение упрощает классы полей, поскольку им не нужно проверять параметры, которые не нужны. Они передают все параметры родительскому классу и больше не используют их. Зависит от вас, хотите ли вы, чтобы ваши поля были более строгими в отношении параметров, которые они выбирают, или использовать более мягкое поведение текущих полей.

Метод Field.__init__() принимает следующие параметры:

  • verbose_name
  • name
  • primary_key
  • max_length
  • unique
  • blank
  • null
  • db_index
  • rel: Используется для связанных полей (например, ForeignKey). Только для продвинутого использования.
  • default
  • editable
  • serialize: Если False, поле не будет сериализовано, когда модель передаётся в сериализаторы Django. По умолчанию True.
  • unique_for_date
  • unique_for_month
  • unique_for_year
  • choices
  • help_text
  • db_column
  • db_tablespace: Только для создания индексов, если бэкенд поддерживает пространства таблиц. Обычно этот параметр можно игнорировать.
  • auto_created: True если поле было создано автоматически, например, для OneToOneField, используемого для наследования моделей. Только для продвинутого использования.

Все параметры без пояснения в вышеуказанном списке имеют то же значение, что и для обычных полей Django. См. документацию по полям для примеров и подробностей.

Распаковка поля

Противоположностью написанию метода __init__() является написание метода deconstruct(). Он используется во время миграций моделей для того, чтобы сообщить Django, как преобразовать экземпляр вашего нового поля в сериализованную форму — в частности, какие аргументы передать в __init__() для его повторного создания.

Если вы не добавили дополнительных параметров помимо унаследованных от поля, то нет необходимости писать новый метод deconstruct(). Однако, если вы изменяете аргументы, передаваемые в __init__() (как мы делаем в HandField), вам нужно дополнить передаваемые значения.

deconstruct() возвращает кортеж из четырех элементов: имя атрибута поля, полный путь импорта класса поля, позиционные аргументы (в виде списка) и ключевые аргументы (в виде словаря). Обратите внимание, что это отличается от метода deconstruct() для пользовательских классов, который возвращает кортеж из трех элементов.

Как автору пользовательского поля, вам не нужно заботиться о первых двух значениях; базовый класс Field содержит весь код для определения имени атрибута поля и пути импорта. Однако вам нужно заботиться о позиционных и ключевых аргументах, поскольку, скорее всего, именно они изменяются.

Например, в нашем классе HandField мы всегда принудительно устанавливаем max_length в __init__(). Метод deconstruct() в базовом классе Field увидит это и попытается вернуть его в ключевых аргументах; таким образом, мы можем исключить его из ключевых аргументов для повышения читаемости:

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

Если вы добавите новый ключевой аргумент, вам нужно написать код в deconstruct(), который поместит его значение в kwargs самостоятельно. Вы также должны исключить значение из 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

Более сложные примеры выходят за рамки этого документа, но помните — для любой конфигурации экземпляра вашего поля, 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)

Атрибуты поля, не влияющие на определение столбца базы данных

Вы можете переопределить Field.non_db_attrs для настройки атрибутов поля, не влияющих на определение столбца. Это используется во время миграций моделей для обнаружения операций AlterField без изменений.

Например:

class CommaSepField(models.Field):
    @property
    def non_db_attrs(self):
        return super().non_db_attrs + ("separator",)

Изменение базового класса пользовательского поля

Вы не можете изменить базовый класс пользовательского поля, потому что 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, вы можете рассмотреть переопределение нескольких стандартных методов, в зависимости от поведения вашего поля. Список методов ниже примерно в порядке убывания важности, поэтому начните с верхней части.

Пользовательские типы баз данных

Предположим, вы создали пользовательский тип PostgreSQL под названием mytype. Вы можете создать подкласс 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. Вы можете справиться с этим в методе db_type(), проверив атрибут connection.vendor. Текущие встроенные имена поставщиков: sqlite, postgresql, mysql, и oracle.

Например:

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

Методы db_type() и rel_db_type() вызываются Django при построении CREATE TABLE-заявлений для вашего приложения — то есть при первом создании таблиц. Методы также вызываются при построении WHERE-клаузы, которая включает поле модели — то есть при получении данных с помощью методов QuerySet, таких как get(), filter(), и exclude(), и при передаче поля модели в качестве аргумента.

Некоторые типы столбцов базы данных принимают параметры, например 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, верните None из db_type(). Это заставит код создания SQL Django пропустить это поле. Затем вы сами отвечаете за создание столбца в правильной таблице каким-либо другим способом, но это даёт вам возможность сказать Django, чтобы он отошёл в сторону.

Метод rel_db_type() вызывается полями, такими как ForeignKey и OneToOneField, которые указывают на другое поле, чтобы определить типы данных столбцов базы данных. Например, если у вас есть 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 в базе данных, поэтому нам необходимо уметь обрабатывать строки и None в from_db_value() метод. В 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)]
        )

Предупреждение

Если ваше пользовательское поле использует типы CHAR, VARCHAR или TEXT для MySQL, вы должны убедиться, что 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"

Независимо от используемого бэкенда базы данных, это означает, что 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() для значения. (В наших примерах в этом документе value будет экземпляром Hand, а не HandField). Поэтому, если ваш метод __str__() автоматически преобразует в строковую форму вашего объекта Python, вы можете сэкономить много работы.

Написание подкласса FileField

В дополнение к вышеперечисленным методам, поля, связанные с файлами, имеют несколько других специальных требований, которые необходимо учитывать. Большая часть механики, предоставляемой FileField, такая как управление хранением и извлечением данных из базы данных, может остаться без изменений, оставляя подклассы для решения задачи поддержки определенного типа файла.

Django предоставляет класс File, который используется в качестве прокси для содержимого и операций с файлом. Его можно подклассировать, чтобы настроить доступ к файлу и доступные методы. Он находится по адресу django.db.models.fields.files, и его поведение по умолчанию объясняется в документации по файлам.

После создания подкласса File, новый подкласс FileField должен быть уведомлен об использовании этого подкласса. Для этого присвойте новый подкласс File специальному атрибуту attr_class подкласса FileField.

Некоторые рекомендации

В дополнение к вышеупомянутым деталям существуют некоторые рекомендации, которые могут значительно улучшить эффективность и читаемость кода поля.

  1. Исходный код собственных ImageField Django (в django/db/models/fields/files.py) — отличный пример того, как подклассировать FileField для поддержки определенного типа файла, поскольку он включает в себя все описанные выше методы.
  2. Кэшируйте атрибуты файла, где это возможно. Поскольку файлы могут храниться в удаленных системах хранения, их получение может потребовать дополнительного времени или даже денег, что не всегда необходимо. После извлечения файла для получения некоторых данных о его содержимом, кэшируйте как можно больше этих данных, чтобы уменьшить количество запросов на извлечение файла при последующих вызовах для получения этой информации.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API