Django

Code

Ticket #535: django-admin-refactor-6.patch

File django-admin-refactor-6.patch, 75.8 kB (added by robert@wittams.com, 3 years ago)

Updated patch

  • django/conf/urls/admin.py

    old new  
    4848 
    4949urlpatterns += ( 
    5050    # Metasystem admin pages 
     51    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_old/$', 'django.views.admin.main.add_stage'), 
     52    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_old/$', 'django.views.admin.main.change_stage'), 
    5153    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 
    52     ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage'), 
     54    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage_new'), 
    5355    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), 
    5456    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/history/$', 'django.views.admin.main.history'), 
    5557    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/delete/$', 'django.views.admin.main.delete_stage'), 
    56     ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage'), 
     58    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage_new'), 
    5759) 
    5860urlpatterns = patterns('', *urlpatterns) 
  • django/conf/admin_templates/admin_change_form.html

    old new  
     1{% extends "base_site" %} 
     2{% load admin_modify %} 
     3{% load adminmedia %} 
     4{% block extrahead %} 
     5  
     6   {% for js in javascript_imports %} 
     7      {% include_admin_script js %} 
     8   {% endfor %} 
     9 
     10{% endblock %} 
     11 
     12{% block coltype %}{{ coltype }}{% endblock %} 
     13 
     14{% block bodyclass %}{{app_label}}-{{object_name.lower}} change-form{% endblock %} 
     15 
     16{% block breadcrumbs %}{% if not is_popup %} 
     17<div class="breadcrumbs"> 
     18     <a href="../../../">Home</a> &rsaquo; 
     19     <a href="../">{{verbose_name_plural|capfirst}}</a> &rsaquo; 
     20     {% if add %} 
     21        Add {{verbose_name}} 
     22     {% else %} 
     23        {{original|striptags|truncatewords:"18"}} 
     24     {% endif %} 
     25</div> 
     26{% endif %} 
     27{% endblock %} 
     28 
     29{% block content %}<div id="content-main"> 
     30{% if change %} 
     31   {% if not is_popup %} 
     32      <ul class="object-tools"><li><a href="history/" class="historylink">History</a></li> 
     33      {% if has_absolute_url %} 
     34         <li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li> 
     35      {% endif%} 
     36      </ul> 
     37   {% endif %} 
     38{% endif %} 
     39 
     40<form {{ form_enc_attrib }} action='{{ form_url }}' method="post"> 
     41 
     42{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} 
     43 
     44{% if save_on_top %} 
     45   {% submit_row %} 
     46{% endif %} 
     47 
     48{% if form.error_dict %} 
     49   <p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p> 
     50{% endif %} 
     51<b> 
     52</b> 
     53{% for fieldset in admin_fieldsets %} 
     54   <fieldset class="module aligned {{ fieldset.classes }}"> 
     55    {% if fieldset.name %} 
     56   <h2>{{fieldset.name }}</h2>  
     57    {% endif %} 
     58    {% for bound_field_set in fieldset.bound_field_sets %} 
     59        {% for bound_field in bound_field_set %} 
     60            {% admin_field_bound bound_field %} 
     61            {% filter_interface_script_maybe bound_field %}  
     62        {% endfor %}  
     63    {% endfor %} 
     64   </fieldset> 
     65{% endfor %} 
     66 
     67{% if change %} 
     68   {% if ordered_objects %} 
     69   <fieldset class="module"><h2>Ordering</h2> 
     70   <div class="form-row{% if form.order_.errors %} error{% endif %} "> 
     71   {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %} 
     72   <p><label for="id_order_">Order:</label> {{ form.order_ }}</p> 
     73   </div></fieldset> 
     74   {% endif %} 
     75{% endif %} 
     76 
     77 
     78{% for relation in inline_related_objects %} 
     79    {% edit_inline relation %} 
     80{% endfor %} 
     81 
     82{% submit_row %} 
     83 
     84{% if add %} 
     85   <script type="text/javascript">document.getElementById("id_{{first_field}}").focus();</script>' 
     86{% endif %} 
     87 
     88{% if auto_populated_fields %} 
     89   <script type="text/javascript"> 
     90   {% auto_populated_field_script auto_populated_fields %}  
     91   </script> 
     92{% endif %} 
     93 
     94{% if change %} 
     95   {% if ordered_objects %} 
     96      {% if form.order_objects %}<ul id="orderthese"> 
     97          {% for object in form.order_objects %} 
     98             <li id="p{% firstof ordered_object_names %}"> 
     99             <span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span> 
     100             </li> 
     101          {% endfor%} 
     102      {% endif %} 
     103   {% endif %} 
     104{% endif%} 
     105</form> 
     106 
     107{% endblock %} 
  • django/conf/admin_templates/admin_edit_inline_stacked.html

    old new  
     1<fieldset class="module aligned"> 
     2   {% for ow in form_object_wrapper_list %}  
     3      <h2>{{relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2> 
     4      {% if ow.show_url %}{% if ow.obj.original %} 
     5      <p><a href="/r/{{ ow.obj.original.content_type_id }}/{{ ow.obj.original.id }}/">View on site</a></p> 
     6      {% endif %}{% endif %} 
     7      {% for bound_field in ow.bound_fields %} 
     8         {% if bound_field.not_in_table %} 
     9            {% field_widget bound_field %} 
     10         {% else %} 
     11            {% admin_field_bound bound_field %} 
     12         {% endif %} 
     13      {% endfor %} 
     14    {%endfor%} 
     15</fieldset> 
     16 
  • django/conf/admin_templates/admin_field.html

    old new  
     1<div class="{{ class_names }}"> 
     2   {% for bound_field in bound_fields %} 
     3      {% if bound_field.field.errors %} 
     4         {{ bound_field.field.html_error_list }} 
     5      {% endif %} 
     6   {% endfor %} 
     7 
     8   {% for bound_field in bound_fields %} 
     9      {% if bound_field.has_label_first %} 
     10         {% field_label bound_field %} 
     11      {% endif %} 
     12       
     13      {% field_widget bound_field %} 
     14 
     15      {% if not bound_field.has_label_first %} 
     16         {% field_label bound_field %} 
     17      {% endif %} 
     18 
     19      {% if change %} 
     20         {% if bound_field.field.primary_key %} 
     21            {{ bound_field.original_value }}  
     22         {% endif %} 
     23 
     24         {% if bound_field.raw_id_admin %} 
     25            {% if bound_field.existing_repr %} 
     26                &nbsp;<strong>{{ bound_field.existing_repr|truncatewords:"14" }}</strong> 
     27            {% endif %} 
     28         {% endif %} 
     29      {% endif %} 
     30 
     31      {% if bound_field.field.help_text %} 
     32        <p class="help"> 
     33           {{bound_field.field.help_text}} 
     34        </p> 
     35      {% endif %} 
     36   {% endfor %} 
     37 
     38</div> 
  • django/conf/admin_templates/admin_field_widget.html

    old new  
     1{% if bound_field.is_date_time %} 
     2   <p class="datetime">  
     3      Date: {{ bound_field.form_fields.0 }}<br /> 
     4      Time: {{ bound_field.form_fields.1 }} 
     5   </p> 
     6{% else %} 
     7    {% if bound_field.is_file_field %} 
     8        {% if bound_field.original_value %} 
     9            Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br /> 
     10            Change: {% output_all bound_field.form_fields %} 
     11        {% else %} 
     12            {% output_all bound_field.form_fields %} 
     13        {% endif %} 
     14    {% else %} 
     15        {% output_all bound_field.form_fields %} 
     16        {% if bound_field.raw_id_admin %} 
     17            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a> 
     18        {% else  %} 
     19            {% if bound_field.needs_add_label %} 
     20            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.element_id}}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a> 
     21            {% endif %} 
     22        {% endif %} 
     23    {% endif %} 
     24{% endif %} 
     25     
     26 
     27 
  • django/conf/admin_templates/admin_edit_inline_tabular.html

    old new  
     1<fieldset class="module"> 
     2   <h2>{{relation.opts.verbose_name_plural|capfirst}}</h2><table> 
     3   <thead><tr> 
     4   {% for fw in field_wrapper_list %} 
     5      {% if fw.needs_header %} 
     6         <th{{fw.header_class_attribute}}> {{fw.field.verbose_name|capfirst}}  </th> 
     7      {% endif %} 
     8   {% endfor %} 
     9   {% for ow in form_object_wrapper_list %}  
     10       
     11      {% if change %}{% if original_row_needed %} 
     12         {% if ow.obj.original %} 
     13            <tr class="row-label {% cycle row1,row2 %}"><td colspan="{{num_headers}}"><strong>{{ ow.obj.original }}</strong></tr> 
     14         {% endif %} 
     15      {% endif %}{% endif %} 
     16      {% if ow.has_errors %} 
     17         <tr class="errorlist"><td colspan="{{num_headers}}"> 
     18            {{ ow.html_combined_error_list }} 
     19         </tr> 
     20      {% endif %} 
     21      <tr class="{% cycle row1,row2 %}"> 
     22      {% for bound_field in ow.bound_fields %} 
     23         {% if not bound_field.not_in_table %} 
     24         <td "{{ bound_field.cell_class_attribute}}">  
     25            {% field_widget bound_field %} 
     26         </td> 
     27         {% endif %} 
     28      {% endfor %} 
     29      {% if ow.show_url %}<td> 
     30         {% if ow.obj.original %}<a href="/r/{{ ow.obj.original.content_type_id }}/{{ ow.obj.original.id }}/">View on site</a>{% endif %}  
     31      </td>{% endif %} 
     32      </tr> 
     33 
     34   {% endfor %} </table> 
     35 
     36   {% for ow in form_object_wrapper_list %} 
     37      {% for bound_field in ow.bound_fields %} 
     38         {% if bound_field.not_in_table %} 
     39            {% field_widget bound_field %} 
     40         {% endif %} 
     41      {% endfor %} 
     42   {% endfor %} 
     43</fieldset> 
     44 
  • django/conf/settings.py

    old new  
    2626    if not me.SETTINGS_MODULE: # If it's set but is an empty string. 
    2727        raise KeyError 
    2828except KeyError: 
    29     raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE 
     29    raise EnvironmentError, "Environment variable %s is undefined." % (ENVIRONMENT_VARIABLE) 
    3030 
    3131try: 
    3232    mod = __import__(me.SETTINGS_MODULE, '', '', ['']) 
  • django/core/formfields.py

    old new  
    2222        for field in self.fields: 
    2323            if field.field_name == field_name: 
    2424                return field 
    25         raise KeyError, "Field %s not found" % field_name 
     25        raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields))  
    2626 
    2727    def __delitem__(self, field_name): 
    2828        "Deletes the field with the given field name; raises KeyError on failure" 
     
    8787        must happen after validation because html2python functions aren't 
    8888        expected to deal with invalid input. 
    8989        """ 
    90         for field in self.fields: 
    91             if new_data.has_key(field.field_name): 
    92                 new_data.setlist(field.field_name, 
    93                     [field.__class__.html2python(data) for data in new_data.getlist(field.field_name)]) 
    94             else: 
    95                 try: 
    96                     # individual fields deal with None values themselves 
    97                     new_data.setlist(field.field_name, [field.__class__.html2python(None)]) 
    98                 except EmptyValue: 
    99                     new_data.setlist(field.field_name, []) 
     90        """ 
     91        for field in self.fields: 
     92        """ 
    10093 
     94        for field in self.fields: 
     95            field.convert_post_data(new_data) 
     96 
    10197class FormWrapper: 
    10298    """ 
    10399    A wrapper linking a Manipulator to the template system. 
    104100    This allows dictionary-style lookups of formfields. It also handles feeding 
    105101    prepopulated data and validation error messages to the formfield objects. 
    106102    """ 
    107     def __init__(self, manipulator, data, error_dict): 
     103    def __init__(self, manipulator, data, error_dict, edit_inline = False): 
    108104        self.manipulator, self.data = manipulator, data 
    109105        self.error_dict = error_dict 
     106        self._inline_collections = None 
     107        self.edit_inline = edit_inline 
    110108 
    111109    def __repr__(self): 
    112         return repr(self.data
     110        return repr(self.__dict__
    113111 
    114112    def __getitem__(self, key): 
    115113        for field in self.manipulator.fields: 
    116114            if field.field_name == key: 
    117                 if hasattr(field, 'requires_data_list') and hasattr(self.data, 'getlist'): 
    118                     data = self.data.getlist(field.field_name) 
    119                 else: 
    120                     data = self.data.get(field.field_name, None) 
    121                 if data is None: 
    122                     data = '' 
    123                 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 
     115                data = field.extract_data(self.data) 
     116                return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 
     117        if self.edit_inline: 
     118            self.fill_inline_collections()  
     119            for inline_collection in self._inline_collections: 
     120                if inline_collection.name == key: 
     121                    return inline_collection 
     122 
    124123        raise KeyError 
    125124 
     125    def fill_inline_collections(self):  
     126        if not self._inline_collections: 
     127            ic = [] 
     128            related_objects = self.manipulator.get_inline_related_objects_wrapped() 
     129            for rel_obj in related_objects: 
     130                data = rel_obj.extract_data(self.data) 
     131                inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict) 
     132                ic.append(inline_collection) 
     133            self._inline_collections = ic 
     134 
     135 
     136 
    126137    def has_errors(self): 
    127138        return self.error_dict != {} 
    128139 
     
    135146    def __str__(self): 
    136147        "Renders the field" 
    137148        return str(self.formfield.render(self.data)) 
     149             
    138150 
    139151    def __repr__(self): 
    140152        return '<FormFieldWrapper for "%s">' % self.formfield.field_name 
     
    155167        else: 
    156168            return '' 
    157169 
     170    def get_id(self): 
     171        return  self.formfield.get_id() 
     172 
    158173class FormFieldCollection(FormFieldWrapper): 
    159174    "A utility class that gives the template access to a dict of FormFieldWrappers" 
    160175    def __init__(self, formfield_dict): 
     
    177192            errors.extend(field.errors()) 
    178193        return errors 
    179194 
     195 
     196 
     197    
     198 
     199class InlineObjectCollection: 
     200    "An object that acts like a list of form field collections."   
     201    def __init__(self, parent_manipulator,  rel_obj, data, errors): 
     202        self.parent_manipulator = parent_manipulator  
     203        self.rel_obj = rel_obj 
     204        self.data = data 
     205        self.errors = errors 
     206        self._collections = None    
     207        self.name = rel_obj.name 
     208  
     209    def __len__(self): 
     210        self.fill()  
     211        return self._collections.__len__() 
     212     
     213    def __getitem__(self, k): 
     214        self.fill() 
     215        return self._collections.__getitem__(k) 
     216 
     217    def __setitem__(self, k, v): 
     218        self.fill() 
     219        return self._collections.__setitem__(k,v) 
     220 
     221    def __delitem__(self, k): 
     222        self.fill() 
     223        return self._collections.__delitem__(k) 
     224 
     225    def __iter__(self): 
     226        self.fill() 
     227        return self._collections.__iter__() 
     228 
     229    def fill(self): 
     230        if self._collections: 
     231            return  
     232        else: 
     233            var_name = self.rel_obj.opts.object_name.lower() 
     234            wrapper = [] 
     235            orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object  or None  
     236            orig_list = self.rel_obj.get_list(orig) 
     237            for i, instance in enumerate(orig_list): 
     238                collection = {'original': instance } 
     239                for f in self.rel_obj.editable_fields(): 
     240                        for field_name in f.get_manipulator_field_names(''): 
     241                            full_field_name = '%s.%d.%s' % (var_name, i, field_name) 
     242                            field = self.parent_manipulator[full_field_name] 
     243                            data = field.extract_data(self.data) 
     244                            collection[field_name] = FormFieldWrapper(field, data, self.errors.get(full_field_name, [])) 
     245                wrapper.append(FormFieldCollection(collection)) 
     246            self._collections = wrapper  
     247 
    180248class FormField: 
    181249    """Abstract class representing a form field. 
    182250 
     
    209277    def render(self, data): 
    210278        raise NotImplementedError 
    211279 
     280    def get_member_name(self): 
     281        if hasattr(self, 'member_name'): 
     282            return self.member_name 
     283        else: 
     284            return self.field_name 
     285 
     286    def extract_data(self, data_dict): 
     287        if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 
     288            data = data_dict.getlist(self.get_member_name()) 
     289        else: 
     290            data = data_dict.get(self.get_member_name(), None) 
     291        if data is None: 
     292            data = '' 
     293        self.data_dict = data_dict   
     294        return data 
     295 
     296    def convert_post_data(self, new_data): 
     297        name = self.get_member_name() 
     298        if new_data.has_key(self.field_name): 
     299            d = new_data.getlist(self.field_name) 
     300            #del new_data[self.field_name] 
     301            new_data.setlist(name, 
     302                    [self.__class__.html2python(data)  
     303                     for data in d]) 
     304        else: 
     305            try: 
     306               # individual fields deal with None values themselves 
     307               new_data.setlist(name, [self.__class__.html2python(None)]) 
     308            except EmptyValue: 
     309               new_data.setlist(name, []) 
     310 
     311    def get_id(self): 
     312        return  FORM_FIELD_ID_PREFIX + self.field_name   
    212313#################### 
    213314# GENERIC WIDGETS  # 
    214315#################### 
     
    237338        if isinstance(data, unicode): 
    238339            data = data.encode('utf-8') 
    239340        return '<input type="text" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    240             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 
     341            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    241342            self.field_name, self.length, escape(data), maxlength) 
    242343 
    243344    def html2python(data): 
     
    248349    def render(self, data): 
    249350        # value is always blank because we never want to redisplay it 
    250351        return '<input type="password" id="%s" class="v%s%s" name="%s" value="" />' % \ 
    251             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 
     352            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    252353            self.field_name) 
    253354 
    254355class LargeTextField(TextField): 
     
    266367        if isinstance(data, unicode): 
    267368            data = data.encode('utf-8') 
    268369        return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
    269             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 
     370            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    270371            self.field_name, self.rows, self.cols, escape(data)) 
    271372 
    272373class HiddenField(FormField): 
     
    276377 
    277378    def render(self, data): 
    278379        return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
    279             (FORM_FIELD_ID_PREFIX + self.field_name, self.field_name, escape(data)) 
     380            (self.get_id(), self.field_name, escape(data)) 
    280381 
    281382class CheckboxField(FormField): 
    282383    def __init__(self, field_name, checked_by_default=False): 
     
    289390        if data or (data is '' and self.checked_by_default): 
    290391            checked_html = ' checked="checked"' 
    291392        return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
    292             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, 
     393            (self.get_id(), self.__class__.__name__, 
    293394            self.field_name, checked_html) 
    294395 
    295396    def html2python(data): 
     
    299400        return False 
    300401    html2python = staticmethod(html2python) 
    301402 
     403 
    302404class SelectField(FormField): 
    303     def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[]): 
     405    def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None): 
    304406        self.field_name = field_name 
    305407        # choices is a list of (value, human-readable key) tuples because order matters 
    306408        self.choices, self.size, self.is_required = choices, size, is_required 
    307409        self.validator_list = [self.isValidChoice] + validator_list 
     410        if member_name != None: 
     411            self.member_name = member_name 
    308412 
    309413    def render(self, data): 
     414        str_data = str(data) # normalize to string 
    310415        output = ['<select id="%s" class="v%s%s" name="%s" size="%s">' % \ 
    311             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 
    312             self.field_name, self.size)] 
    313         str_data = str(data) # normalize to string 
     416            (self.get_id(), self.__class__.__name__,  
     417             self.is_required and ' required' or '', self.field_name, self.size)] 
    314418        for value, display_name in self.choices: 
    315419            selected_html = '' 
    316420            if str(value) == str_data: 
     
    334438    html2python = staticmethod(html2python) 
    335439 
    336440class RadioSelectField(FormField): 
    337     def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[]): 
     441    def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None): 
    338442        self.field_name = field_name 
    339443        # choices is a list of (value, human-readable key) tuples because order matters 
    340444        self.choices, self.is_required = choices, is_required 
    341445        self.validator_list = [self.isValidChoice] + validator_list 
    342446        self.ul_class = ul_class 
     447        if member_name != None: 
     448            self.member_name = member_name 
    343449 
    344450    def render(self, data): 
    345451        """ 
     
    382488                'value': value, 
    383489                'name': display_name, 
    384490                'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
    385                     (FORM_FIELD_ID_PREFIX + self.field_name + '_' + str(i), self.field_name, value, selected_html), 
     491                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html), 
    386492                'label': '<label for="%s">%s</label>' % \ 
    387                     (FORM_FIELD_ID_PREFIX + self.field_name + '_' + str(i), display_name), 
     493                    (self.get_id() + '_' + str(i), display_name), 
    388494            }) 
    389495        return RadioFieldRenderer(datalist, self.ul_class) 
    390496 
     
    414520    requires_data_list = True 
    415521    def render(self, data): 
    416522        output = ['<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \ 
    417             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 
     523            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    418524            self.field_name, self.size)] 
    419525        str_data_list = map(str, data) # normalize to strings 
    420526        for value, choice in self.choices: 
     
    469575            if str(value) in str_data_list: 
    470576                checked_html = ' checked="checked"' 
    471577            field_name = '%s%s' % (self.field_name, value) 
    472             output.append('<li><input type="checkbox" id="%s%s" class="v%s" name="%s"%s /> <label for="%s%s">%s</label></li>' % \ 
    473                 (FORM_FIELD_ID_PREFIX, field_name, self.__class__.__name__, field_name, checked_html, 
    474                 FORM_FIELD_ID_PREFIX, field_name, choice)) 
     578            output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \ 
     579                (get_id() + value , self.__class__.__name__, field_name, checked_html, 
     580                get_id() + value, choice)) 
    475581        output.append('</ul>') 
    476582        return '\n'.join(output) 
    477583 
     
    490596 
    491597    def render(self, data): 
    492598        return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    493             (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, 
     599            (self.get_id(), self.__class__.__name__, 
    494600            self.field_name) 
    495601 
    496602    def html2python(data): 
  • django/core/meta/__init__.py

    old new  
    146146class BadKeywordArguments(Exception): 
    147147    pass 
    148148 
     149 
     150class InlineRelatedObject(object): 
     151    def __init__(self,parent_opts, opts, field): 
     152        self.parent_opts = parent_opts 
     153        self.opts = opts 
     154        self.field = field 
     155        self.name = opts.module_name 
     156 
     157    def flatten_data(self,obj = None): 
     158        var_name = self.opts.object_name.lower() 
     159        new_data = {} 
     160        rel_instances = self.get_list(obj) 
     161 
     162        for i, rel_instance in enumerate(rel_instances): 
     163            instance_data = {}  
     164            for f in self.opts.fields + self.opts.many_to_many: 
     165                field_data = f.flatten_data(rel_instance) 
     166                #if hasattr(f, 'editable') and f.editable and f != self.field: 
     167                for name, value in field_data.items(): 
     168                    instance_data['%s.%d.%s' % (var_name, i, name)] = value 
     169            new_data.update(instance_data)              
     170     
     171        return new_data         
     172 
     173    def extract_data(self, data): 
     174        "Pull out the data meant for inline objects of this class, ie anything starting with our module name" 
     175        return data # TODO   
     176     
     177    def get_list(self, parent_instance = None): 
     178        "Get the list of this type of object from an instance of the parent class" 
     179        if parent_instance != None: 
     180            func_name = 'get_%s_list' % self.parent_opts.get_rel_object_method_name(self.opts, self.field) 
     181            func = getattr(parent_instance, func_name) 
     182            list = func() 
     183             
     184            count = len(list) + self.field.rel.num_extra_on_change 
     185            if self.field.rel.min_num_in_admin: 
     186               count = max(count, self.field.rel.min_num_in_admin) 
     187            if self.field.rel.max_num_in_admin: 
     188               count = min(count, self.field.rel.max_num_in_admin) 
     189        
     190            change = count - len(list)  
     191            if change > 0: 
     192                return list + [None for _ in range(change)] 
     193            if change < 0: 
     194                return list[:change] 
     195            else: # Just right 
     196                return list 
     197        else: 
     198            return [None for _ in range(self.field.rel.num_in_admin)] 
     199 
     200     
     201    def editable_fields(self): 
     202        "Get the fields in this class that should be edited inline" 
     203        return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field ] 
     204         
     205    def __repr__(self): 
     206        return "<InlineRelatedObject: %s related to %s>" % ( self.name, self.field.name)       
     207 
     208 
     209 
    149210class Options: 
    150211    def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', 
    151212        fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, 
     
    317378    def get_inline_related_objects(self): 
    318379        return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] 
    319380 
     381    def get_inline_related_objects_wrapped(self): 
     382        return [InlineRelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline] 
     383 
     384    def get_data_holders(self): 
     385        return self.fields + self.many_to_many + self.get_inline_related_objects_wrapped() 
     386 
    320387    def get_all_related_many_to_many_objects(self): 
    321388        module_list = get_installed_model_modules() 
    322389        rel_objs = [] 
     
    594661            new_mod.get_latest = curry(function_get_latest, opts, new_class, does_not_exist_exception) 
    595662 
    596663        for f in opts.fields: 
     664            #TODO : change this into a virtual function so that user defined fields will be able to add methods to module or class.  
    597665            if f.choices: 
    598666                # Add "get_thingie_display" method to get human-readable value. 
    599667                func = curry(method_get_display_value, f) 
     
    788856        # If it does already exist, do an UPDATE. 
    789857        if cursor.fetchone(): 
    790858            db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 
    791             cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 
    792                 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 
     859            while 1: 
     860                try: 
     861                    idx = db_values.index('') 
     862                    non_pks[idx:idx+1] = [] 
     863                    db_values[idx:idx +1] = [] 
     864                except: break 
     865            cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table,  
     866                ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 
    793867                db_values + [pk_val]) 
    794868        else: 
    795869            record_exists = False 
     
    13321406        if f == '?': # Special case. 
    13331407            order_by.append(db.get_random_function_sql()) 
    13341408        else: 
     1409            if f.startswith('-'): 
     1410                col_name = f[1:] 
     1411                order = "DESC" 
     1412            else: 
     1413                col_name = f 
     1414                order = "ASC" 
    13351415            # Use the database table as a column prefix if it wasn't given, 
    13361416            # and if the requested column isn't a custom SELECT. 
    1337             if "." not in f and f not in [k[0] for k in kwargs.get('select', [])]: 
     1417            if "." not in col_name and col_name not in [k[0] for k in kwargs.get('select', [])]: 
    13381418                table_prefix = opts.db_table + '.' 
    13391419            else: 
    13401420                table_prefix = '' 
    1341             if f.startswith('-'): 
    1342                 order_by.append('%s%s DESC' % (table_prefix, orderfield2column(f[1:], opts))) 
    1343             else: 
    1344                 order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts))) 
     1421             
     1422            order_by.append('%s%s %s' % (table_prefix, orderfield2column(col_name, opts), order)) 
     1423     
    13451424    order_by = ", ".join(order_by) 
    13461425 
    13471426    # LIMIT and OFFSET clauses 
     
    13971476    man.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above. 
    13981477    man.__init__ = curry(manipulator_init, opts, add, change) 
    13991478    man.save = curry(manipulator_save, opts, klass, add, change) 
     1479    man.get_inline_related_objects_wrapped = curry(manipulator_get_inline_related_objects_wrapped, opts, klass, add, change) 
     1480    man.flatten_data = curry(manipulator_flatten_data, opts, klass, add, change) 
    14001481    for field_name_list in opts.unique_together: 
    14011482        setattr(man, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, opts)) 
    14021483    for f in opts.fields: 
     
    14391520            self.fields.extend(f.get_manipulator_fields(opts, self, change)) 
    14401521 
    14411522    # Add fields for related objects. 
    1442     for rel_opts, rel_field in opts.get_inline_related_objects(): 
     1523    for obj in opts.get_inline_related_objects_wrapped(): 
    14431524        if change: 
    1444             count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(rel_opts, rel_field))() 
    1445             count += rel_field.rel.num_extra_on_change 
    1446             if rel_field.rel.min_num_in_admin: 
    1447                 count = max(count, rel_field.rel.min_num_in_admin) 
    1448             if rel_field.rel.max_num_in_admin: 
    1449                 count = min(count, rel_field.rel.max_num_in_admin) 
     1525            count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(obj.opts, obj.field))() 
     1526            count += obj.field.rel.num_extra_on_change 
     1527            if obj.field.rel.min_num_in_admin: 
     1528                count = max(count, obj.field.rel.min_num_in_admin) 
     1529            if obj.field.rel.max_num_in_admin: 
     1530                count = min(count, obj.field.rel.max_num_in_admin) 
    14501531        else: 
    1451             count = rel_field.rel.num_in_admin 
    1452         for f in rel_opts.fields + rel_opts.many_to_many: 
    1453             if f.editable and f != rel_field and (not f.primary_key or (f.primary_key and change))
     1532            count = obj.field.rel.num_in_admin 
     1533        for f in obj.opts.fields + obj.opts.many_to_many: 
     1534            if f.editable and f != obj.field
    14541535                for i in range(count): 
    1455                     self.fields.extend(f.get_manipulator_fields(rel_opts, self, change, name_prefix='%s.%d.' % (rel_opts.object_name.lower(), i), rel=True)) 
     1536                    self.fields.extend(f.get_manipulator_fields(obj.opts, self, change, name_prefix='%s.%d.' % (obj.opts.object_name.lower(), i), rel=True)) 
    14561537 
    14571538    # Add field for ordering. 
    14581539    if change and opts.get_ordered_objects(): 
     
    15931674            getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) 
    15941675    return new_object 
    15951676 
     1677def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, self): 
     1678    return opts.get_inline_related_objects_wrapped()  
     1679         
     1680def manipulator_flatten_data(opts, klass, add, change, self): 
     1681     new_data = {} 
     1682     obj = change and self.original_object or None 
     1683     for f in opts.get_data_holders(): 
     1684            new_data.update(f.flatten_data(obj)) 
     1685     return new_data 
     1686 
    15961687def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): 
    15971688    from django.utils.text import get_text_list 
    15981689    field_list = [opts.get_field(field_name) for field_name in field_name_list] 
  • django/core/meta/fields.py

    </
    old new  
    1616BLANK_CHOICE_NONE = [("", "None")] 
    1717 
    1818# Values for Relation.edit_inline. 
    19 TABULAR, STACKED = 1, 2 
     19TABULAR, STACKED = "admin_edit_inline_tabular", "admin_edit_inline_stacked" 
    2020 
    2121RECURSIVE_RELATIONSHIP_CONSTANT = 'self' 
    2222 
     
    152152            if hasattr(self.default, '__get_value__'): 
    153153                return self.default.__get_value__() 
    154154            return self.default 
    155         if self.null: 
     155        if not self.empty_strings_allowed or self.null: 
    156156            return None 
    157157        return "" 
    158158 
     
    174174        if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter. 
    175175            params['maxlength'] = self.maxlength 
    176176        if isinstance(self.rel, ManyToOne): 
     177            params['member_name'] = name_prefix + self.get_db_column() 
    177178            if self.rel.raw_id_admin: 
    178179                field_objs = self.get_manipulator_field_objs() 
    179180                params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator)) 
    180181            else: 
    181182                if self.radio_admin: 
    182183                    field_objs = [formfields.RadioSelectField] 
    183                     params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
    184184                    params['ul_class'] = get_ul_class(self.radio_admin) 
    185185                else: 
    186186                    if self.null: 
    187187                        field_objs = [formfields.NullSelectField] 
    188188                    else: 
    189189                        field_objs = [formfields.SelectField] 
    190                     params['choices'] = self.get_choices() 
     190                params['choices'] = self.get_choices_default() 
    191191        elif self.choices: 
    192192            if self.radio_admin: 
    193193                field_objs = [formfields.RadioSelectField] 
    194                 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
    195194                params['ul_class'] = get_ul_class(self.radio_admin) 
    196195            else: 
    197196                field_objs = [formfields.SelectField] 
    198                 params['choices'] = self.get_choices() 
     197              
     198            params['choices'] = self.get_choices_default() 
    199199        else: 
    200200            field_objs = self.get_manipulator_field_objs() 
    201201 
     
    255255                val = None 
    256256            return val 
    257257 
     258 
    258259    def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 
    259260        "Returns a list of tuples used as SelectField choices for this field." 
     261        
    260262        first_choice = include_blank and blank_choice or [] 
    261263        if self.choices: 
    262264            return first_choice + list(self.choices) 
    263265        rel_obj = self.rel.to 
    264         return first_choice + [(getattr(x, rel_obj.pk.column), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)] 
     266        choices = first_choice + [(getattr(x, rel_obj.pk.column), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)] 
     267  
     268        return choices 
    265269 
     270 
     271    def get_choices_default(self): 
     272        if(self.radio_admin): 
     273            return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
     274        else: 
     275            return self.get_choices() 
     276 
     277    def _get_val_from_obj(self, obj): 
     278        if obj: 
     279           return getattr(obj, self.column)  
     280        else:  
     281           return self.get_default() 
     282 
     283    def flatten_data(self, obj = None): 
     284         """ 
     285             Returns a dictionary mapping the field's manipulator field names to its 
     286             "flattened" string values for the admin view. Obj is the instance to extract the  
     287             values from.  
     288         """ 
     289         return { self.get_db_column(): self._get_val_from_obj(obj)} 
     290         
     291  
    266292class AutoField(Field): 
    267293    empty_strings_allowed = False 
    268294    def __init__(self, *args, **kwargs):