Django "can't set attribute" in model

Question:

I have a model like this one:

class MyReport(models.Model):
    group_id    = models.PositiveIntegerField(blank=False, null=False)
    test        = models.ForeignKey(Test, on_delete=models.CASCADE)
    owner       = models.ForeignKey(User, editable=False, default=get_current_user, on_delete=models.CASCADE)

    user_objects = UserFilterManager()

    @property
    def location(self):
        return self.test.location

to which I added a location property.
I get this error at run-time. I pasted only parts below my latest call, which was to len() in this case (I know, use count instead).

  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 240, in __len__
    self._fetch_all()
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 1074, in _fetch_all
    self._result_cache = list(self.iterator())
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 75, in __iter__
    setattr(obj, attr_name, row[col_pos])
AttributeError: can't set attribute

I cannot figure why the error? If I call it locationx then there is no error.

Are there some special property definition rules for Django?

How could I debug this?

I added the property since I added the test indirection and want to change as little code as possible. I could, of course, just change each report.location to report.test.location, but that’s a nuisance. It would be great if I could define an alias for DB searches so I would need to change those either.

EDIT: To answer the comments:

  1. I do not want to set the value, I just want a property (getter). I want to be able to do report.location instead adding an indirection report.test.location (don’t want to change existing code where location was inside the report model).
  2. self.test.location and report.test.location work, and also reports.locationx works as well.
  3. I mentioned this above, but I will repeat. All I did was to call len() on a query, e.g. len(MyReport.objects.all()).

Here is a full trace from the Eclipse console:

Traceback (most recent call last):
  File "C:Eclipse-SDK-4.2.1-win32-x86_64pluginsorg.python.pydev_2.7.1.2012100913pysrcpydevd_comm.py", line 765, in doIt
    result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec)
  File "C:Eclipse-SDK-4.2.1-win32-x86_64pluginsorg.python.pydev_2.7.1.2012100913pysrcpydevd_vars.py", line 378, in evaluateExpression
    sys.stdout.write('%sn' % (result,))
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 234, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 258, in __iter__
    self._fetch_all()
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 1074, in _fetch_all
    self._result_cache = list(self.iterator())
  File "C:Python27Libsite-packagesdjangodbmodelsquery.py", line 75, in __iter__
    setattr(obj, attr_name, row[col_pos])
AttributeError: can't set attribute
Asked By: mibm

||

Answers:

Try to ensure that test is set:

 @property
 def location(self):
     return self.test.location if self.test else None
Answered By: Igor Pomaranskiy

The problem was a name clash.

Apparently when querying the DB I had:

objs = MyReport.objects.annotate(location=F('test__location'))

This added location to the objects (didn’t see it in __dict__, but maybe I just missed it). This means I could give up the property since I could call report_instance.location. Of course, this means that all places that access MyReport I need to add the annotation (a special manager?).

Answered By: mibm

I have the same problem. I solved it by

@location.setter
def location(self, val):
    pass
Answered By: Ivan Lavrenov

Just had a similar problem that seemed to be resolved by making sure I didn’t annotate with an alias that was the same name as the property I was trying to call.

Answered By: GSchriver

Add a setter method and the MyReport.location will be able to be set.

class MyReport(models.Model):
    group_id    = models.PositiveIntegerField(blank=False, null=False)
    test        = models.ForeignKey(Test, on_delete=models.CASCADE)
    owner       = models.ForeignKey(User, editable=False, default=get_current_user, on_delete=models.CASCADE)

    user_objects = UserFilterManager()


    _location = None

    @property
    def location(self):
        if self._location is not None:
            return self._location
        return self.test.location

    @location.setter
    def location(self, value):
        self._location = value

Answered By: mazurekt
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.