Django

Code

Changeset 7771

Show
Ignore:
Timestamp:
06/26/08 11:53:53 (1 year ago)
Author:
brosner
Message:

newforms-admin: Fixed #7541 -- RelatedFieldWidgetWrapper? now wraps the widget and not the just the render function which caused some stale values. Thanks lukas and Doug Napoleone.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin/django/contrib/admin/options.py

    r7638 r7771  
    199199            # Don't wrap raw_id fields. Their add function is in the popup window. 
    200200            if not db_field.name in self.raw_id_fields: 
    201                 formfield.widget.render = widgets.RelatedFieldWidgetWrapper(formfield.widget.render, db_field.rel, self.admin_site) 
     201                formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site) 
    202202            return formfield 
    203203         
  • django/branches/newforms-admin/django/contrib/admin/widgets.py

    r7626 r7771  
    22Form Widget classes specific to the Django admin site. 
    33""" 
     4 
     5import copy 
    46 
    57from django import newforms as forms 
     
    163165        return False 
    164166 
    165 class RelatedFieldWidgetWrapper(object): 
    166     """ 
    167     This class is a wrapper whose __call__() method mimics the interface of a 
    168     Widget's render() method. 
    169     """ 
    170     def __init__(self, render_func, rel, admin_site): 
    171         self.render_func, self.rel = render_func, rel 
     167class RelatedFieldWidgetWrapper(forms.Widget): 
     168    """ 
     169    This class is a wrapper to a given widget to add the add icon for the 
     170    admin interface. 
     171    """ 
     172    def __init__(self, widget, rel, admin_site): 
     173        self.is_hidden = widget.is_hidden 
     174        self.needs_multipart_form = widget.needs_multipart_form 
     175        self.attrs = widget.attrs 
     176        self.choices = widget.choices 
     177        self.widget = widget 
     178        self.rel = rel 
    172179        # so we can check if the related object is registered with this AdminSite 
    173180        self.admin_site = admin_site 
    174181 
    175     def __call__(self, name, value, *args, **kwargs): 
     182    def __deepcopy__(self, memo): 
     183        obj = copy.copy(self) 
     184        obj.widget = copy.deepcopy(self.widget, memo) 
     185        obj.attrs = self.widget.attrs 
     186        memo[id(self)] = obj 
     187        return obj 
     188 
     189    def render(self, name, value, *args, **kwargs): 
    176190        from django.conf import settings 
    177191        rel_to = self.rel.to 
    178192        related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    179         output = [self.render_func(name, value, *args, **kwargs)] 
     193        self.widget.choices = self.choices 
     194        output = [self.widget.render(name, value, *args, **kwargs)] 
    180195        if rel_to in self.admin_site._registry: # If the related object has an admin interface: 
    181196            # TODO: "id_" is hard-coded here. This should instead use the correct 
     
    186201        return mark_safe(u''.join(output)) 
    187202 
    188     def __deepcopy__(self, memo): 
    189         # There's no reason to deepcopy admin_site, etc, so just return self. 
    190         memo[id(self)] = self 
    191         return self 
     203    def build_attrs(self, extra_attrs=None, **kwargs): 
     204        "Helper function for building an attribute dictionary." 
     205        self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs) 
     206        return self.attrs 
     207 
     208    def value_from_datadict(self, data, files, name): 
     209        return self.widget.value_from_datadict(data, files, name) 
     210 
     211    def _has_changed(self, initial, data): 
     212        return self.widget._has_changed(initial, data) 
     213 
     214    def id_for_label(self, id_): 
     215        return self.widget.id_for_label(id_) 
  • django/branches/newforms-admin/tests/regressiontests/modeladmin/models.py

    r7626 r7771  
    123123<class 'django.contrib.admin.widgets.AdminDateWidget'> 
    124124 
     125If we need to override the queryset of a ModelChoiceField in our custom form 
     126make sure that RelatedFieldWidgetWrapper doesn't mess that up. 
     127 
     128>>> band2 = Band(name='The Beetles', bio='', sign_date=date(1962, 1, 1)) 
     129>>> band2.save() 
     130 
     131>>> class AdminConcertForm(forms.ModelForm): 
     132...     class Meta: 
     133...         model = Concert 
     134... 
     135...     def __init__(self, *args, **kwargs): 
     136...         super(AdminConcertForm, self).__init__(*args, **kwargs) 
     137...         self.fields["main_band"].queryset = Band.objects.filter(name='The Doors') 
     138 
     139>>> class ConcertAdmin(ModelAdmin): 
     140...     form = AdminConcertForm 
     141 
     142>>> ma = ConcertAdmin(Concert, site) 
     143>>> form = ma.get_form(request)() 
     144>>> print form["main_band"] 
     145<select name="main_band" id="id_main_band"> 
     146<option value="" selected="selected">---------</option> 
     147<option value="1">The Doors</option> 
     148</select> 
     149 
     150>>> band2.delete() 
     151 
    125152# radio_fields behavior ################################################ 
    126153 
    127154First, without any radio_fields specified, the widgets for ForeignKey 
    128155and fields with choices specified ought to be a basic Select widget. 
    129 For Select fields, all of the choices lists have a first entry of dashes. 
     156ForeignKey widgets in the admin are wrapped with RelatedFieldWidgetWrapper so 
     157they need to be handled properly when type checking. For Select fields, all of 
     158the choices lists have a first entry of dashes. 
    130159 
    131160>>> cma = ModelAdmin(Concert, site) 
    132161>>> cmafa = cma.get_form(request) 
    133162 
    134 >>> type(cmafa.base_fields['main_band'].widget
     163>>> type(cmafa.base_fields['main_band'].widget.widget
    135164<class 'django.newforms.widgets.Select'> 
    136165>>> list(cmafa.base_fields['main_band'].widget.choices) 
    137166[(u'', u'---------'), (1, u'The Doors')] 
    138167 
    139 >>> type(cmafa.base_fields['opening_band'].widget
     168>>> type(cmafa.base_fields['opening_band'].widget.widget
    140169<class 'django.newforms.widgets.Select'> 
    141170>>> list(cmafa.base_fields['opening_band'].widget.choices) 
     
    153182 
    154183Now specify all the fields as radio_fields.  Widgets should now be 
    155 RadioSelect, and the choices list should have a first entry of 'None' iff 
     184RadioSelect, and the choices list should have a first entry of 'None' if 
    156185blank=True for the model field.  Finally, the widget should have the 
    157186'radiolist' attr, and 'inline' as well if the field is specified HORIZONTAL. 
     
    168197>>> cmafa = cma.get_form(request) 
    169198 
    170 >>> type(cmafa.base_fields['main_band'].widget
     199>>> type(cmafa.base_fields['main_band'].widget.widget
    171200<class 'django.contrib.admin.widgets.AdminRadioSelect'> 
    172201>>> cmafa.base_fields['main_band'].widget.attrs 
     
    175204[(1, u'The Doors')] 
    176205 
    177 >>> type(cmafa.base_fields['opening_band'].widget
     206>>> type(cmafa.base_fields['opening_band'].widget.widget
    178207<class 'django.contrib.admin.widgets.AdminRadioSelect'> 
    179208>>> cmafa.base_fields['opening_band'].widget.attrs