summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/admin/options.py57
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py4
-rw-r--r--django/contrib/admin/views/main.py15
-rw-r--r--django/core/management.py81
-rw-r--r--django/db/models/__init__.py2
-rw-r--r--django/db/models/base.py7
-rw-r--r--django/db/models/options.py59
-rw-r--r--tests/regressiontests/invalid_admin_options/models.py14
8 files changed, 108 insertions, 131 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index dd321c389b..aafea0d09f 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -30,6 +30,43 @@ def unquote(s):
myappend('_' + item)
return "".join(res)
+class AdminFieldSet(object):
+ def __init__(self, name, classes, field_locator_func, line_specs, description):
+ self.name = name
+ self.field_lines = [AdminFieldLine(field_locator_func, line_spec) for line_spec in line_specs]
+ self.classes = classes
+ self.description = description
+
+ def __repr__(self):
+ return "FieldSet: (%s, %s)" % (self.name, self.field_lines)
+
+ def bind(self, field_mapping, original, bound_field_set_class):
+ 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 AdminFieldLine(object):
+ def __init__(self, field_locator_func, linespec):
+ 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):
+ 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 ModelAdmin(object):
"Encapsulates all admin options and functionality for a given model."
@@ -44,6 +81,7 @@ class ModelAdmin(object):
save_on_top = False
ordering = None
js = None
+ fields = None
def __init__(self, model):
self.model = model
@@ -74,6 +112,21 @@ class ModelAdmin(object):
else:
return self.change_view(request, unquote(url))
+ def get_field_sets(self, opts):
+ "Returns a list of AdminFieldSet objects."
+ 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, models.AutoField)]}),)
+ else:
+ field_struct = self.fields
+ new_fieldset_list = []
+ for fieldset in field_struct:
+ fs_options = fieldset[1]
+ classes = fs_options.get('classes', ())
+ description = fs_options.get('description', '')
+ new_fieldset_list.append(AdminFieldSet(fieldset[0], classes,
+ opts.get_field, fs_options['fields'], description))
+ return new_fieldset_list
+
def has_add_permission(self, request):
"Returns True if the given request has permission to add an object."
opts = self.opts
@@ -173,7 +226,7 @@ class ModelAdmin(object):
if object_id_override is not None:
c['object_id'] = object_id_override
- return render_change_form(model, manipulator, c, add=True)
+ return render_change_form(self, model, manipulator, c, add=True)
def change_view(self, request, object_id):
"The 'change' admin view for this model."
@@ -272,7 +325,7 @@ class ModelAdmin(object):
'original': manipulator.original_object,
'is_popup': request.REQUEST.has_key('_popup'),
})
- return render_change_form(model, manipulator, c, change=True)
+ return render_change_form(self, model, manipulator, c, change=True)
def change_list_view(self, request):
"The 'change list' admin view for this model."
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 5c9009ea4e..fcb5876b0c 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -41,8 +41,8 @@ def submit_row(context):
and 'onclick="submitOrderForm();"' or ''),
'show_delete_link': (not is_popup and context['has_delete_permission']
and (change or context['show_delete'])),
- 'show_save_as_new': not is_popup and change and opts.ModelAdmin.save_as,
- 'show_save_and_add_another': not is_popup and (not opts.ModelAdmin.save_as or context['add']),
+ 'show_save_as_new': not is_popup and change and opts.admin.save_as,
+ 'show_save_and_add_another': not is_popup and (not opts.admin.save_as or context['add']),
'show_save_and_continue': not is_popup and context['has_change_permission'],
'show_save': True
}
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index d3ca7a7070..908aee74dd 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -57,8 +57,8 @@ def get_javascript_imports(opts, auto_populated_fields, field_sets):
js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
if opts.get_ordered_objects():
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
- if opts.ModelAdmin.js:
- js.extend(opts.ModelAdmin.js)
+ if opts.admin.js:
+ js.extend(opts.admin.js)
seen_collapse = False
for field_set in field_sets:
if not seen_collapse and 'collapse' in field_set.classes:
@@ -81,7 +81,7 @@ def model_admin_view(request, app_label, model_name, rest_of_url):
raise Http404("App %r, model %r, not found" % (app_label, model_name))
if not model._meta.admin:
raise Http404("This object has no admin interface.")
- mav = model._meta.ModelAdmin(model)
+ mav = model._meta.admin(model)
return mav(request, rest_of_url)
model_admin_view = staff_member_required(never_cache(model_admin_view))
@@ -166,11 +166,11 @@ class AdminBoundFieldSet(object):
def __len__(self):
return len(self.bound_field_lines)
-def render_change_form(model, manipulator, context, add=False, change=False, form_url=''):
+def render_change_form(model_admin, model, manipulator, context, add=False, change=False, form_url=''):
opts = model._meta
app_label = opts.app_label
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
- field_sets = opts.admin.get_field_sets(opts)
+ field_sets = model_admin.get_field_sets(opts)
original = getattr(manipulator, 'original_object', None)
bound_field_sets = [field_set.bind(context['form'], original, AdminBoundFieldSet) for field_set in field_sets]
first_form_field_id = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
@@ -192,7 +192,7 @@ def render_change_form(model, manipulator, context, add=False, change=False, for
'form_url': form_url,
'opts': opts,
'content_type_id': ContentType.objects.get_for_model(model).id,
- 'save_on_top': opts.ModelAdmin.save_on_top,
+ 'save_on_top': model_admin.save_on_top,
}
context.update(extra_context)
return render_to_response([
@@ -305,6 +305,7 @@ class ChangeList(object):
self.search_fields = search_fields
self.list_select_related = list_select_related
self.list_per_page = list_per_page
+ self.model_admin = model_admin
# Get search parameters from the query string.
try:
@@ -399,7 +400,7 @@ class ChangeList(object):
# then check the object's default ordering. If neither of those exist,
# order descending by ID by default. Finally, look for manually-specified
# ordering from the query string.
- ordering = lookup_opts.ModelAdmin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
+ ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
# Normalize it to new-style ordering.
ordering = handle_legacy_orderlist(ordering)
diff --git a/django/core/management.py b/django/core/management.py
index 812f00021d..cbea3bafbc 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -979,51 +979,48 @@ def get_validation_errors(outfile, app=None):
# Check admin attribute.
if opts.admin is not None:
- if not isinstance(opts.admin, models.AdminOptions):
- e.add(opts, '"admin" attribute, if given, must be set to a models.AdminOptions() instance.')
+ # list_display
+ if not isinstance(opts.admin.list_display, (list, tuple)):
+ e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
else:
- # list_display
- if not isinstance(opts.ModelAdmin.list_display, (list, tuple)):
- e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
- else:
- for fn in opts.ModelAdmin.list_display:
- try:
- f = opts.get_field(fn)
- except models.FieldDoesNotExist:
- if not hasattr(cls, fn):
- e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn)
- else:
- if isinstance(f, models.ManyToManyField):
- e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
- # list_display_links
- if opts.ModelAdmin.list_display_links and not opts.ModelAdmin.list_display:
- e.add(opts, '"admin.list_display" must be defined for "admin.list_display_links" to be used.')
- if not isinstance(opts.ModelAdmin.list_display_links, (list, tuple)):
- e.add(opts, '"admin.list_display_links", if given, must be set to a list or tuple.')
- else:
- for fn in opts.ModelAdmin.list_display_links:
- try:
- f = opts.get_field(fn)
- except models.FieldDoesNotExist:
- if not hasattr(cls, fn):
- e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
- if fn not in opts.ModelAdmin.list_display:
- e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
- # list_filter
- if not isinstance(opts.ModelAdmin.list_filter, (list, tuple)):
- e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
- else:
- for fn in opts.ModelAdmin.list_filter:
- try:
- f = opts.get_field(fn)
- except models.FieldDoesNotExist:
- e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
- # date_hierarchy
- if opts.ModelAdmin.date_hierarchy:
+ for fn in opts.admin.list_display:
+ try:
+ f = opts.get_field(fn)
+ except models.FieldDoesNotExist:
+ if not hasattr(cls, fn):
+ e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn)
+ else:
+ if isinstance(f, models.ManyToManyField):
+ e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
+ # list_display_links
+ if opts.admin.list_display_links and not opts.admin.list_display:
+ e.add(opts, '"admin.list_display" must be defined for "admin.list_display_links" to be used.')
+ if not isinstance(opts.admin.list_display_links, (list, tuple)):
+ e.add(opts, '"admin.list_display_links", if given, must be set to a list or tuple.')
+ else:
+ for fn in opts.admin.list_display_links:
+ try:
+ f = opts.get_field(fn)
+ except models.FieldDoesNotExist:
+ if not hasattr(cls, fn):
+ e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
+ if fn not in opts.admin.list_display:
+ e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
+ # list_filter
+ if not isinstance(opts.admin.list_filter, (list, tuple)):
+ e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
+ else:
+ for fn in opts.admin.list_filter:
try:
- f = opts.get_field(opts.ModelAdmin.date_hierarchy)
+ f = opts.get_field(fn)
except models.FieldDoesNotExist:
- e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.ModelAdmin.date_hierarchy)
+ e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
+ # date_hierarchy
+ if opts.admin.date_hierarchy:
+ try:
+ f = opts.get_field(opts.admin.date_hierarchy)
+ except models.FieldDoesNotExist:
+ e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy)
# Check ordering attribute.
if opts.ordering:
diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
index 0308dd047a..964f84ba86 100644
--- a/django/db/models/__init__.py
+++ b/django/db/models/__init__.py
@@ -5,7 +5,7 @@ from django.db import backend, connection
from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
from django.db.models.query import Q
from django.db.models.manager import Manager
-from django.db.models.base import Model, AdminOptions
+from django.db.models.base import Model
from django.db.models.fields import *
from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
from django.db.models.fields.generic import GenericRelation, GenericRel, GenericForeignKey
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 98608d8915..58a6fcb75c 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel, ManyToOneRel
from django.db.models.query import delete_objects
-from django.db.models.options import Options, AdminOptions
+from django.db.models.options import Options
from django.db import connection, backend, transaction
from django.db.models import signals
from django.db.models.loading import register_models, get_model
@@ -135,10 +135,7 @@ class Model(object):
# of both ModelAdmin and the 'class Admin' on this model. The
# resulting class is same as if the 'class Admin' were a subclass
# of ModelAdmin.
- cls._meta.ModelAdmin = type('ModelAdmin', (value, ModelAdmin), {})
- # This AdminOptions stuff is legacy and will eventually be removed.
- value = AdminOptions(**dict([(k, v) for k, v in value.__dict__.items() if not k.startswith('_') and k not in ('list_display', 'list_display_links', 'list_filter', 'date_hierarchy', 'save_as', 'search_fields', 'list_select_related', 'list_per_page', 'ordering', 'save_on_top', 'js', 'manager')]))
- value.contribute_to_class(cls, name)
+ cls._meta.admin = type('ModelAdmin', (value, ModelAdmin), {})
elif hasattr(value, 'contribute_to_class'):
value.contribute_to_class(cls, name)
else:
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 015e612dec..eafb6f0e03 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -196,62 +196,3 @@ class Options(object):
else:
self._field_types[field_type] = False
return self._field_types[field_type]
-
-class AdminOptions(object):
- def __init__(self, fields=None):
- self.fields = fields
-
- def get_field_sets(self, opts):
- "Returns a list of AdminFieldSet objects for this AdminOptions object."
- 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:
- fs_options = fieldset[1]
- classes = fs_options.get('classes', ())
- description = fs_options.get('description', '')
- new_fieldset_list.append(AdminFieldSet(fieldset[0], classes,
- opts.get_field, fs_options['fields'], description))
- return new_fieldset_list
-
- def contribute_to_class(self, cls, name):
- cls._meta.admin = self
-
-class AdminFieldSet(object):
- def __init__(self, name, classes, field_locator_func, line_specs, description):
- self.name = name
- self.field_lines = [AdminFieldLine(field_locator_func, line_spec) for line_spec in line_specs]
- self.classes = classes
- self.description = description
-
- def __repr__(self):
- return "FieldSet: (%s, %s)" % (self.name, self.field_lines)
-
- def bind(self, field_mapping, original, bound_field_set_class):
- 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 AdminFieldLine(object):
- def __init__(self, field_locator_func, linespec):
- 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):
- 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)
diff --git a/tests/regressiontests/invalid_admin_options/models.py b/tests/regressiontests/invalid_admin_options/models.py
index 43bcc533ba..94e6ccdde1 100644
--- a/tests/regressiontests/invalid_admin_options/models.py
+++ b/tests/regressiontests/invalid_admin_options/models.py
@@ -8,18 +8,6 @@ model validation is working properly.
from django.db import models
model_errors = ""
-# TODO: Invalid admin options should not cause a metaclass error
-##This should fail gracefully but is causing a metaclass error
-#class BadAdminOption(models.Model):
-# "Test nonexistent admin option"
-# name = models.CharField(maxlength=30)
-#
-# class Admin:
-# nonexistent = 'option'
-#
-#model_errors += """invalid_admin_options.badadminoption: "admin" attribute, if given, must be set to a models.AdminOptions() instance.
-#"""
-
class ListDisplayBadOne(models.Model):
"Test list_display, list_display must be a list or tuple"
first_name = models.CharField(maxlength=30)
@@ -334,4 +322,4 @@ class OrderingBad(models.Model):
# manager = 'nonexistent'
#
#model_errors += """invalid_admin_options.managerbad: "admin.manager" refers to 'nonexistent', which isn't a Manager().
-#""" \ No newline at end of file
+#"""