Wednesday, September 2, 2020

getattr() issue with copied model instance (2.2.16)

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

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAC%2BLcyiD9WQK9GX76d1seC%2Bq%2B0Oh%3DhO%3DCZx46-tUR2B-WCAALg%40mail.gmail.com.

No comments:

Post a Comment