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.