Django Field Instances覆盖彼此的参数

时间:2018-10-18 16:51:35

标签: django field class-instance-variables

我正在测试并准备一个新的Django程序包,以将bleach与Django ORM中的Text和Char字段以及DRF一起使用。但是,我遇到了一些障碍,这让我停下来想知道我是否真正了解如何实例化模型字段。希望有人可以解决这个问题。

我正在通过从django.conf.settings加载默认设置dict初始化bleach的参数,然后检查field_args参数以查看是否已为特定的字段定义覆盖了任何参数,如下所示。然后在pre_save函数中使用它来调用漂白:

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字段是模型的最后一个字段,它将覆盖之前的所有BleachCharField和BleachedTextField实例的self.args,因此它们都具有相同的参数。

我在这方面缺少什么吗? 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 :(得分: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。必须是字典,否则以后的行会引发错误。

最后,如果您要做的只是创建两个字典的浅表合并,则可以使用**的unpack运算符(如果您使用的是Python 3.5+)来做到这一点,那么就不需要全部那些if块。

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

这将创建一个新字典。但是嵌套列表或字典将与其他实例共享,因此请勿对嵌套数据结构进行任何更改。