Экземпляры Django Field перекрывают аргументы друг друга - PullRequest
0 голосов
/ 18 октября 2018

Я тестирую и готовлю новый пакет 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 Ответ

0 голосов
/ 18 октября 2018

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.Это должен быть словарь или более поздние строки вызовут ошибки.

Наконец, если все, что вам нужно, это создать поверхностное слияние двух словарей, вы можете сделать это с помощью оператора ** unpack (если вы используете python 3.5+). Тогда вам не нужновсе эти if блоки.

    self.args = {**settings.BLEACHFIELDS, **field_args}

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

...