Opened 9 years ago
Closed 9 years ago
#27419 closed Bug (needsinfo)
Model that worked before 1.10 causes "Cannot force an update in save() with no primary key." in 1.10.
| Reported by: | Louis-Dominique Dubeau | Owned by: | nobody |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 1.10 |
| Severity: | Normal | Keywords: | save model property |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
This problem does not happen prior to Django 1.10. I'm running this on Python 2.7.12 but I don't think the Python version is an issue. The following case is distilled form a full-fledged application that has run fine in production since Django 1.6. Upon upgrading to 1.10, I immediately got the error reported here.
Consider the following models.py file:
from __future__ import unicode_literals
from django.db import models
class Foo(models.Model):
a = models.CharField(max_length=10)
b = models.CharField(max_length=10)
parent = models.ForeignKey(
"self", related_name="children", null=True, blank=True)
class Bar(models.Model):
a = models.CharField(max_length=10)
_b = models.CharField(max_length=10, name="b", db_column="b")
parent = models.ForeignKey(
"self", related_name="children", null=True, blank=True)
@property
def b(self):
return self._b
@b.setter
def b(self, val):
print "SETTING B"
# This would make the problem disappear:
# self.__dict__["b"] = val
self._b = val
And the following tests.py file which is a sibling to models.py above:
from django.test import TestCase
from .models import *
class TestFoo(TestCase):
def test(self):
parent = Foo(a="parent_a", b="parent_b")
parent.save()
foo = Foo(a="1a", b="1b", parent=parent)
foo.save()
class TestBar(TestCase):
def test(self):
parent = Bar(a="parent_a", b="parent_b")
parent.save()
bar = Bar(a="1a", b="1b", parent=parent)
bar.save()
Running ./manage.py test results in:
$ ./manage.py test
Creating test database for alias 'default'...
SETTING B
SETTING B
E.
======================================================================
ERROR: test (myapp.tests.TestBar)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/ldd/src/django_issues/deferred_fields_in_1.10/issue/myapp/tests.py", line 19, in test
bar.save()
File "/home/ldd/src/django_issues/deferred_fields_in_1.10/issue-venv/local/lib/python2.7/site-packages/django/db/models/base.py", line 796, in save
force_update=force_update, update_fields=update_fields)
File "/home/ldd/src/django_issues/deferred_fields_in_1.10/issue-venv/local/lib/python2.7/site-packages/django/db/models/base.py", line 824, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/home/ldd/src/django_issues/deferred_fields_in_1.10/issue-venv/local/lib/python2.7/site-packages/django/db/models/base.py", line 880, in _save_table
raise ValueError("Cannot force an update in save() with no primary key.")
ValueError: Cannot force an update in save() with no primary key.
----------------------------------------------------------------------
Ran 2 tests in 0.002s
FAILED (errors=1)
Destroying test database for alias 'default'...
If I downgrade to Django 1.9, I get:
$ ./manage.py test Creating test database for alias 'default'... SETTING B SETTING B .. ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK Destroying test database for alias 'default'...
Attachments (2)
Change History (5)
by , 9 years ago
by , 9 years ago
comment:1 by , 9 years ago
comment:2 by , 9 years ago
The bug seems to be introduced here:
commit 7f51876f99851fdc3fef63aecdfbcffa199c26b9
Author: Anssi Kääriäinen <anssi.kaariainen@thl.fi>
Date: Tue Feb 2 11:33:09 2016 +0200
Fixed #26207 -- Replaced dynamic classes with non-data descriptors for deferred instance loading.
:040000 040000 bc475a97153a13ed83629d19d53f4aff0cb064f6 0343367b1a537396c570ab15d6c8495b70e4700c M django
:040000 040000 439540406e70c8b9ef43db729dbec4cb473e7ee0 a4775e8e1bdb64eb549d000a9e6efcd58868b86e M docs
:040000 040000 cc14ca4b983abe3a6aa6f0f82b329375e7259df7 a9e3feb171711fe29098f1f3d2370f878f936c04 M tests
comment:3 by , 9 years ago
| Resolution: | → needsinfo |
|---|---|
| Status: | new → closed |
It seems to have to do with specifying a different name attribute ('b') on the field than the field name itself ('_b'). If I omit that attribute, the tests pass. I can't find documentation for passing a different name attribute. Maybe this is incorrect?
It's not obvious that this is a Django bug since you're doing some non-standard stuff with
setter. Could you try bisecting to find the commit where the behavior changed? A similar issue was reported on django-users.