1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

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
This commit is contained in:
Robert Wittams 2005-10-20 23:27:27 +00:00
parent bd47a97997
commit c40fd89b1a
8 changed files with 83 additions and 53 deletions

View File

@ -1,7 +1,7 @@
<fieldset class="module aligned"> <fieldset class="module aligned">
{% for fcw in form_field_collection_wrapper_list %} {% for fcw in bound_related_object.form_field_collection_wrappers %}
<h2>{{relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2> <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
{% if fcw.show_url %}{% if fcw.obj.original %} {% if bound_related_object.show_url %}{% if fcw.obj.original %}
<p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p> <p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p>
{% endif %}{% endif %} {% endif %}{% endif %}
{% for bound_field in fcw.bound_fields %} {% for bound_field in fcw.bound_fields %}
@ -12,5 +12,4 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{%endfor%} {%endfor%}
</fieldset> </fieldset>

View File

@ -1,12 +1,12 @@
<fieldset class="module"> <fieldset class="module">
<h2>{{relation.opts.verbose_name_plural|capfirst}}</h2><table> <h2>{{bound_related_object.relation.opts.verbose_name_plural|capfirst}}</h2><table>
<thead><tr> <thead><tr>
{% for fw in field_wrapper_list %} {% for fw in bound_related_object.field_wrapper_list %}
{% if fw.needs_header %} {% if fw.needs_header %}
<th{{fw.header_class_attribute}}> {{fw.field.verbose_name|capfirst}} </th> <th{{fw.header_class_attribute}}> {{fw.field.verbose_name|capfirst}} </th>
{% endif %} {% endif %}
{% endfor %} {% 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 change %}{% if original_row_needed %}
{% if fcw.obj.original %} {% if fcw.obj.original %}
@ -26,14 +26,14 @@
</td> </td>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if fcw.show_url %}<td> {% if bound_related_object.show_url %}<td>
{% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %} {% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
</td>{% endif %} </td>{% endif %}
</tr> </tr>
{% endfor %} </table> {% endfor %} </table>
{% 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 %} {% for bound_field in fcw.bound_fields %}
{% if bound_field.not_in_table %} {% if bound_field.not_in_table %}
{% field_widget bound_field %} {% field_widget bound_field %}

View File

@ -4,11 +4,11 @@ from django.conf.settings import ADMIN_MEDIA_PREFIX
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.html import escape from django.utils.html import escape
from django.utils.functional import curry from django.utils.functional import curry
from django.core.template.decorators import simple_tag, inclusion_tag from django.core.template.decorators import simple_tag, inclusion_tag
from django.contrib.admin.views.main import AdminBoundField from django.contrib.admin.views.main import AdminBoundField
from django.core.meta.fields import BoundField, Field from django.core.meta.fields import BoundField, Field
from django.core.meta import BoundRelatedObject, TABULAR, STACKED
import re import re
word_re = re.compile('[A-Z][a-z]+') word_re = re.compile('[A-Z][a-z]+')
@ -122,14 +122,41 @@ class FieldWrapper(object):
return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \ return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \
and self.field.rel.raw_id_admin and self.field.rel.raw_id_admin
class FormFieldCollectionWrapper(object): class FormFieldCollectionWrapper(object):
def __init__(self, field_mapping, fields): def __init__(self, field_mapping, fields):
self.field_mapping = field_mapping self.field_mapping = field_mapping
self.fields = fields 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): def template_name(self):
return False 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): class EditInlineNode(template.Node):
def __init__(self, rel_var): def __init__(self, rel_var):
@ -137,34 +164,24 @@ class EditInlineNode(template.Node):
def render(self, context): def render(self, context):
relation = template.resolve_variable(self.rel_var, context) relation = template.resolve_variable(self.rel_var, context)
add, change = context['add'], context['change']
context.push() 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) output = t.render(context)
context.pop() context.pop()
return output 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 #@simple_tag
def output_all(form_fields): def output_all(form_fields):
@ -201,7 +218,7 @@ def do_one_arg_tag(node_factory, parser,token):
tokens = token.contents.split() tokens = token.contents.split()
if len(tokens) != 2: if len(tokens) != 2:
raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0])
return node_factory(tokens[1]) return node_factory(tokens[1])
one_arg_tag_nodes = [ one_arg_tag_nodes = [

View File

@ -146,7 +146,17 @@ class FieldDoesNotExist(Exception):
class BadKeywordArguments(Exception): class BadKeywordArguments(Exception):
pass 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): class RelatedObject(object):
def __init__(self,parent_opts, opts, field): def __init__(self,parent_opts, opts, field):
self.parent_opts = parent_opts 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)) f.get_manipulator_fields(self.opts, manipulator, change, name_prefix=prefix, rel=True))
return fields return fields
class BoundRelatedObject(object): def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject):
pass return bound_related_object_class(self, field_mapping, original)
class Options: class Options:
def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='',
@ -469,7 +479,7 @@ class Options:
self._ordered_objects = objects self._ordered_objects = objects
return self._ordered_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 Returns True if this object's admin form has at least one of the given
field_type (e.g. FileField). 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): def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.utils.text import get_text_list from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_list] field_list = [opts.get_field(field_name) for field_name in field_name_list]
if isinstance(field_list[0].rel, ManyToOne): if isinstance(field_list[0].rel, ManyToOne):
kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data} kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data}
else: else:
kwargs = {'%s__iexact' % field_name_list[0]: field_data} kwargs = {'%s__iexact' % field_name_list[0]: field_data}
for f in field_list[1:]: 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) field_val = all_data.get(f.column, None)
if field_val is None: if field_val is None:
# This will be caught by another validator, assuming the field # This will be caught by another validator, assuming the field

