diff --git a/django/conf/admin_templates/admin_change_form.html b/django/conf/admin_templates/admin_change_form.html index 692de223af..6efd0e82b9 100644 --- a/django/conf/admin_templates/admin_change_form.html +++ b/django/conf/admin_templates/admin_change_form.html @@ -50,16 +50,16 @@ {% endif %} -{% for fieldset in admin_fieldsets %} -
- {% if fieldset.name %} -

{{fieldset.name }}

+{% for bound_field_set in bound_field_sets %} +
+ {% if bound_field_set.name %} +

{{bound_field_set.name }}

{% endif %} - {% for bound_field_set in fieldset.bound_field_sets %} - {% for bound_field in bound_field_set %} - {% admin_field_bound bound_field %} + {% for bound_field_line in bound_field_set %} + {% admin_field_line bound_field_line %} + {% for bound_field in bound_field_line %} {% filter_interface_script_maybe bound_field %} - {% endfor %} + {% endfor %} {% endfor %}
{% endfor %} diff --git a/django/conf/admin_templates/admin_edit_inline_stacked.html b/django/conf/admin_templates/admin_edit_inline_stacked.html index 66b93d4ffc..4a9f6ce17c 100644 --- a/django/conf/admin_templates/admin_edit_inline_stacked.html +++ b/django/conf/admin_templates/admin_edit_inline_stacked.html @@ -8,7 +8,7 @@ {% if bound_field.not_in_table %} {% field_widget bound_field %} {% else %} - {% admin_field_bound bound_field %} + {% admin_field_line bound_field %} {% endif %} {% endfor %} {%endfor%} diff --git a/django/conf/admin_templates/admin_field.html b/django/conf/admin_templates/admin_field_line.html similarity index 89% rename from django/conf/admin_templates/admin_field.html rename to django/conf/admin_templates/admin_field_line.html index 9a2228ac41..1b3903b71e 100644 --- a/django/conf/admin_templates/admin_field.html +++ b/django/conf/admin_templates/admin_field_line.html @@ -1,8 +1,8 @@ -
+
{% for bound_field in bound_fields %} {{ bound_field.html_error_list }} {% endfor %} - + {% for bound_field in bound_fields %} {% if bound_field.has_label_first %} {% field_label bound_field %} @@ -16,8 +16,8 @@ {% if change %} {% if bound_field.field.primary_key %} - {{ bound_field.original_value }} - {% endif %} + {{ bound_field.original_value }} + {% endif %} {% if bound_field.raw_id_admin %} {% if bound_field.existing_repr %} @@ -31,6 +31,6 @@ {{bound_field.field.help_text}}

{% endif %} - {% endfor %} + {% endfor %}
diff --git a/django/conf/admin_templates/admin_field_widget.html b/django/conf/admin_templates/admin_field_widget.html deleted file mode 100644 index 6a882cd2b6..0000000000 --- a/django/conf/admin_templates/admin_field_widget.html +++ /dev/null @@ -1,27 +0,0 @@ -{% if bound_field.is_date_time %} -

- Date: {{ bound_field.form_fields.0 }}
- Time: {{ bound_field.form_fields.1 }} -

