Написание пользовательских полей модели ¶
Введение ¶
Эталонная модель документация объясняет , как использовать стандартные классы Джанго поля - CharField
,
DateField
и т.д. Для многих целей, эти классы все , что вам нужно. Однако иногда версия Django не соответствует вашим точным требованиям, или вы захотите использовать поле, которое полностью отличается от тех, которые поставляются с Django.
Встроенные типы полей Django не охватывают все возможные типы столбцов базы данных - только общие типы, такие как VARCHAR
и INTEGER
. Для более непонятных типов столбцов, таких как географические многоугольники, или даже для пользовательских типов, таких как
пользовательские типы PostgreSQL , вы можете определить свои собственные Field
подклассы Django .
В качестве альтернативы у вас может быть сложный объект Python, который можно каким-либо образом сериализовать, чтобы он соответствовал стандартному типу столбца базы данных. Это еще один случай, когда Field
подкласс поможет вам использовать ваш объект с вашими моделями.
Наш пример объекта ¶
Создание настраиваемых полей требует внимания к деталям. Чтобы упростить понимание, мы будем использовать последовательный пример в этом документе: обертывание объекта Python, представляющего раздачу карт в руке Bridge . Не волнуйтесь, вам не обязательно знать, как играть в Bridge, чтобы следовать этому примеру. Вам нужно только знать, что 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
классе при создании класса модели (точные детали того, как это делается, здесь не важны). Это связано с тем, что классы полей не нужны, когда вы просто создаете и изменяете атрибуты. Вместо этого они предоставляют механизм для преобразования между значением атрибута и тем, что хранится в базе данных или отправляется в сериализатор .
Помните об этом при создании собственных настраиваемых полей. Field
Подкласс Django, который вы пишете, предоставляет механизм для преобразования между вашими экземплярами 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
a,
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()
возвращает кортеж из четырех элементов: имя атрибута поля, полный путь импорта класса поля, позиционные аргументы (в виде списка) и аргументы ключевого слова (в виде dict). Обратите внимание, что это отличается от
deconstruct()
метода для настраиваемых классов,
который возвращает кортеж из трех элементов.
Как автору настраиваемого поля вам не нужно заботиться о первых двух значениях; базовый Field
класс имеет весь код для определения имени атрибута поля и пути импорта. Однако вам нужно позаботиться о позиционных аргументах и аргументах ключевых слов, поскольку это, вероятно, то, что вы меняете.
Например, в нашем HandField
классе мы всегда принудительно устанавливаем max_length в __init__()
. deconstruct()
Метод на базовом Field
классе будет видеть это и попытаться вернуть его в именованных аргументов; таким образом, мы можем исключить его из ключевого слова arguments для удобства чтения:
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
Более сложные примеры выходят за рамки этого документа, но помните - для любой конфигурации вашего экземпляра 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
класс, пока у вас есть миграции, которые ссылаются на него.
Документирование вашего настраиваемого поля ¶
Как всегда, вы должны задокументировать свой тип поля, чтобы пользователи знали, что это такое. Помимо предоставления для него строки документации, которая полезна для разработчиков, вы также можете разрешить пользователям приложения администратора видеть краткое описание типа поля через приложение django.contrib.admindocs . Для этого description
укажите описательный текст в атрибуте класса вашего настраиваемого поля. В приведенном выше примере описанием, отображаемым admindocs
приложением для a, 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.settings_dict['ENGINE']
атрибут.
Например:
class MyDateField(models.Field):
def db_type(self, connection):
if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
return 'datetime'
else:
return 'timestamp'
db_type()
И rel_db_type()
методы называются Джанго , когда структура строит заявления для вашего приложения - то есть, когда вы сначала создать таблицы. Эти методы также вызывается при построении пункта , который включает в себя поле модели - то есть, когда вы извлечение данных с использованием методов QuerySet как ,
и и имеете поле модели в качестве аргумента. Они не вызываются в другое время, поэтому он может позволить себе выполнить немного сложный код, такой как проверка в приведенном выше примере.CREATE TABLE
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, вернитесь 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 и форматами базы данных и сериализации. Вот несколько советов, которые помогут упростить жизнь:
- Посмотрите на существующие поля Django (in
django/db/models/fields/__init__.py
) для вдохновения. Попробуйте найти поле, которое похоже на то, что вы хотите, и немного его расширите, вместо того, чтобы создавать совершенно новое поле с нуля. - Поместите
__str__()
метод в класс, который вы завершаете как поле. Есть много мест, где по умолчанию код поля вызываетstr()
значение. (В наших примерах в этом документеvalue
это будетHand
экземпляр, а не аHandField
). Поэтому, если ваш__str__()
метод автоматически преобразуется в строковую форму вашего объекта Python, вы можете сэкономить много работы.
Написание FileField
подкласса ¶
В дополнение к вышеупомянутым методам, поля, которые имеют дело с файлами, имеют несколько других особых требований, которые необходимо учитывать. Большая часть предоставляемых механизмов FileField
, таких как управление хранением и извлечением из базы данных, может оставаться неизменной, оставляя подклассам возможность решать проблему поддержки определенного типа файла.
Django предоставляет File
класс, который используется в качестве прокси для содержимого файла и операций. Его можно разделить на подклассы, чтобы настроить способ доступа к файлу и доступные методы. Он находится по адресу
django.db.models.fields.files
, и его поведение по умолчанию объяснено в документации к
файлу .
После создания подкласса необходимо указать File
новому FileField
подклассу использовать его. Для этого назначьте новый File
подкласс специальному
attr_class
атрибуту FileField
подкласса.
Несколько предложений ¶
В дополнение к приведенным выше деталям есть несколько рекомендаций, которые могут значительно повысить эффективность и читаемость кода поля.
- Исходный код Django own
ImageField
(indjango/db/models/fields/files.py
) является отличным примером того, какFileField
создать подкласс для поддержки определенного типа файла, поскольку он включает в себя все методы, описанные выше. - По возможности кешируйте атрибуты файлов. Поскольку файлы могут храниться в удаленных системах хранения, их извлечение может потребовать дополнительного времени или даже денег, что не всегда необходимо. Как только файл получен для получения некоторых данных о его содержимом, кешируйте как можно больше этих данных, чтобы уменьшить количество раз, когда файл должен быть получен при последующих вызовах этой информации.