View File

@ -16,7 +16,7 @@ BLANK_CHOICE_DASH = [("", "---------")]
BLANK_CHOICE_NONE = [("", "None")] BLANK_CHOICE_NONE = [("", "None")]
# Values for Relation.edit_inline. # Values for Relation.edit_inline.
TABULAR, STACKED = "admin/edit_inline_tabular", "admin/edit_inline_stacked" TABULAR, STACKED = 1, 2
RECURSIVE_RELATIONSHIP_CONSTANT = 'self' RECURSIVE_RELATIONSHIP_CONSTANT = 'self'

View File

@ -195,6 +195,12 @@ class Context:
if dict.has_key(key): if dict.has_key(key):
return True return True
return False 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): def update(self, other_dict):
"Like dict.update(). Pushes an entire dictionary's keys and values onto the context." "Like dict.update(). Pushes an entire dictionary's keys and values onto the context."

View File

@ -7,17 +7,13 @@ import os
# At compile time, cache the directories to search. # At compile time, cache the directories to search.
app_template_dirs = [] app_template_dirs = []
for app in INSTALLED_APPS: for app in INSTALLED_APPS:
try: i = app.rfind('.')
i = app.rfind('.') m, a = app[:i], app[i+1:]
m, a = app[:i], app[i+1:] mod = getattr(__import__(m, '', '', [a]), a)
mod = getattr(__import__(m, '', '', [a]), a) template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates') if os.path.isdir(template_dir):
if os.path.isdir(template_dir): app_template_dirs.append(template_dir)
app_template_dirs.append(template_dir)
except Exception, e:
print "exception loading"
print e
raise e
# It won't change, so convert it to a tuple to save memory. # It won't change, so convert it to a tuple to save memory.
app_template_dirs = tuple(app_template_dirs) app_template_dirs = tuple(app_template_dirs)

View File

@ -100,7 +100,6 @@ class TestRunner:
self.output(1, "Creating test database") self.output(1, "Creating test database")
try: try:
cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME) cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
print "created"
except Exception, e: except Exception, e:
self.output(0, "There was an error creating the test database:%s " % str(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) confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)