Экземпляры Django Field перекрывают аргументы друг друга
Я тестирую и готовлю новый пакет Django для использования отбеливателя с полями Text и Char в Django ORM и с DRF. Однако я столкнулся с некоторыми препятствиями, и это заставило меня задуматься и понять, действительно ли я понимаю, как создаются поля моделей. Надеюсь, кто-то может это прояснить.
Я инициализирую аргументы для отбеливания, загружая dict настроек по умолчанию из django.conf.settings, а затем проверяю параметр field_args, чтобы увидеть, были ли какие-либо переопределены для конкретного определения поля, как показано ниже. Затем это используется в функции pre_save для вызова bleach:
class BleachedCharField(CharField):
"""
An enhanced CharField for sanitising input with the Python library, bleach.
"""
def __init__(self, *args, field_args=None, **kwargs):
"""
Initialize the BleachedCharField with default arguments, and update with called parameters.
:param tags: (dict) optional bleach argument overrides, format matches BLEACHFIELDS defaults.
:param args: extra args to pass to CharField __init__
:param kwargs: undefined args
"""
super(BleachedCharField, self).__init__(*args, **kwargs)
self.args = settings.BLEACHFIELDS or None
if field_args:
if 'tags' in field_args:
self.args['tags'] = field_args['tags']
if 'attributes' in field_args:
self.args['attributes'] = field_args['attributes']
if 'styles' in field_args:
self.args['styles'] = field_args['styles']
if 'protocols' in field_args:
self.args['protocols'] = field_args['protocols']
if 'strip' in field_args:
self.args['strip'] = field_args['strip']
if 'strip_comments' in field_args:
self.args['strip_comments'] = field_args['strip_comments']
def pre_save(self, model_instance, add):
"""
Clean text, update model and return cleaned text.
:param model_instance: (obj) model instance
:param add: default textfield parameter, unused
:return: clean text as unicode
"""
bleached = clean(getattr(model_instance, self.attname), **self.args)
setattr(model_instance, self.attname, bleached)
return bleached
У меня проблема в том, что self.args
Значение для всех полей в модели представляется значением последнего поля, загруженного в модель. Так, например, на этой модели:
class Writing(models.Model):
"""
Stores a single writing of a specific Form ( relation :model:`writings.WritingForm` ) and
Category ( relation :model:`writings.Category` ).
"""
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
help_text=trans("Author")
)
title = BleachedCharField(
max_length=200,
help_text=trans("Title")
)
created = models.DateTimeField(
auto_now_add=True,
help_text=trans("First created.")
)
edited = models.DateTimeField(
auto_now_add=True,
help_text=trans("Last edited.")
)
description = BleachedTextField(
blank=True,
help_text=trans("A short description of the writing to entice potential readers.")
)
body = BleachedTextField(
field_args=settings.PERMISSIVE_BLEACHFIELDS,
help_text=trans("The body of the writing itself.")
)
writing_form = models.ForeignKey(
WritingForm,
on_delete=models.CASCADE,
help_text=trans("Primary writing form.")
)
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
help_text=trans("Writing form category")
)
slug = models.SlugField(
editable=False,
help_text=trans("URL and SEO friendly lower-cased string."),
unique=True
)
comments = GenericRelation(settings.COMMENT_MODEL)
На этой модели body
Поле, которое является последним полем в модели, переопределяет self.args всех экземпляров BleachCharField и BleachedTextField перед ним, поэтому они все принимают одинаковые параметры.
Я что-то упустил по этому поводу? Является ли self.args добавленным не в поля, а в экземпляр модели? Поэтому последние настройки полей переопределяют все настройки полей? Как я должен делать это, чтобы избежать этой проблемы?
Обновить:
Для большей ясности я добавляю диктат BEACHFIELDS по умолчанию и диктат PERMISSIVE_BLEACHFIELDS:
BLEACHFIELDS = {
'tags': [],
'attributes': {},
'styles': [],
'protocols': [],
'strip': True,
'strip_comments': True
}
PERMISSIVE_BLEACHFIELDS = {
'tags': ['b', 'em', 'i', 'strong', 'span'],
'attributes': {'span': ['style']},
'styles': ['text-decoration', 'font-weight'],
'strip_comments': False
}
1 ответ
settings.BLEACHFIELDS
это один изменяемый словарь Так что все случаи " self.args
указать на тот же объект. Когда вы изменяете этот объект, это повлияет на все экземпляры.
self.args = settings.BLEACHFIELDS or None
Один из способов исправить это использовать copy.deepcopy()
import copy # standard library module
self.args = copy.deepcopy(settings.BLEACHFIELDS or {})
Также, self.args
не может быть None
, Это должен быть словарь, или более поздние строки вызовут ошибки.
Наконец, если все, что вы хотите сделать, это создать поверхностное слияние двух словарей, вы можете сделать это, используя **
Оператор распаковки (если вы используете Python 3.5+) Тогда вам не нужны все эти if
блоки.
self.args = {**settings.BLEACHFIELDS, **field_args}
Это создаст новый словарь. Но вложенные списки или словари будут использоваться совместно с другими экземплярами, поэтому не делайте никаких мутаций на вложенных структурах данных.