getattr() issue with copied model instance (2.2.16)

16 views
Skip to first unread message

Liam Boer

unread,
Sep 2, 2020, 11:37:16 AM9/2/20
to django...@googlegroups.com
Hello,

- python version: 3.7
- django version: 2.2.16

After upgrading django from 2.2.15 to 2.2.16, I am experiencing errors with the code shown below (see ----code----). Since 2.2.16 the "getattr(self.original, field_name)" line results in the following stack trace, which did not happen in 2.2.15:

--------stack trace---------
 File "/app/src/contrib/changed_model_fields/mixins.py", line 20, in save
    self.has_changed_fields(related_model)
  File "/app/src/contrib/changed_model_fields/mixins.py", line 44, in has_changed_fields
    print(getattr(self.original, field_name))
  File "/usr/local/lib/python3.7/site-packages/django/db/models/fields/related_descriptors.py", line 189, in __get__
    "%s has no %s." % (self.field.model.__name__, self.field.name)
src.core.models.Variant.product.RelatedObjectDoesNotExist: Variant has no product.
--------stack trace---------


The purpose of the "ChangedFieldMixin" is to determine if it contains any modified (changed) attributes. The patch notes of 2.2.16 mentions something, which I believe to be related to my issue: https://code.djangoproject.com/ticket/31863


----------code------------

from copy import copy


class ChangedFieldMixin(object):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.original = copy(self)


    def save(self, related_model: ModelType = None, *args, **kwargs):
        force_changed_field = kwargs.pop('force_changed_field', False)

        if self.has_changed_fields(related_model):
            ...

        super().save(*args, **kwargs)

    def has_changed_fields(self, related_model: ModelType):
        for field in self._meta.fields:
            field_name = str(field.name)

            if not getattr(self, field_name) == getattr(self.original, field_name):
                return True

        return False


class Variant(ChangedFieldMixin, models.Model):
    product = models.ForeignKey('Product', on_delete=models.CASCADE)
    code = models.CharField(max_length=255, null=True, blank=True)
    name = models.CharField(max_length=255, null=True, blank=True)
    key = models.CharField(max_length=255, unique=True, null=False, blank=False)
    quantity = models.PositiveIntegerField(null=True, blank=True)
    default_price = models.PositiveIntegerField(default=None, null=True, blank=True)

----------code------------

Is this expected behaviour now, or did the patch introduce a bug? Let me know if I can provide additional information for the subject.


Thanks in advance
Reply all
Reply to author
Forward
0 new messages