Spec-Zone.ru › Django 4.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)

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

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

class CustomCharField(models.CharField):
    ...

и затем решаете, что хотите использовать TextField вместо него, вы не можете изменить подкласс таким образом:

class CustomCharField(models.TextField):
    ...

Вместо этого, вы должны создать новый класс пользовательского поля и обновить свои модели для ссылки на него:

class CustomCharField(models.CharField):
    ...

class CustomTextField(models.TextField):
    ...

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

Документирование вашего пользовательского поля

Как всегда, вы должны документировать тип вашего поля, чтобы пользователи понимали, что это такое. В дополнение к предоставлению для него строковой документации, полезной для разработчиков, вы также можете позволить пользователям приложения администрирования увидеть краткое описание типа поля с помощью приложения 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(), и поле модели является аргументом. Они не вызываются в других ситуациях, поэтому могут позволить себе выполнять немного более сложный код, например, проверку 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’s 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/4.0/howto/custom-model-fields/

Spec-Zone.ru

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