﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
12048	MultiWidget does not define __deepcopy__	powderflask <powderflask@…>	nobody	"django.forms.!MultiWidget defines an instance variable, {{{ widgets }}}, which is a list of widgets.  However, it does not override the __deepcopy__() method in the Widget base class to make a copy of this instance variable.  A deepcopy of the widgets is needed when an instance when a django Form containing a Field that uses a !MultiWidget is instantiated, otherwise all such forms have a reference to the same widgets list rather than their own copy.

The patch is simple - override __deepcopy__() in !MultiWidget to make a copy of widgets.  A proposed patch is attached.

Replicating the problem requires that we extend !MultiWidget, make a deepcopy of the widget, then alter one of the widgets in some way.  Here is a minimal example that demonstrates the issue - applying the patch fixes the issue.

{{{
from django import forms
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe

#####################################################
## Simplified ChoiceWithOtherWidget  
##   original downloaded from: http://www.djangosnippets.org/snippets/863/
#####################################################
class ChoiceWithOtherWidget(forms.MultiWidget):
    """"""MultiWidget for use with ChoiceWithOtherField.""""""
    def __init__(self, choices=[]):
        widgets = [
            forms.RadioSelect(choices=choices),
            forms.TextInput
        ]
        super(ChoiceWithOtherWidget, self).__init__(widgets)
    
    def _set_choices(self, choices):
        """""" When choices are set for this widget, we want to pass those along to the Select widget""""""
        self.widgets[0].choices = choices
    def _get_choices(self):
        """""" The choices for this widget are the Select widget's choices""""""
        return self.widgets[0].choices
    choices = property(_get_choices, _set_choices)
    
    def decompress(self, value):
        if not value:
            return [None, None]
        return value


#################################################
##  Minimal code to reproduces bug
#################################################
from copy import deepcopy

widget1 = ChoiceWithOtherWidget(choices=['A','B','C'])
widget2 = deepcopy(widget1)
widget2.choices=['X','Y','Z']

widget1.choices
widget2.choices
}}}

Here is the output from running the sample code above in a shell before the patch was applied:
{{{
>>> widget1 = ChoiceWithOtherWidget(choices=['A','B','C'])
>>> widget2 = deepcopy(widget1)
>>> widget2.choices=['X','Y','Z']
>>> 
>>> widget1.choices
['X', 'Y', 'Z']
>>> widget2.choices
['X', 'Y', 'Z']
}}}

... and after the patch was applied:

{{{
>>> widget1 = ChoiceWithOtherWidget(choices=['A','B','C'])
>>> widget2 = deepcopy(widget1)
>>> widget2.choices=['X','Y','Z']
>>> 
>>> widget1.choices
['A', 'B', 'C']
>>> widget2.choices
['X', 'Y', 'Z']
}}}
"		closed	Forms	dev		fixed		ben@…	Design decision needed	1	0	0	0	0	0
