From c40fd89b1a388fbe6faee695ad8a8a764c308adf Mon Sep 17 00:00:00 2001 From: Robert Wittams Date: Thu, 20 Oct 2005 23:27:27 +0000 Subject: [PATCH] Changed how edit_inline works in the admin. Now it is done by a subclass of BoundRelatedObject. This means anyone using edit_inline="path/to/template" will need to subclass this ( or TabularBoundRelatedObject or StackedBoundRelatedObject). The functionality may be restored if it becomes obvious exactly what information should be available to these templates; before it was kind of random. So you can do class MyBoundRelatedObject(TabularBoundRelatedObject): def template_name(self): return "path/to/template" and then edit_inline=MyBoundRelatedObject to duplicate the previous functionality. git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@986 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- .../templates/admin/edit_inline_stacked.html | 9 ++- .../templates/admin/edit_inline_tabular.html | 10 +-- .../admin/templatetags/admin_modify.py | 67 ++++++++++++------- django/core/meta/__init__.py | 23 +++++-- django/core/meta/fields.py | 2 +- django/core/template/__init__.py | 6 ++ .../core/template/loaders/app_directories.py | 18 ++--- tests/runtests.py | 1 - 8 files changed, 83 insertions(+), 53 deletions(-) diff --git a/django/contrib/admin/templates/admin/edit_inline_stacked.html b/django/contrib/admin/templates/admin/edit_inline_stacked.html index 4a9f6ce17c..ca9e7095d1 100644 --- a/django/contrib/admin/templates/admin/edit_inline_stacked.html +++ b/django/contrib/admin/templates/admin/edit_inline_stacked.html @@ -1,7 +1,7 @@
- {% for fcw in form_field_collection_wrapper_list %} -

{{relation.opts.verbose_name|capfirst }} #{{ forloop.counter }}

- {% if fcw.show_url %}{% if fcw.obj.original %} + {% for fcw in bound_related_object.form_field_collection_wrappers %} +

{{ bound_related_object.relation.opts.verbose_name|capfirst }} #{{ forloop.counter }}

+ {% if bound_related_object.show_url %}{% if fcw.obj.original %}

View on site

{% endif %}{% endif %} {% for bound_field in fcw.bound_fields %} @@ -12,5 +12,4 @@ {% endif %} {% endfor %} {%endfor%} -
- + \ No newline at end of file diff --git a/django/contrib/admin/templates/admin/edit_inline_tabular.html b/django/contrib/admin/templates/admin/edit_inline_tabular.html index a7f25cbcbe..ddfe6f0ff2 100644 --- a/django/contrib/admin/templates/admin/edit_inline_tabular.html +++ b/django/contrib/admin/templates/admin/edit_inline_tabular.html @@ -1,12 +1,12 @@
-

{{relation.opts.verbose_name_plural|capfirst}}

+

{{bound_related_object.relation.opts.verbose_name_plural|capfirst}}

