Consider the following Django model:
class Outlet(models.Model):
a = models.CharField(max_length=253, primary_key=True, db_index=True)
def _split(self, idx):
if not self.a:
return None
return self.a.split(".")[idx]
@property
def b(self):
return self._split(0)
@property
def c(self):
return self._split(1)
Couple with this class registered in admin:
@admin.register(Outlet)
class OutletAdmin(admin.ModelAdmin):
fields = ("a", "b", "c")
readonly_fields = ("b", "c")
Start with an empty DB:
Add one instance in the admin UI:
Fine so far. Now go to modify that instance:
Huh? What just happened? Django just performed an INSERT when it should reliably be expected to perform an UPDATE.
Directly from How Django knows to UPDATE vs. INSERT:
You may have noticed Django database objects use the same save() method for creating and changing objects. Django abstracts the need to use INSERT or UPDATE SQL statements. Specifically, when you call save(), Django follows this algorithm:
- If the object’s primary key attribute is set to a value that evaluates to True (i.e., a value other than None or the empty string), Django executes an UPDATE.
- If the object’s primary key attribute is not set or if the UPDATE didn’t update anything, Django executes an INSERT.
Now, this question will tell me that this is because a
is a primary key. Firstly, I don't see that mentioned anywhere in the Django docs. Secondly, I'm not able to remove the primary_key
attribute from a
.
I'm more interested in knowing why this is the case, what causes it internally with Django, and where it is documented (if anywhere)? Are there other undocumented conditions wherein an INSERT gets done when you would expect an UPDATE to happen?