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:
- 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).
self.test.location
and report.test.location
work, and also reports.locationx
works as well.
- 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
Answers:
Try to ensure that test
is set:
@property
def location(self):
return self.test.location if self.test else None
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?).
I have the same problem. I solved it by
@location.setter
def location(self, val):
pass
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.
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
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:
- 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 indirectionreport.test.location
(don’t want to change existing code where location was inside the report model). self.test.location
andreport.test.location
work, and alsoreports.locationx
works as well.- 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
Try to ensure that test
is set:
@property
def location(self):
return self.test.location if self.test else None
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?).
I have the same problem. I solved it by
@location.setter
def location(self, val):
pass
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.
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