Warn about mutable defaults with ArrayField
I found an issue when upgrading to Django 1.8 and using the new ArrayField and a model's instance methods.
This code will cause Model#array_field's data to bleed between different instances.
def add_to_array(self, item):
if item not in self.array_field:
self.array_field.append(item)
However, this code does the correct thing:
def add_to_array(self, item):
existing = self.array_field
if item not in self.array_field:
self.array_field = existing + [item]
I put together a django project/app with the code and tests that show the problems with self.array_field.append(). It also has a Vagrant file setup that should also exhibit the problems.
https://github.com/redbeard0x0a/django18bug
Change History
(11)
| Severity: |
Normal → Release blocker
|
| Triage Stage: |
Unreviewed → Accepted
|
| Resolution: |
→ worksforme
|
| Status: |
new → closed
|
| Component: |
contrib.postgres → Documentation
|
| Owner: |
set to nobody
|
| Resolution: |
worksforme
|
| Severity: |
Release blocker → Normal
|
| Status: |
closed → new
|
| Summary: |
ArrayField data bleeds between instances when using field.append(item) → Warn about mutable defaults with ArrayField
|
| Type: |
Bug → Cleanup/optimization
|
| Owner: |
changed from nobody to MZ
|
| Status: |
new → assigned
|
| Patch needs improvement: |
set
|
| Patch needs improvement: |
unset
|
| Patch needs improvement: |
set
|
| Patch needs improvement: |
unset
|
| Resolution: |
→ fixed
|
| Status: |
assigned → closed
|
The issue in your example code is that you have used
default=[]. This default is shared between all instances and in this case it is mutable, so you are modifying the default array.The correct code is to use
default=list.I don't consider this a bug in Django - this is the same behaviour as you would get if you have code like:
def myfunc(foo, bar=[]): bar.append(foo) return barThe first call to
myfunc(1)would return[1], the second[1, 1], etc.