-{% else %} - {% if bound_field.is_file_field %} - {% if bound_field.original_value %} - Currently: {{ bound_field.original_value }}
- Change: {% output_all bound_field.form_fields %} - {% else %} - {% output_all bound_field.form_fields %} - {% endif %} - {% else %} - {% output_all bound_field.form_fields %} - {% if bound_field.raw_id_admin %} - Lookup - {% else %} - {% if bound_field.needs_add_label %} - Add Another - {% endif %} - {% endif %} - {% endif %} -{% endif %} - - - diff --git a/django/core/meta/fields.py b/django/core/meta/fields.py index 75e5f37925..bb7f4fcc32 100644 --- a/django/core/meta/fields.py +++ b/django/core/meta/fields.py @@ -786,6 +786,93 @@ class OneToOne(ManyToOne): self.lookup_overrides = lookup_overrides or {} self.raw_id_admin = raw_id_admin + + +class BoundField(object): + def __init__(self, field, field_mapping, original): + self.field = field + self.form_fields = self.resolve_form_fields(field_mapping) + self.original = original + + def resolve_form_fields(self, field_mapping): + return [field_mapping[name] for name in self.field.get_manipulator_field_names('')] + + def as_field_list(self): + return [self.field] + + def original_value(self): + return self.original.__dict__[self.field.name] + + def __repr__(self): + return "BoundField:(%s, %s)" %( self.field.name, self.form_fields) + +class BoundFieldLine(object): + def __init__(self, field_line, field_mapping, original, bound_field_class=BoundField): + self.bound_fields = [bound_field_class(field, field_mapping, original) for field in field_line] + + def __iter__(self): + for bound_field in self.bound_fields: + yield bound_field + + def __repr__(self): + return "%s:(%s)" % (self.__class__.__name__, self.bound_fields) + + def __len__(self): + return len(self.bound_fields) + +class FieldLine(object): + def __init__(self, linespec, field_locator_func): + if isinstance(linespec, basestring): + self.fields = [field_locator_func(linespec)] + else: + self.fields = [field_locator_func(field_name) for field_name in linespec] + + def bind(self, field_mapping, original, bound_field_line_class=BoundFieldLine): + return bound_field_line_class(self, field_mapping, original) + + def __iter__(self): + for field in self.fields: + yield field + + def __len__(self): + return len(self.fields) + +class BoundFieldSet(object): + def __init__(self, field_set, field_mapping, original, bound_field_line_class=BoundFieldLine): + self.name = field_set.name + self.classes = field_set.classes + self.bound_field_lines = [ field_line.bind(field_mapping,original, bound_field_line_class) + for field_line in field_set] + def __repr__(self): + return "%s:(%s,%s)" % (self.__class__.__name__, self.name, self.bound_field_lines) + + def __iter__(self): + for bound_field_line in self.bound_field_lines: + yield bound_field_line + + def __len__(self): + return len(self.bound_field_lines) + +class FieldSet(object): + def __init__(self, name, classes, field_lines): + self.name = name + self.field_lines = field_lines + self.classes = classes + + def __repr__(self): + return "FieldSet:(%s,%s)" % (self.name, self.field_lines) + + def bind(self, field_mapping, original, bound_field_set_class=BoundFieldSet): + return bound_field_set_class(self, field_mapping, original) + + def __iter__(self): + for field_line in self.field_lines: + yield field_line + + def __len__(self): + return len(self.field_lines) + + class Admin: def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None, save_as=False, ordering=None, search_fields=None, save_on_top=False): @@ -808,7 +895,7 @@ class Admin: the dict has attribs 'fields' and maybe 'classes'. fields is a list of subclasses of Field. - Return value needs to be encapsulated. + TODO:Return value needs to be encapsulated. """ if self.fields is None: field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),) @@ -827,3 +914,23 @@ class Admin: new_fieldset[1]['fields'] = admin_fields new_fieldset_list.append(new_fieldset) return new_fieldset_list + + def get_field_sets(self, opts): + if self.fields is None: + field_struct = ((None, { + 'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)] + }),) + else: + field_struct = self.fields + + + new_fieldset_list = [] + for fieldset in field_struct: + name = fieldset[0] + fs_options = fieldset[1] + classes = fs_options.get('classes', None) + line_specs = fs_options['fields'] + field_lines = [FieldLine(line_spec, opts.get_field) for line_spec in line_specs] + new_fieldset_list.append(FieldSet(name, classes, field_lines) ) + return new_fieldset_list + \ No newline at end of file diff --git a/django/core/template.py b/django/core/template.py index 6f1da2a858..c0c95b165c 100644 --- a/django/core/template.py +++ b/django/core/template.py @@ -113,6 +113,11 @@ class Template: def __init__(self, template_string, filename=UNKNOWN_SOURCE): "Compilation stage" self.nodelist = compile_string(template_string, filename) + from pprint import pprint, pformat + print "------------------------" + print filename + pprint(self.nodelist) + print "------------------------" def __iter__(self): for node in self.nodelist: @@ -212,6 +217,11 @@ def tokenize(template_string, filename): while linebreaks and line != lastline and linebreaks[line] <= upto: line += 1 + + last_bit = template_string[upto:] + if len(last_bit): + token_tups.append( (last_bit, line) ) + return [ create_token(tok, (filename, line)) for tok, line in token_tups] diff --git a/django/templatetags/admin_modify.py b/django/templatetags/admin_modify.py index fbf9071b01..2ab019b3ee 100644 --- a/django/templatetags/admin_modify.py +++ b/django/templatetags/admin_modify.py @@ -8,6 +8,7 @@ from django.utils.functional import curry from django.core.template_decorators import simple_tag, inclusion_tag from django.views.admin.main import AdminBoundField +from django.core.meta.fields import BoundField import re word_re = re.compile('[A-Z][a-z]+') @@ -24,24 +25,24 @@ include_admin_script = simple_tag(include_admin_script) #@inclusion_tag('admin_submit_line', takes_context=True) def submit_row(context): - change = context['change'] - add = context['add'] - show_delete = context['show_delete'] - ordered_objects = context['ordered_objects'] - save_as = context['save_as'] - has_delete_permission = context['has_delete_permission'] - is_popup = context['is_popup'] - - return { - 'onclick_attrib' : (ordered_objects and change - and 'onclick="submitOrderForm();"' or ''), - 'show_delete_link' : (not is_popup and has_delete_permission - and (change or show_delete)), - 'show_save_as_new' : not is_popup and change and save_as, - 'show_save_and_add_another': not is_popup and (not save_as or add), - 'show_save_and_continue': not is_popup, - 'show_save': True - } + change = context['change'] + add = context['add'] + show_delete = context['show_delete'] + ordered_objects = context['ordered_objects'] + save_as = context['save_as'] + has_delete_permission = context['has_delete_permission'] + is_popup = context['is_popup'] + + return { + 'onclick_attrib' : (ordered_objects and change + and 'onclick="submitOrderForm();"' or ''), + 'show_delete_link' : (not is_popup and has_delete_permission + and (change or show_delete)), + 'show_save_as_new' : not is_popup and change and save_as, + 'show_save_and_add_another': not is_popup and (not save_as or add), + 'show_save_and_continue': not is_popup, + 'show_save': True + } srdec = inclusion_tag('admin_submit_line', takes_context=True) submit_row = srdec(submit_row) @@ -118,10 +119,10 @@ class FieldWrapper(object): and self.field.rel.raw_id_admin class FormFieldCollectionWrapper(object): - def __init__(self, obj, fields): - self.obj = obj + def __init__(self, field_mapping, fields): + self.field_mapping = field_mapping self.fields = fields - self.bound_fields = [ AdminBoundField(field, obj['original'], True, self.obj) for field in self.fields ] + self.bound_fields = [ AdminBoundField(field, self.field_mapping, field_mapping['original']) for field in self.fields ] def showurl(self): return False @@ -145,7 +146,6 @@ class EditInlineNode(template.Node): context.pop() return output - def fill_context(self, relation, add, change, context): field_wrapper_list = relation.editable_fields(FieldWrapper) @@ -154,13 +154,12 @@ class EditInlineNode(template.Node): form = template.resolve_variable('form', context) form_field_collections = form[relation.opts.module_name] fields = relation.editable_fields() - form_field_collection_wrapper_list = [FormFieldCollectionWrapper(o,fields) for o in form_field_collections] + form_field_collection_wrapper_list = [FormFieldCollectionWrapper(field_mapping ,fields) for field_mapping in form_field_collections] context['field_wrapper_list'] = field_wrapper_list context['form_field_collection_wrapper_list'] = form_field_collection_wrapper_list context['num_headers'] = len(field_wrapper_list) context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) -# context['name_prefix'] = "%s." % (var_name,) #@simple_tag @@ -215,13 +214,13 @@ def register_one_arg_tag(node): for node in one_arg_tag_nodes: register_one_arg_tag(node) - -#@inclusion_tag('admin_field', takes_context=True) -def admin_field_bound(context, argument_val): - if (isinstance(argument_val, list)): - bound_fields = argument_val + +#@inclusion_tag('admin_field_line', takes_context=True) +def admin_field_line(context, argument_val): + if (isinstance(argument_val, BoundField)): + bound_fields = [argument_val] else: - bound_fields = [argument_val] + bound_fields = [bf for bf in argument_val] add = context['add'] change = context['change'] @@ -236,15 +235,15 @@ def admin_field_bound(context, argument_val): if isinstance(bound_fields[0].field, meta.BooleanField): class_names.append('checkbox-row') - return { + return { 'add' : context['add'], 'change' : context['change'], 'bound_fields' : bound_fields, 'class_names' : " ".join(class_names) - } + } -afbdec = inclusion_tag('admin_field', takes_context=True) -admin_field_bound = afbdec(admin_field_bound) +afbdec = inclusion_tag('admin_field_line', takes_context=True) +admin_field_line = afbdec(admin_field_line) diff --git a/django/views/admin/main.py b/django/views/admin/main.py index 1e451413a1..14b88fcc14 100644 --- a/django/views/admin/main.py +++ b/django/views/admin/main.py @@ -1,6 +1,7 @@ # Generic admin views, with admin templates created dynamically at runtime. from django.core import formfields, meta, template_loader, template +from django.core.meta.fields import BoundField, BoundFieldLine, BoundFieldSet from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied from django.core.extensions import DjangoContext as Context from django.core.extensions import get_object_or_404, render_to_response @@ -539,25 +540,10 @@ def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_fi break return js -class BoundField(object): - def __init__(self, field, original, rel, field_mapping): - self.field = field - self.form_fields = self.resolve_form_fields(field_mapping) - self.original = original - self.rel = rel - def resolve_form_fields(self, field_mapping): - return [field_mapping[name] for name in self.field.get_manipulator_field_names('')] - - def as_field_list(self): - return [self.field] - - def original_value(self): - return self.original.__dict__[self.field.name] - class AdminBoundField(BoundField): - def __init__(self, field, original, rel, field_mapping): - super(AdminBoundField, self).__init__(field,original, rel, field_mapping) + def __init__(self, field, field_mapping, original): + super(AdminBoundField, self).__init__(field,field_mapping,original) self.element_id = self.form_fields[0].get_id() self.has_label_first = not isinstance(self.field, meta.BooleanField) @@ -577,8 +563,6 @@ class AdminBoundField(BoundField): self.cell_class_attribute = ' class="%s" ' % ' '.join(classes) self._repr_filled = False - - def _fetch_existing_repr(self, func_name): class_dict = self.original.__class__.__dict__ func = class_dict.get(func_name) @@ -591,9 +575,9 @@ class AdminBoundField(BoundField): if isinstance(self.field.rel, meta.ManyToOne): func_name = 'get_%s' % self.field.name self._repr = self._fetch_existing_repr(func_name) - elif isinstance(self.field.rel, meta.ManyToMany): - func_name = 'get_%s_list' % self.field.name - self._repr = ",".join(self._fetch_existing_repr(func_name)) + elif isinstance(self.field.rel, meta.ManyToMany): + func_name = 'get_%s_list' % self.field.name + self._repr = ",".join(self._fetch_existing_repr(func_name)) self._repr_filled = True def existing_repr(self): @@ -607,33 +591,24 @@ class AdminBoundField(BoundField): return " ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors]) -class AdminFieldSet(object): - def __init__(self, fieldset_name, options, form, original): - self.name = fieldset_name - self.options = options - self.bound_field_sets = self.get_bound_field_sets(form, original) - self.classes = options.get('classes', '') - - def __repr__(self): - return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets) - - def get_bound_field_sets(self, form, original): - fields = self.options['fields'] - bound_field_sets = [ [AdminBoundField(f, original, False, form) for f in field ] for field in fields] - for set in bound_field_sets: - first = True - for bound_field in set: - bound_field.first = first - first = False - - return bound_field_sets +class AdminBoundFieldLine(BoundFieldLine): + def __init__(self, field_line, field_mapping, original): + super(AdminBoundFieldLine, self).__init__(field_line, field_mapping, original, AdminBoundField) + for bound_field in self: + bound_field.first = True + break +class AdminBoundFieldSet(BoundFieldSet): + def __init__(self, field_set, field_mapping, original): + super(AdminBoundFieldSet, self).__init__(field_set, field_mapping, original, AdminBoundFieldLine) + + def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): admin_field_objs = opts.admin.get_field_objs(opts) ordered_objects = opts.get_ordered_objects()[:] auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] - javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs); + javascript_imports = get_javascript_imports(opts, auto_populated_fields, ordered_objects, admin_field_objs); if ordered_objects: coltype = 'colMS' @@ -646,25 +621,26 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d form = context['form'] original = context['original'] - admin_fieldsets = [AdminFieldSet(name, options, form, original) for name, options in admin_field_objs] + bound_field_sets = [field_set.bind(form, original, AdminBoundFieldSet) + for field_set in opts.admin.get_field_sets(opts)] + inline_related_objects = opts.get_inline_related_objects_wrapped() ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) extra_context = { 'add': add, - 'change': change, - 'admin_field_objs' : admin_field_objs, + 'change': change, 'ordered_objects' : ordered_objects, + 'ordered_object_names' : ordered_object_names, 'auto_populated_fields' : auto_populated_fields, 'javascript_imports' : javascript_imports, 'coltype' : coltype, 'has_absolute_url': has_absolute_url, 'form_enc_attrib': form_enc_attrib, - 'form_url' : form_url, - 'admin_fieldsets' : admin_fieldsets, + 'form_url' : form_url, + 'bound_field_sets' : bound_field_sets, 'inline_related_objects': inline_related_objects, - 'ordered_object_names' : ordered_object_names, 'content_type_id' : opts.get_content_type_id(), 'save_on_top' : opts.admin.save_on_top, 'verbose_name_plural': opts.verbose_name_plural,