Spec-Zone.ru › Django 4.1

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

Введение

Документация по моделям базы данных объясняет, как использовать стандартные классы полей 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)

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

Новое в Django 4.1.

Вы можете переопределить 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, когда фреймворк строит SQL-заявления для вашего приложения — то есть, когда вы впервые создаёте свои таблицы. Методы также вызываются при построении фрагмента WHERE, включающего поле модели — то есть, когда вы получаете данные с помощью методов QuerySet, таких как 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, верните 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 использует этот метод для правильного задания атрибута в случае 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/4.1/howto/custom-model-fields/

Spec-Zone.ru

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