Django

Code

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

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

patch that refactors data flattening

  • django/conf/urls/admin.py

    old new  
    4848 
    4949urlpatterns += ( 
    5050    # Metasystem admin pages 
     51    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_new/$', 'django.views.admin.main.add_stage_new'), 
     52    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_new/$', 'django.views.admin.main.change_stage_new'), 
    5153    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 
    5254    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage'), 
    5355    ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), 
  • 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                {% for field in bound_field.form_fields %} 
     62                    {% if field.needs_filter_script %} 
     63                   <script type="text/javascript"> 
     64                     addEvent(window, load, function(e){ 
     65                            SelectFilter.init("id_{{f.name}}", {{ f.verbose_name}}, {{f.filterthing}}); 
     66                     } 
     67                   </script> 
     68                {% endif %} 
     69            {% endfor %} 
     70         {% endfor%}  
     71      {% endfor %} 
     72   </fieldset> 
     73{% endfor %} 
     74 
     75{% if change %} 
     76   {% if ordered_objects %} 
     77   <fieldset class="module"><h2>Ordering</h2> 
     78   <div class="form-row{% if form.order_.errors %} error{% endif %} "> 
     79   {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %} 
     80   <p><label for="id_order_">Order:</label> {{ form.order_ }}</p> 
     81   </div></fieldset> 
     82   {% endif %} 
     83{% endif %} 
     84 
     85{% for relation in inline_related_objects %} 
     86    {% edit_inline relation %} 
     87{% endfor %} 
     88 
     89{% submit_row %} 
     90 
     91{% if add %} 
     92   <script type="text/javascript">document.getElementById("id_{{first_field}}").focus();</script>' 
     93{% endif %} 
     94 
     95{% if auto_populated_fields %} 
     96   <script type="text/javascript"> 
     97   {% auto_populated_field_script auto_populated_fields %}  
     98   </script> 
     99{% endif %} 
     100 
     101{% if change %} 
     102   {% if ordered_objects %} 
     103      {% if form.order_objects %}<ul id="orderthese"> 
     104          {% for object in form.order_objects %} 
     105             <li id="p{% firstof ordered_object_names %}"> 
     106             <span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span> 
     107             </li> 
     108          {% endfor%} 
     109      {% endif %} 
     110   {% endif %} 
     111{% endif%} 
     112</form> 
     113 
     114{% 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.label_name}}" 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.label_name}}"> <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.obj.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/core/formfields.py

    old new  
    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. 
     
    114110    def __getitem__(self, key): 
    115111        for field in self.manipulator.fields: 
    116112            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, [])) 
     113                 
     114                data = field.extract_data(self.data) 
     115                 
     116                return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 
    124117        raise KeyError 
    125118 
    126119    def has_errors(self): 
     
    209202    def render(self, data): 
    210203        raise NotImplementedError 
    211204 
     205    def get_member_name(self): 
     206        return self.field_name 
     207 
     208    def extract_data(self, data_dict): 
     209        if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 
     210            data = data_dict.getlist(self.get_member_name()) 
     211        else: 
     212            data = data_dict.get(self.get_member_name(), None) 
     213        if data is None: 
     214            data = '' 
     215        self.data_dict = data_dict   
     216        return data 
     217 
     218    def convert_post_data(self, new_data): 
     219        name = self.get_member_name() 
     220        if new_data.has_key(self.field_name): 
     221            d = new_data.getlist(self.field_name) 
     222            #del new_data[self.field_name] 
     223            new_data.setlist(name, 
     224                    [self.__class__.html2python(data)  
     225                     for data in d]) 
     226        else: 
     227            try: 
     228               # individual fields deal with None values themselves 
     229               new_data.setlist(name, [self.__class__.html2python(None)]) 
     230            except EmptyValue: 
     231               new_data.setlist(name, []) 
     232 
    212233#################### 
    213234# GENERIC WIDGETS  # 
    214235#################### 
     
    319340        output.append('  </select>') 
    320341        return '\n'.join(output) 
    321342 
     343    def get_member_name(self): 
     344        return "%s_id" % self.field_name 
     345 
    322346    def isValidChoice(self, data, form): 
    323347        str_data = str(data) 
    324348        str_choices = [str(item[0]) for item in self.choices] 
  • 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 
     156    def flatten_data(self,obj = None): 
     157        var_name = self.opts.object_name.lower() 
     158        new_data = {} 
     159        if obj == None: 
     160            rel_instances = [None for i in range(self.field.rel.num_in_admin)] 
     161        else: 
     162            method_name = 'get_%s_list' % self.parent_opts.get_rel_object_method_name(self.opts, self.field) 
     163            rel_instances =  getattr(obj, method_name)() 
     164 
     165        for i, rel_instance in enumerate(rel_instances): 
     166            for f in self.opts.fields + self.opts.many_to_many: 
     167                subdata = f.flatten_data(rel_instance) 
     168                if hasattr(f, 'editable') and f.editable and f != self.field: 
     169                    for name, value in subdata.items(): 
     170                        new_data['%s.%d.%s' % (var_name, i, name)] = value 
     171            
     172               #     new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] 
     173     
     174        return new_data         
     175 
     176 
     177 
    149178class Options: 
    150179    def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', 
    151180        fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, 
     
    317346    def get_inline_related_objects(self): 
    318347        return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] 
    319348 
     349    def get_inline_related_objects_wrapped(self): 
     350        return [InlineRelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline] 
     351 
     352    def get_data_holders(self): 
     353        return self.fields + self.many_to_many + self.get_inline_related_objects_wrapped() 
     354 
    320355    def get_all_related_many_to_many_objects(self): 
    321356        module_list = get_installed_model_modules() 
    322357        rel_objs = [] 
     
    592627            new_mod.get_latest = curry(function_get_latest, opts, new_class, does_not_exist_exception) 
    593628 
    594629        for f in opts.fields: 
     630            if len(f.choices) != 0: 
     631                # Add an accessor method to get to the human readable value 
     632                func = curry(method_get_display_value, f) 
     633                setattr(new_class, 'get_%s_display' % f.name , func) 
    595634            if isinstance(f, DateField) or isinstance(f, DateTimeField): 
    596635                # Add "get_next_by_thingie" and "get_previous_by_thingie" methods 
    597636                # for all DateFields and DateTimeFields that cannot be null. 
     
    782821        # If it does already exist, do an UPDATE. 
    783822        if cursor.fetchone(): 
    784823            db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 
    785             cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 
    786                 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 
     824            while 1: 
     825                try: 
     826                    idx = db_values.index('') 
     827                    non_pks[idx:idx+1] = [] 
     828                    db_values[idx:idx +1] = [] 
     829                except: break 
     830            cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table,  
     831                ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 
    787832                db_values + [pk_val]) 
    788833        else: 
    789834            record_exists = False 
     
    9911036    kwargs['limit'] = 1 
    9921037    return get_object_func(**kwargs) 
    9931038 
     1039# CHOICE-RELATED METHODS ################### 
     1040 
     1041def method_get_display_value(field, self): 
     1042    value = getattr(self, field.name) 
     1043    for (v, d) in field.choices: 
     1044        if v==value: 
     1045            return d 
     1046    # Couldn't find it in the list 
     1047    return value 
     1048 
    9941049# FILE-RELATED METHODS ##################### 
    9951050 
    9961051def method_get_file_filename(field, self): 
  • django/core/meta/fields.py

    old new  
    180180            else: 
    181181                if self.radio_admin: 
    182182                    field_objs = [formfields.RadioSelectField] 
    183                     params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
    184183                    params['ul_class'] = get_ul_class(self.radio_admin) 
    185184                else: 
    186185                    if self.null: 
    187186                        field_objs = [formfields.NullSelectField] 
    188187                    else: 
    189188                        field_objs = [formfields.SelectField] 
    190                     params['choices'] = self.get_choices() 
     189                params['choices'] = self.get_choices_default() 
    191190        elif self.choices: 
    192191            if self.radio_admin: 
    193192                field_objs = [formfields.RadioSelectField] 
    194                 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
    195193                params['ul_class'] = get_ul_class(self.radio_admin) 
    196194            else: 
    197195                field_objs = [formfields.SelectField] 
    198                 params['choices'] = self.get_choices() 
     196              
     197            params['choices'] = self.get_choices_default() 
    199198        else: 
    200199            field_objs = self.get_manipulator_field_objs() 
    201200 
     
    255254                val = None 
    256255            return val 
    257256 
     257 
    258258    def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 
    259259        "Returns a list of tuples used as SelectField choices for this field." 
     260        
    260261        first_choice = include_blank and blank_choice or [] 
    261262        if self.choices: 
    262263            return first_choice + list(self.choices) 
    263264        rel_obj = self.rel.to 
    264265        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)] 
    265266 
     267    def get_choices_default(self): 
     268        if(self.radio_admin): 
     269            return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 
     270        else: 
     271            return self.get_choices() 
     272 
     273    def _get_val_from_obj(self, obj): 
     274        return obj and getattr(obj, self.column) or self.get_default() 
     275 
     276    def flatten_data(self, obj = None): 
     277         """ 
     278             Returns a dictionary mapping the field's manipulator field names to its 
     279             "flattened" string values for the admin view. Obj is the instance to extract the  
     280             values from.  
     281         """ 
     282         return { self.get_db_column(): self._get_val_from_obj(obj)} 
     283         
     284  
    266285class AutoField(Field): 
    267286    empty_strings_allowed = False 
    268287    def __init__(self, *args, **kwargs): 
     
    327346    def get_manipulator_field_objs(self): 
    328347        return [formfields.DateField] 
    329348 
     349    def flatten_data(self, obj = None): 
     350        val = self._get_val_from_obj(obj) 
     351        return {self.get_db_column(): (val is not None and val.strftime("%Y-%m-%d") or '')} 
     352 
    330353class DateTimeField(DateField): 
    331354    def get_db_prep_save(self, value): 
    332355        # Casts dates into string format for entry into database. 
     
    356379            return datetime.datetime.combine(d, t) 
    357380        return self.get_default() 
    358381 
     382    def flatten_data(self,obj = None): 
     383        val = self._get_val_from_obj(obj)  
     384        date_field, time_field = self.get_manipulator_field_names('') 
     385        return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), 
     386                time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 
     387 
    359388class EmailField(Field): 
    360389    def get_manipulator_field_objs(self): 
    361390        return [formfields.EmailField] 
     
    539568    def get_manipulator_field_objs(self): 
    540569        return [formfields.TimeField] 
    541570 
     571    def flatten_data(self,obj = None): 
     572        val = self._get_val_from_obj(obj)  
     573        return {self.get_db_column(): (val is not None and val.strftime("%H:%M:%S") or '')}  
     574 
    542575class URLField(Field): 
    543576    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 
    544577        if verify_exists: 
     
    592625    def get_manipulator_field_objs(self): 
    593626        return [formfields.IntegerField] 
    594627 
     628    def flatten_data(self, obj = None): 
     629        if not obj:  
     630            # In required many-to-one fields with only one available choice, 
     631            # select that one available choice. Note: We have to check that 
     632            # the length of choices is *2*, not 1, because SelectFields always 
     633            # have an initial "blank" value. 
     634            if not self.blank and not self.rel.raw_id_admin and self.choices: 
     635               choice_list = self.get_choices_default() 
     636               if len(choice_list) == 2: 
     637                  return { self.name : choice_list[1][0] } 
     638        return Field.flatten_data(self, obj) 
     639 
    595640class ManyToManyField(Field): 
    596641    def __init__(self, to, **kwargs): 
    597642        kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural) 
     
    609654        if self.rel.raw_id_admin: 
    610655            return [formfields.CommaSeparatedIntegerField] 
    611656        else: 
    612             choices = self.get_choices(include_blank=False
     657            choices = self.get_choices_default(
    613658            return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] 
    614659 
     660    def get_choices_default(self): 
     661        Field.get_choices(self, include_blank=False) 
     662 
    615663    def get_m2m_db_table(self, original_opts): 
    616664        "Returns the name of the many-to-many 'join' table." 
    617665        return '%s_%s' % (original_opts.db_table, self.name) 
     
    632680                len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 
    633681                len(badkeys) == 1 and "is" or "are") 
    634682 
     683    def flatten_data(self, obj = None): 
     684        new_data = {}  
     685        if obj: 
     686            get_list_func = getattr(obj, 'get_%s_list' % self.rel.singular) 
     687            instance_ids = [getattr(instance, self.rel.to.pk.column) for instance in get_list_func()] 
     688            if self.rel.raw_id_admin: 
     689                 new_data[self.name] = ",".join([str(id) for id in instance_ids]) 
     690            elif not f.rel.edit_inline: 
     691                 new_data[self.name] = instance_ids  
     692        else: 
     693            # In required many-to-many fields with only one available choice, 
     694            # select that one available choice. 
     695            if not self.blank and not self.rel.edit_inline and not self.rel.raw_id_admin and self.choices: 
     696               choice_list = self.get_choices_default() 
     697               if len(choice_list) == 1: 
     698                   new_data[self.name] = [choices_list[0][0]] 
     699        return new_data 
     700 
     701 
    635702class OneToOneField(IntegerField): 
    636703    def __init__(self, to, to_field=None, **kwargs): 
    637704        kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID') 
     
    714781        Returns self.fields, except with fields as Field objects instead of 
    715782        field names. If self.fields is None, defaults to putting every 
    716783        non-AutoField field with editable=True in a single fieldset. 
     784         
     785        returns a list of lists of name, dict  
     786        the dict has attribs 'fields' and maybe 'classes'.  
     787        fields is a list of subclasses of Field.  
     788 
     789        Return value needs to be encapsulated. 
    717790        """ 
    718791        if self.fields is None: 
    719792            field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),) 
  • django/templatetags/admin_modify.py

    old new  
     1from django.core import template, template_loader, meta 
     2from django.conf.settings import ADMIN_MEDIA_PREFIX 
     3from django.utils.text import capfirst 
     4from django.utils.html import escape 
     5 
     6 
     7from django.views.admin.main import BoundField 
     8 
     9class IncludeAdminScriptNode(template.Node): 
     10      def __init__(self, var): 
     11         self.var = var 
     12  
     13      def render(self, context): 
     14        resolved = template.resolve_variable(self.var, context) 
     15        return '<script type="text/javascript" src="%s%s"></script>' % \ 
     16                (ADMIN_MEDIA_PREFIX, resolved) 
     17           
     18class SubmitRowNode(template.Node): 
     19      def __init__(self): 
     20          pass 
     21 
     22      def render(self, context): 
     23          change = context['change'] 
     24          add = context['add'] 
     25          show_delete = context['show_delete'] 
     26          ordered_objects = context['ordered_objects'] 
     27          save_as = context['save_as'] 
     28          has_delete_permission = context['has_delete_permission'] 
     29          is_popup = context['is_popup'] 
     30           
     31          t = ['<div class="submit-row">'] 
     32          onclick_attrib = ordered_objects and change and 'onclick="submitOrderForm();"' or ''  
     33           
     34          if not is_popup: 
     35                if has_delete_permission and (change or show_delete): 
     36                   t.append('<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>') 
     37                if change and save_as: 
     38                   t.append('<input type="submit" value="Save as new" name="_saveasnew" %s/>' %  onclick_attrib) 
     39                if (not save_as or add): 
     40                   t.append('<input type="submit" value="Save and add another" name="_addanother" %s/>' %  onclick_attrib) 
     41          t.append('<input type="submit" value="Save and continue editing" name="_continue" %s/>' %  onclick_attrib ) 
     42          t.append('<input type="submit" value="Save" class="default" %s/>' %  onclick_attrib) 
     43          t.append('</div>\n') 
     44          
     45          return ''.join(t) 
     46 
     47 
     48 
     49 
     50class AdminFieldBoundNode(template.Node): 
     51    def __init__(self, argument): 
     52        self.argument = argument 
     53     
     54    def render(self, context): 
     55        argument_val = template.resolve_variable(self.argument, context) 
     56        if (isinstance(argument_val, list)): 
     57            bound_fields = argument_val  
     58        else: 
     59            bound_fields = [argument_val] 
     60        add = context['add'] 
     61        change = context['change'] 
     62         
     63        context.push() 
     64        context['bound_fields'] = bound_fields 
     65        context['class_names'] = " ".join(self.get_class_names(bound_fields)) 
     66        t = template_loader.get_template("admin_field") 
     67        output =  t.render(context) 
     68        context.pop() 
     69           
     70        return output 
     71 
     72    def get_class_names(self, bound_fields): 
     73 
     74        class_names = ['form-row'] 
     75        for bound_field in bound_fields:  
     76            for f in bound_field.form_fields: 
     77                if f.errors(): 
     78                    class_names.append('errors') 
     79                    break 
     80           
     81        # Assumes BooleanFields won't be stacked next to each other! 
     82        if isinstance(bound_fields[0].field, meta.BooleanField): 
     83            class_names.append('checkbox-row') 
     84           
     85        return class_names 
     86        
     87class FieldWidgetNode(template.Node): 
     88    def __init__(self, bound_field_var): 
     89        self.bound_field_var = bound_field_var 
     90 
     91    def render(self, context): 
     92        bound_field = template.resolve_variable(self.bound_field_var, context) 
     93        add = context['add'] 
     94        change = context['change'] 
     95         
     96        context.push() 
     97        context['bound_field'] = bound_field 
     98        t = template_loader.get_template("admin_field_widget") 
     99        output =  t.render(context) 
     100        context.pop() 
     101           
     102        return output 
     103 
     104         
     105 
     106class FieldWrapper(object): 
     107    def __init__(self, field ): 
     108        self.field = field 
     109 
     110    def needs_header(self): 
     111        return not isinstance(self.field, meta.AutoField) 
     112 
     113    def header_class_attribute(self): 
     114        return self.field.blank and ' class="optional"' or '' 
     115 
     116    def use_raw_id_admin(self): 
     117         return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \ 
     118                and self.field.rel.raw_id_admin 
     119 
     120class FormObjectWrapper(object): 
     121    def __init__(self, obj, field_wrappers, i, object_name): 
     122        self.obj = obj 
     123        self.field_wrappers = field_wrappers 
     124       
     125        form_prefix = '%s.%s.' % ( object_name, i)  
     126      
     127        name_prefix = '' 
     128      
     129        self.bound_fields = [ BoundField(0, fw.field, obj['original'], form_prefix,  
     130                              name_prefix, True, self.resolve_form_fields(fw.field, name_prefix) ) \ 
     131                                for fw in self.field_wrappers ] 
     132 
     133     
     134    def resolve_form_fields(self,field, name_prefix): 
     135        return [self.obj[name] for name in field.get_manipulator_field_names(name_prefix)] 
     136 
     137#HACK 
     138    def has_errors(self): 
     139        return max([ bool( len( self.obj[fw.field.name].errors() ) )  for fw in self.field_wrappers])  
     140         
     141    def html_combined_error_list(self): 
     142        return ''.join( [ self.obj[fw.field.name].html_error_list() for fw in self.field_wrappers]) 
     143 
     144     
     145     
     146 
     147class EditInlineNode(template.Node): 
     148    def __init__(self, rel_var): 
     149        self.rel_var = rel_var 
     150     
     151    def render(self, context): 
     152        relation = template.resolve_variable(self.rel_var, context) 
     153        add, change = context['add'], context['change'] 
     154         
     155        context.push() 
     156 
     157        self.fill_context(relation, add, change, context) 
     158         
     159        if relation.field.rel.edit_inline == meta.TABULAR: 
     160            t = template_loader.get_template("admin_edit_inline_tabular") 
     161        else:  # meta.STACKED 
     162            t = template_loader.get_template("admin_edit_inline_stacked")     
     163        
     164        output = t.render(context) 
     165         
     166        context.pop() 
     167        return output 
     168 
     169        
     170    def fill_context(self, relation, add, change, context): 
     171        field_wrapper_list = [FieldWrapper(f) for f in relation.opts.fields + relation.opts.many_to_many if f.editable and f != relation.field] 
     172         
     173        var_name = relation.opts.object_name.lower() 
     174         
     175        form_objects = template.resolve_variable('form.%s' % relation.opts.module_name , context) 
     176        form_object_wrapper_list = [FormObjectWrapper(o,field_wrapper_list, i, var_name) for i,o in enumerate(form_objects)]  
     177    
     178        context['field_wrapper_list'] = field_wrapper_list 
     179        context['form_object_wrapper_list'] = form_object_wrapper_list 
     180        context['num_headers'] = len(field_wrapper_list) 
     181        context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list])  
     182        context['name_prefix'] = "%s." % (var_name,) 
     183    
     184class FieldLabelNode(template.Node): 
     185    def __init__(self, bound_field_var): 
     186        self.bound_field_var = bound_field_var 
     187         
     188    def render(self, context): 
     189        bound_field = template.resolve_variable(self.bound_field_var, context) 
     190        class_names = [] 
     191        if isinstance(bound_field.field, meta.BooleanField): 
     192            class_names.append("vCheckboxLabel") 
     193        else: 
     194            if not bound_field.field.blank: 
     195                class_names.append('required') 
     196            if bound_field.index > 0: 
     197                class_names.append('inline') 
     198         
     199        class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' 
     200        return '<label for="%s"%s>%s:</label> ' % (bound_field.label_name, class_str, capfirst(bound_field.field.verbose_name) ) 
     201 
     202class OutputAllNode(template.Node): 
     203    def __init__(self, form_fields_var): 
     204        self.form_fields_var = form_fields_var 
     205     
     206    def render(self, context): 
     207        form_fields = template.resolve_variable(self.form_fields_var, context) 
     208        return ''.join([str(f) for f in form_fields]) 
     209 
     210class AutoPopulatedFieldScriptNode(template.Node): 
     211    def __init__(self, auto_pop_var): 
     212        self.auto_pop_var = auto_pop_var 
     213 
     214    def render(self,context): 
     215        auto_pop_fields = template.resolve_variable(self, context) 
     216        change = context['change'] 
     217        for field in auto_populated_fields: 
     218            t = [] 
     219            if change: 
     220                t.append('document.getElementById("id_%s")._changed = true;' % field.name ) 
     221            else:  
     222                t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 
     223 
     224            add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) 
     225            for f in field.prepopulate_from: 
     226                t.append(""" 
     227                         document.getElementById("id_%s").onkeyup = function() {  
     228                                var e = document.getElementById("id_%s");  
     229                                if(!e._changed) { e.value = URLify(%s, %s);} 
     230                        } 
     231                        """ % (f, field.name, add_values, field.maxlength) 
     232                ) 
     233 
     234        return ''.join(t) 
     235 
     236def do_include_admin_script(parser, token):  
     237     tokens = token.contents.split() 
     238     if len(tokens) != 2: 
     239         raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     240      
     241     return IncludeAdminScriptNode(tokens[1]) 
     242 
     243def do_dummy(parser, token): 
     244     return DummyNode(token.contents) 
     245 
     246def do_submit_row(parser, token): 
     247     return SubmitRowNode() 
     248 
     249def do_admin_field_bound(parser, token): 
     250    tokens = token.contents.split() 
     251    if len(tokens) != 2: 
     252        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     253    return AdminFieldBoundNode(tokens[1])  
     254 
     255 
     256def do_field_label(parser, token): 
     257    tokens = token.contents.split() 
     258    if len(tokens) != 2: 
     259        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     260    return FieldLabelNode(tokens[1])  
     261 
     262def do_field_widget(parser, token): 
     263    tokens = token.contents.split() 
     264    if len(tokens) != 2: 
     265        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     266    return FieldWidgetNode(tokens[1])  
     267 
     268def do_output_all(parser, token): 
     269    tokens = token.contents.split() 
     270    if len(tokens) != 2: 
     271        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     272    return OutputAllNode(tokens[1])  
     273 
     274 
     275def do_edit_inline(parser, token): 
     276    tokens = token.contents.split() 
     277    if len(tokens) != 2: 
     278        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     279    return EditInlineNode(tokens[1])  
     280 
     281def do_auto_populated_field_script(parser, token): 
     282    tokens = token.contents.split() 
     283    if len(tokens) != 2: 
     284        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 
     285    return AutoPopulatedFieldScriptNode(tokens[1])  
     286 
     287 
     288 
     289template.register_tag('include_admin_script', do_include_admin_script) 
     290template.register_tag('submit_row', do_submit_row ) 
     291template.register_tag('admin_field_bound', do_admin_field_bound) 
     292template.register_tag('edit_inline', do_edit_inline) 
     293template.register_tag('auto_populated_field_script', do_auto_populated_field_script) 
     294template.register_tag('field_label', do_field_label) 
     295template.register_tag('field_widget', do_field_widget) 
     296template.register_tag('output_all', do_output_all) 
     297           
     298           
  • django/views/admin/main.py

    old new  
    11# Generic admin views, with admin templates created dynamically at runtime. 
    22 
    3 from django.core import formfields, meta, template_loader 
     3from django.core import formfields, meta, template_loader, template 
    44from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied 
    55from django.core.extensions import DjangoContext as Context 
    66from django.models.auth import log 
     
    493493    }) 
    494494    return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 
    495495 
    496 def _get_flattened_data(field, val): 
    497     """ 
    498     Returns a dictionary mapping the field's manipulator field names to its 
    499     "flattened" string values for the admin view. "val" is an instance of the 
    500     field's value. 
    501     """ 
    502     if isinstance(field, meta.DateTimeField): 
    503         date_field, time_field = field.get_manipulator_field_names('') 
    504         return {date_field: (val is not None and val.strftime("%Y-%m-%