Экземпляры 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}

Это создаст новый словарь. Но вложенные списки или словари будут использоваться совместно с другими экземплярами, поэтому не делайте никаких мутаций на вложенных структурах данных.

Другие вопросы по тегам