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

File django-admin-refactor-3.patch, 57.9 KB (added by robert@…, 19 years ago)

patch that refactors data flattening

  • django/conf/urls/admin.py

     
    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

     
     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

     
     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

     
     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

     
     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

     
     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

     
    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

     
    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

     
    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

     
     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

     
    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-%d") or ''),
    505                 time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
    506     elif isinstance(field, meta.DateField):
    507         return {field.name: (val is not None and val.strftime("%Y-%m-%d") or '')}
    508     elif isinstance(field, meta.TimeField):
    509         return {field.name: (val is not None and val.strftime("%H:%M:%S") or '')}
    510     else:
    511         return {field.name: val}
    512 
    513496use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin
    514497
    515498def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects):
     
    530513    t.append('</div>\n')
    531514    return t
    532515
     516def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs):
     517# Put in any necessary JavaScript imports.
     518    js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
     519    if auto_populated_fields:
     520        js.append('js/urlify.js')
     521    if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField):
     522        js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
     523    if ordered_objects:
     524        js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
     525    if opts.admin.js:
     526        js.extend(opts.admin.js)
     527    seen_collapse = False
     528    for _, options in admin_field_objs:
     529        if not seen_collapse and 'collapse' in options.get('classes', ''):
     530            seen_collapse = True
     531            js.append('js/admin/CollapsedFieldsets.js' )
     532        try:
     533            for field_list in options['fields']:
     534                for f in field_list:
     535                    if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface:
     536                        js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
     537                        raise StopIteration
     538        except StopIteration:
     539            break
     540    return js
     541
     542class BoundField(object):
     543    def __init__(self, index, field, original, form_prefix, name_prefix, rel, form_fields):
     544        self.index = index
     545        self.field = field
     546        self.hack_prefix = 'form.' + form_prefix
     547        self.label_name = 'id_%s%s' % (form_prefix, field.get_manipulator_field_names('')[0])
     548        self.has_label_first = not isinstance(self.field, meta.BooleanField)
     549        self.original = original
     550        self.raw_id_admin = use_raw_id_admin(field)
     551        self.name_prefix = name_prefix
     552        self.rel = rel
     553        self.is_date_time = isinstance(field, meta.DateTimeField)
     554        self.is_file_field = isinstance(field, meta.FileField)
     555        self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOne) and field.rel.to.admin
     556        self.not_in_table = isinstance(self.field, meta.AutoField)
     557        self.form_fields = form_fields
     558       
     559        classes = []
     560        if(self.raw_id_admin):
     561            classes.append('nowrap')
     562      #  if(self.field.errors()):
     563      #      classes.append('error')
     564        self.cell_class_attribute = ' '.join(classes)   
     565        self._repr_filled = False
     566       
     567    def as_field_list(self):
     568        return [self.field]
     569
     570    def original_value(self):
     571        return self.original.__dict__[self.field.name]
     572       
     573    def _fetch_existing_repr(self, func_name):
     574        class_dict = self.original.__class__.__dict__
     575        func = class_dict.get(func_name)
     576        return func(self.original)
     577       
     578    def _fill_existing_repr(self):
     579        if self._repr_filled:
     580            return
     581        #HACK
     582        if isinstance(self.field.rel, meta.ManyToOne):
     583             func_name = 'get_%s' % self.field.name
     584             self._repr = self._fetch_existing_repr(func_name)
     585        elif isinstance(self.field.rel, meta.ManyToMany):
     586             func_name = 'get_%s_list' % self.field.name
     587             self._repr =  ",".join(self._fetch_existing_repr(func_name))
     588        self._repr_filled = True
     589             
     590    def existing_repr(self):
     591        self._fill_existing_repr()
     592        return self._repr
     593
     594    def __repr__(self):
     595        return repr(self.__dict__)
     596
     597   
     598
     599
     600class AdminFieldSet(object):
     601     def __init__(self, fieldset_name, options, bound_field_sets):
     602         self.name = fieldset_name
     603         self.options = options
     604         self.bound_field_sets = bound_field_sets
     605         self.classes = options.get('classes', '')
     606# self.fields = options['fields']
     607     
     608     def __repr__(self):
     609        return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets)
     610
     611
     612def bound_field_sets(opts, context, name_prefix):
     613    original = template.resolve_variable('original', context);
     614    form_prefix = ''
     615    fields = opts['fields']
     616    bound_field_sets = [ [BoundField(i, f, original, form_prefix, name_prefix, False,
     617                                resolve_form_fields(f,name_prefix, context)) for i,f in enumerate(field)  ] for field in fields]
     618
     619    return bound_field_sets
     620
     621def resolve_form_fields(field, name_prefix, context):
     622    return [template.resolve_variable(name, context) for name in field.get_manipulator_field_names(name_prefix)]
     623
     624
     625def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''):
     626    admin_field_objs = opts.admin.get_field_objs(opts)
     627    ordered_objects = opts.get_ordered_objects()[:]
     628    auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
     629 
     630    javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs);
     631   
     632    if ordered_objects:
     633        coltype = 'colMS'
     634    else:
     635        coltype = 'colM'
     636       
     637    has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
     638   
     639    form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or ''
     640
     641    admin_fieldsets = [AdminFieldSet(f, o, bound_field_sets(o, context, 'form.')) for f, o in admin_field_objs]
     642    inline_related_objects = opts.get_inline_related_objects_wrapped()
     643   
     644    ordered_object_names =   ' '.join(['object.%s' % o.pk.name for o in ordered_objects])
     645   
     646    extra_context = {
     647        'add': add,
     648        'change': change,
     649        'admin_field_objs' : admin_field_objs,
     650        'ordered_objects' : ordered_objects,
     651        'auto_populated_fields' : auto_populated_fields,
     652        'javascript_imports' : javascript_imports,
     653        'coltype' : coltype,
     654        'has_absolute_url': has_absolute_url,
     655        'form_enc_attrib': form_enc_attrib,
     656        'form_url' : form_url,
     657        'admin_fieldsets' : admin_fieldsets,
     658        'inline_related_objects': inline_related_objects,
     659        'ordered_object_names' : ordered_object_names,
     660        'content_type_id' : opts.get_content_type_id(),
     661        'save_on_top' : opts.admin.save_on_top,
     662        'verbose_name_plural': opts.verbose_name_plural,
     663        'save_as': opts.admin.save_as,
     664        'app_label': app_label,
     665        'object_name': opts.object_name,
     666        'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()]
     667    }
     668   
     669    context.update(extra_context)   
     670   
     671   
     672def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None):
     673    mod, opts = _get_mod_opts(app_label, module_name)
     674    if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
     675        raise PermissionDenied
     676    manipulator = mod.AddManipulator()
     677    if request.POST:
     678        new_data = request.POST.copy()
     679        if opts.has_field_type(meta.FileField):
     680            new_data.update(request.FILES)
     681        errors = manipulator.get_validation_errors(new_data)
     682        if not errors and not request.POST.has_key("_preview"):
     683            for f in opts.many_to_many:
     684                if f.rel.raw_id_admin:
     685                    new_data.setlist(f.name, new_data[f.name].split(","))
     686            manipulator.do_html2python(new_data)
     687            new_object = manipulator.save(new_data)
     688            pk_value = getattr(new_object, opts.pk.column)
     689            log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION)
     690            msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object)
     691            # Here, we distinguish between different save types by checking for
     692            # the presence of keys in request.POST.
     693            if request.POST.has_key("_continue"):
     694                request.user.add_message("%s You may edit it again below." % msg)
     695                if request.POST.has_key("_popup"):
     696                    post_url_continue += "?_popup=1"
     697                return HttpResponseRedirect(post_url_continue % pk_value)
     698            if request.POST.has_key("_popup"):
     699                return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
     700                    (pk_value, repr(new_object).replace('"', '\\"')), mimetype='text/html; charset=utf-8')
     701            elif request.POST.has_key("_addanother"):
     702                request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name))
     703                return HttpResponseRedirect(request.path)
     704            else:
     705                request.user.add_message(msg)
     706                return HttpResponseRedirect(post_url)
     707        if request.POST.has_key("_preview"):
     708            manipulator.do_html2python(new_data)
     709    else:
     710        new_data = {}
     711        # Add default data.
     712        for f in opts.get_data_holders():
     713               new_data.update( f.flatten_data() )
     714       
     715        # Override the defaults with request.GET, if it exists.
     716        new_data.update(request.GET)
     717        errors = {}
     718
     719    # Populate the FormWrapper.
     720    form = formfields.FormWrapper(manipulator, new_data, errors)
     721    for rel_opts, rel_field in opts.get_inline_related_objects():
     722        var_name = rel_opts.object_name.lower()
     723        wrapper = []
     724        for i in range(rel_field.rel.num_in_admin):
     725            collection = {}
     726            for f in rel_opts.fields + rel_opts.many_to_many:
     727                if f.editable and f != rel_field and not isinstance(f, meta.AutoField):
     728                    for field_name in f.get_manipulator_field_names(''):
     729                        full_field_name = '%s.%d.%s' % (var_name, i, field_name)
     730                        field = manipulator[full_field_name]
     731                        data = field.extract_data(new_data)
     732                        collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, []))
     733            wrapper.append(formfields.FormFieldCollection(collection))
     734        setattr(form, rel_opts.module_name, wrapper)
     735
     736    c = Context(request, {
     737        'title': 'Add %s' % opts.verbose_name,
     738        "form": form,
     739        "is_popup": request.REQUEST.has_key("_popup"),
     740    })
     741    if object_id_override is not None:
     742        c['object_id'] = object_id_override
     743   
     744   
     745    fill_extra_context(opts, app_label, c, change=True)
     746   
     747    t = template_loader.get_template("admin_change_form");
     748   
     749    return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8')
     750
     751
     752
     753def change_stage_new(request, app_label, module_name, object_id):
     754    mod, opts = _get_mod_opts(app_label, module_name)
     755    if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
     756        raise PermissionDenied
     757    if request.POST and request.POST.has_key("_saveasnew"):
     758        return add_stage_new(request, app_label, module_name, form_url='../add/')
     759    try:
     760        manipulator = mod.ChangeManipulator(object_id)
     761    except ObjectDoesNotExist:
     762        raise Http404
     763
     764    inline_related_objects = opts.get_inline_related_objects()
     765    if request.POST:
     766        new_data = request.POST.copy()
     767        if opts.has_field_type(meta.FileField):
     768            new_data.update(request.FILES)
     769
     770        errors = manipulator.get_validation_errors(new_data)
     771        if not errors and not request.POST.has_key("_preview"):
     772            for f in opts.many_to_many:
     773                if f.rel.raw_id_admin:
     774                    new_data.setlist(f.name, new_data[f.name].split(","))
     775            manipulator.do_html2python(new_data)
     776            new_object = manipulator.save(new_data)
     777            pk_value = getattr(new_object, opts.pk.column)
     778
     779            # Construct the change message.
     780            change_message = []
     781            if manipulator.fields_added:
     782                change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and'))
     783            if manipulator.fields_changed:
     784                change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and'))
     785            if manipulator.fields_deleted:
     786                change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and'))
     787            change_message = ' '.join(change_message)
     788            if not change_message:
     789                change_message = 'No fields changed.'
     790
     791            log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message)
     792            msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object)
     793            if request.POST.has_key("_continue"):
     794                request.user.add_message("%s You may edit it again below." % msg)
     795                if request.REQUEST.has_key('_popup'):
     796                    return HttpResponseRedirect(request.path + "?_popup=1")
     797                else:
     798                    return HttpResponseRedirect(request.path)
     799            elif request.POST.has_key("_saveasnew"):
     800                request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object))
     801                return HttpResponseRedirect("../%s/" % pk_value)
     802            elif request.POST.has_key("_addanother"):
     803                request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name))
     804                return HttpResponseRedirect("../add/")
     805            else:
     806                request.user.add_message(msg)
     807                return HttpResponseRedirect("../")
     808        if request.POST.has_key("_preview"):
     809            manipulator.do_html2python(new_data)
     810    else:
     811        # Populate new_data with a "flattened" version of the current data.
     812        new_data = {}
     813        obj = manipulator.original_object
     814        for f in opts.get_data_holders():
     815            new_data.update(f.flatten_data(obj))
     816       
     817        # If the object has ordered objects on its admin page, get the existing
     818        # order and flatten it into a comma-separated list of IDs.
     819        id_order_list = []
     820        for rel_obj in opts.get_ordered_objects():
     821            id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())())
     822        if id_order_list:
     823            new_data['order_'] = ','.join(map(str, id_order_list))
     824        errors = {}
     825
     826    # Populate the FormWrapper.
     827    form = formfields.FormWrapper(manipulator, new_data, errors)
     828    form.original = manipulator.original_object
     829    form.order_objects = []
     830    for rel_opts, rel_field in inline_related_objects:
     831        var_name = rel_opts.object_name.lower()
     832        wrapper = []
     833        orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))()
     834        count = len(orig_list) + rel_field.rel.num_extra_on_change
     835        if rel_field.rel.min_num_in_admin:
     836            count = max(count, rel_field.rel.min_num_in_admin)
     837        if rel_field.rel.max_num_in_admin:
     838            count = min(count, rel_field.rel.max_num_in_admin)
     839        for i in range(count):
     840            collection = {'original': (i < len(orig_list) and orig_list[i] or None)}
     841            for f in rel_opts.fields + rel_opts.many_to_many:
     842                if f.editable and f != rel_field:
     843                    for field_name in f.get_manipulator_field_names(''):
     844                        full_field_name = '%s.%d.%s' % (var_name, i, field_name)
     845                        field = manipulator[full_field_name]
     846                        data = field.extract_data(new_data)
     847                        collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, []))
     848            wrapper.append(formfields.FormFieldCollection(collection))
     849        setattr(form, rel_opts.module_name, wrapper)
     850        if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts:
     851            form.order_objects.extend(orig_list)
     852
     853    c = Context(request, {
     854        'title': 'Change %s' % opts.verbose_name,
     855        'form': form,
     856        'object_id': object_id,
     857        'original': manipulator.original_object,
     858        'is_popup' : request.REQUEST.has_key('_popup')
     859    })
     860
     861    fill_extra_context(opts, app_label, c, change=True)
     862   
     863    #raw_template = _get_template(opts, app_label, change=True)
     864    # return HttpResponse(raw_template, mimetype='text/plain')
     865    #t = template_loader.get_template_from_string(raw_template)
     866    t = template_loader.get_template("admin_change_form");
     867    return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8')
     868
     869
    533870def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''):
    534871    admin_field_objs = opts.admin.get_field_objs(opts)
    535872    ordered_objects = opts.get_ordered_objects()[:]
     
    8021139        # Add default data.
    8031140        for f in opts.fields:
    8041141            if f.has_default():
    805                 new_data.update(_get_flattened_data(f, f.get_default()))
     1142                new_data.update( f.flatten_data() )
    8061143            # In required many-to-one fields with only one available choice,
    8071144            # select that one available choice. Note: We have to check that
    8081145            # the length of choices is *2*, not 1, because SelectFields always
     
    8371174                if f.editable and f != rel_field and not isinstance(f, meta.AutoField):
    8381175                    for field_name in f.get_manipulator_field_names(''):
    8391176                        full_field_name = '%s.%d.%s' % (var_name, i, field_name)
    840                         collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, ''), errors.get(full_field_name, []))
     1177                        field = manipulator[full_field_name]
     1178                        data = field.extract_data(new_data)
     1179                        collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, []))
    8411180            wrapper.append(formfields.FormFieldCollection(collection))
    8421181        setattr(form, rel_opts.module_name, wrapper)
    8431182
     
    9151254        new_data = {}
    9161255        obj = manipulator.original_object
    9171256        for f in opts.fields:
    918             new_data.update(_get_flattened_data(f, getattr(obj, f.column)))
     1257            new_data.update(f.flatten_data(obj))
    9191258        for f in opts.many_to_many:
    9201259            get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular)
    9211260            if f.rel.raw_id_admin:
     
    9271266            for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()):
    9281267                for f in rel_obj.fields:
    9291268                    if f.editable and f != rel_field:
    930                         for k, v in _get_flattened_data(f, getattr(rel_instance, f.column)).items():
     1269                        for k, v in f.flatten_data(rel_instance).items():
    9311270                            new_data['%s.%d.%s' % (var_name, i, k)] = v
    9321271                for f in rel_obj.many_to_many:
    9331272                    new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()]
     
    9601299                if f.editable and f != rel_field:
    9611300                    for field_name in f.get_manipulator_field_names(''):
    9621301                        full_field_name = '%s.%d.%s' % (var_name, i, field_name)
    963                         collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, f.get_default()), errors.get(full_field_name, []))
     1302                        field = manipulator[full_field_name]
     1303                        data = field.extract_data(new_data)
     1304                        collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, []))
    9641305            wrapper.append(formfields.FormFieldCollection(collection))
    9651306        setattr(form, rel_opts.module_name, wrapper)
    9661307        if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts:
  • tests/runtests.py

     
    100100            self.output(1, "Creating test database")
    101101            try:
    102102                cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
    103             except:
     103            except Exception, e:
     104                self.output(0, "There was an error creating the test database:%s " % str(e))
    104105                confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
    105106                if confirm == 'yes':
    106107                    cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)
Back to Top