- {% for fw in field_wrapper_list %} + {% for fw in bound_related_object.field_wrapper_list %} {% if fw.needs_header %} {{fw.field.verbose_name|capfirst}} {% endif %} {% endfor %} - {% for fcw in form_field_collection_wrapper_list %} + {% for fcw in bound_related_object.form_field_collection_wrappers %} {% if change %}{% if original_row_needed %} {% if fcw.obj.original %} @@ -26,14 +26,14 @@ {% endif %} {% endfor %} - {% if fcw.show_url %}{% endif %} {% endfor %}
+ {% if bound_related_object.show_url %} {% if fcw.obj.original %}View on site{% endif %}
- {% for fcw in form_field_collection_wrapper_list %} + {% for fcw in bound_related_object.form_field_collection_wrapper_list %} {% for bound_field in fcw.bound_fields %} {% if bound_field.not_in_table %} {% field_widget bound_field %} diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index 38b5f2c8fa..aed13c22c9 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -4,11 +4,11 @@ from django.conf.settings import ADMIN_MEDIA_PREFIX from django.utils.text import capfirst from django.utils.html import escape from django.utils.functional import curry - from django.core.template.decorators import simple_tag, inclusion_tag - from django.contrib.admin.views.main import AdminBoundField from django.core.meta.fields import BoundField, Field +from django.core.meta import BoundRelatedObject, TABULAR, STACKED + import re word_re = re.compile('[A-Z][a-z]+') @@ -122,14 +122,41 @@ class FieldWrapper(object): return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \ and self.field.rel.raw_id_admin + class FormFieldCollectionWrapper(object): def __init__(self, field_mapping, fields): self.field_mapping = field_mapping self.fields = fields - self.bound_fields = [ AdminBoundField(field, self.field_mapping, field_mapping['original']) for field in self.fields ] + self.bound_fields = [AdminBoundField(field, self.field_mapping, field_mapping['original']) for field in self.fields ] + +class TabularBoundRelatedObject(BoundRelatedObject): + def __init__(self, related_object, field_mapping, original): + super(TabularBoundRelatedObject, self).__init__(related_object, field_mapping, original) + self.field_wrapper_list = self.relation.editable_fields(FieldWrapper) + fields = self.relation.editable_fields() + self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields) + for field_mapping in self.field_mappings] + self.original_row_needed = max([fw.use_raw_id_admin() for fw in self.field_wrapper_list]) + self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url') - def showurl(self): - return False + def template_name(self): + return "admin/edit_inline_tabular" + +class StackedBoundRelatedObject(BoundRelatedObject): + def __init__(self, related_object, field_mapping, original): + super(StackedBoundRelatedObject, self).__init__(related_object, field_mapping, original) + fields = self.relation.editable_fields() + self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields) + for field_mapping in self.field_mappings] + self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url') + + def template_name(self): + return "admin/edit_inline_stacked" + +bound_related_object_overrides = { + TABULAR : TabularBoundRelatedObject, + STACKED : StackedBoundRelatedObject +} class EditInlineNode(template.Node): def __init__(self, rel_var): @@ -137,34 +164,24 @@ class EditInlineNode(template.Node): def render(self, context): relation = template.resolve_variable(self.rel_var, context) - add, change = context['add'], context['change'] - + context.push() - self.fill_context(relation, add, change, context) + klass = relation.field.rel.edit_inline + bound_related_object_class = bound_related_object_overrides.get(klass, klass) - t = template_loader.get_template(relation.field.rel.edit_inline) + original = context.get('original', None) + + bound_related_object = relation.bind(context['form'], original, bound_related_object_class) + context['bound_related_object'] = bound_related_object + + t = template_loader.get_template( bound_related_object.template_name() ) output = t.render(context) context.pop() return output - def fill_context(self, relation, add, change, context): - field_wrapper_list = relation.editable_fields(FieldWrapper) - - var_name = relation.opts.object_name.lower() - - form = template.resolve_variable('form', context) - form_field_collections = form[relation.opts.module_name] - fields = relation.editable_fields() - 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]) - #@simple_tag def output_all(form_fields): @@ -201,7 +218,7 @@ def do_one_arg_tag(node_factory, parser,token): tokens = token.contents.split() if len(tokens) != 2: raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) - return node_factory(tokens[1]) + return node_factory(tokens[1]) one_arg_tag_nodes = [ diff --git a/django/core/meta/__init__.py b/django/core/meta/__init__.py index d843ce7862..5cf823140c 100644 --- a/django/core/meta/__init__.py +++ b/django/core/meta/__init__.py @@ -146,7 +146,17 @@ class FieldDoesNotExist(Exception): class BadKeywordArguments(Exception): pass - +class BoundRelatedObject(object): + def __init__(self,related_object, field_mapping, original): + self.relation = related_object + self.field_mappings = field_mapping[related_object.opts.module_name] + + def template_name(self): + raise NotImplementedException + + def __repr__(self): + return repr(self.__dict__) + class RelatedObject(object): def __init__(self,parent_opts, opts, field): self.parent_opts = parent_opts @@ -249,9 +259,9 @@ class RelatedObject(object): f.get_manipulator_fields(self.opts, manipulator, change, name_prefix=prefix, rel=True)) return fields - -class BoundRelatedObject(object): - pass + + def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject): + return bound_related_object_class(self, field_mapping, original) class Options: def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', @@ -469,7 +479,7 @@ class Options: self._ordered_objects = objects return self._ordered_objects - def has_field_type(self, field_type, follow): + def has_field_type(self, field_type, follow = None): """ Returns True if this object's admin form has at least one of the given field_type (e.g. FileField). @@ -1754,12 +1764,15 @@ def manipulator_flatten_data(opts, klass, add, change, self): def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): from django.utils.text import get_text_list + field_list = [opts.get_field(field_name) for field_name in field_name_list] if isinstance(field_list[0].rel, ManyToOne): kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data} else: kwargs = {'%s__iexact' % field_name_list[0]: field_data} for f in field_list[1:]: + # This is really not going to work for fields that have different form fields, eg DateTime + # This validation needs to occur after html2python to be effective. field_val = all_data.get(f.column, None) if field_val is None: # This will be caught by another validator, assuming the field diff --git a/django/core/meta/fields.py b/django/core/meta/fields.py index 8e82d66b23..a1a33ec4b7 100644 --- a/django/core/meta/fields.py +++ b/django/core/meta/fields.py @@ -16,7 +16,7 @@ BLANK_CHOICE_DASH = [("", "---------")] BLANK_CHOICE_NONE = [("", "None")] # Values for Relation.edit_inline. -TABULAR, STACKED = "admin/edit_inline_tabular", "admin/edit_inline_stacked" +TABULAR, STACKED = 1, 2 RECURSIVE_RELATIONSHIP_CONSTANT = 'self' diff --git a/django/core/template/__init__.py b/django/core/template/__init__.py index 611126abd3..3290b5d940 100644 --- a/django/core/template/__init__.py +++ b/django/core/template/__init__.py @@ -195,6 +195,12 @@ class Context: if dict.has_key(key): return True return False + + def get(self, key, otherwise): + for dict in self.dicts: + if dict.has_key(key): + return dict[key] + return otherwise def update(self, other_dict): "Like dict.update(). Pushes an entire dictionary's keys and values onto the context." diff --git a/django/core/template/loaders/app_directories.py b/django/core/template/loaders/app_directories.py index a698e94971..31cdfd5993 100644 --- a/django/core/template/loaders/app_directories.py +++ b/django/core/template/loaders/app_directories.py @@ -7,17 +7,13 @@ import os # At compile time, cache the directories to search. app_template_dirs = [] for app in INSTALLED_APPS: - try: - i = app.rfind('.') - m, a = app[:i], app[i+1:] - mod = getattr(__import__(m, '', '', [a]), a) - template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates') - if os.path.isdir(template_dir): - app_template_dirs.append(template_dir) - except Exception, e: - print "exception loading" - print e - raise e + i = app.rfind('.') + m, a = app[:i], app[i+1:] + mod = getattr(__import__(m, '', '', [a]), a) + template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates') + if os.path.isdir(template_dir): + app_template_dirs.append(template_dir) + # It won't change, so convert it to a tuple to save memory. app_template_dirs = tuple(app_template_dirs) diff --git a/tests/runtests.py b/tests/runtests.py index fd2d7b43ef..9aa96f20fc 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -100,7 +100,6 @@ class TestRunner: self.output(1, "Creating test database") try: cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME) - print "created" except Exception, e: self.output(0, "There was an error creating the test database:%s " % str(e)) confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)