Fixed #10533 -- Worked around an apparent limitation of xgettext.

Looks like we have to use the same format specifiers in both singular and
plural forms of a message or the PO file cannot be compiled to MO format. This
commit changes one place in admin that was using different specifiers.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10081 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2009-03-18 09:19:24 +00:00
parent 4a88785e38
commit 4a67fee785
1 changed files with 117 additions and 114 deletions

View File

@ -32,11 +32,11 @@ class IncorrectLookupParameters(Exception):
# Defaults for formfield_overrides. ModelAdmin subclasses can change this # Defaults for formfield_overrides. ModelAdmin subclasses can change this
# by adding to ModelAdmin.formfield_overrides. # by adding to ModelAdmin.formfield_overrides.
FORMFIELD_FOR_DBFIELD_DEFAULTS = { FORMFIELD_FOR_DBFIELD_DEFAULTS = {
models.DateTimeField: { models.DateTimeField: {
'form_class': forms.SplitDateTimeField, 'form_class': forms.SplitDateTimeField,
'widget': widgets.AdminSplitDateTime 'widget': widgets.AdminSplitDateTime
}, },
models.DateField: {'widget': widgets.AdminDateWidget}, models.DateField: {'widget': widgets.AdminDateWidget},
models.TimeField: {'widget': widgets.AdminTimeWidget}, models.TimeField: {'widget': widgets.AdminTimeWidget},
@ -51,7 +51,7 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
class BaseModelAdmin(object): class BaseModelAdmin(object):
"""Functionality common to both ModelAdmin and InlineAdmin.""" """Functionality common to both ModelAdmin and InlineAdmin."""
raw_id_fields = () raw_id_fields = ()
fields = None fields = None
exclude = None exclude = None
@ -62,57 +62,57 @@ class BaseModelAdmin(object):
radio_fields = {} radio_fields = {}
prepopulated_fields = {} prepopulated_fields = {}
formfield_overrides = {} formfield_overrides = {}
def __init__(self): def __init__(self):
self.formfield_overrides = dict(FORMFIELD_FOR_DBFIELD_DEFAULTS, **self.formfield_overrides) self.formfield_overrides = dict(FORMFIELD_FOR_DBFIELD_DEFAULTS, **self.formfield_overrides)
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" """
Hook for specifying the form Field instance for a given database Field Hook for specifying the form Field instance for a given database Field
instance. instance.
If kwargs are given, they're passed to the form Field's constructor. If kwargs are given, they're passed to the form Field's constructor.
""" """
request = kwargs.pop("request", None) request = kwargs.pop("request", None)
# If the field specifies choices, we don't need to look for special # If the field specifies choices, we don't need to look for special
# admin widgets - we just need to use a select widget of some kind. # admin widgets - we just need to use a select widget of some kind.
if db_field.choices: if db_field.choices:
return self.formfield_for_choice_field(db_field, request, **kwargs) return self.formfield_for_choice_field(db_field, request, **kwargs)
# ForeignKey or ManyToManyFields # ForeignKey or ManyToManyFields
if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)): if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
# Combine the field kwargs with any options for formfield_overrides. # Combine the field kwargs with any options for formfield_overrides.
# Make sure the passed in **kwargs override anything in # Make sure the passed in **kwargs override anything in
# formfield_overrides because **kwargs is more specific, and should # formfield_overrides because **kwargs is more specific, and should
# always win. # always win.
if db_field.__class__ in self.formfield_overrides: if db_field.__class__ in self.formfield_overrides:
kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs) kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs)
# Get the correct formfield. # Get the correct formfield.
if isinstance(db_field, models.ForeignKey): if isinstance(db_field, models.ForeignKey):
formfield = self.formfield_for_foreignkey(db_field, request, **kwargs) formfield = self.formfield_for_foreignkey(db_field, request, **kwargs)
elif isinstance(db_field, models.ManyToManyField): elif isinstance(db_field, models.ManyToManyField):
formfield = self.formfield_for_manytomany(db_field, request, **kwargs) formfield = self.formfield_for_manytomany(db_field, request, **kwargs)
# For non-raw_id fields, wrap the widget with a wrapper that adds # For non-raw_id fields, wrap the widget with a wrapper that adds
# extra HTML -- the "add other" interface -- to the end of the # extra HTML -- the "add other" interface -- to the end of the
# rendered output. formfield can be None if it came from a # rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary. # OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields: if formfield and db_field.name not in self.raw_id_fields:
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site) formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
return formfield return formfield
# If we've got overrides for the formfield defined, use 'em. **kwargs # If we've got overrides for the formfield defined, use 'em. **kwargs
# passed to formfield_for_dbfield override the defaults. # passed to formfield_for_dbfield override the defaults.
if db_field.__class__ in self.formfield_overrides: if db_field.__class__ in self.formfield_overrides:
kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs) kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs)
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
# For any other type of field, just call its formfield() method. # For any other type of field, just call its formfield() method.
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
def formfield_for_choice_field(self, db_field, request=None, **kwargs): def formfield_for_choice_field(self, db_field, request=None, **kwargs):
""" """
Get a form Field for a database Field that has declared choices. Get a form Field for a database Field that has declared choices.
@ -130,7 +130,7 @@ class BaseModelAdmin(object):
blank_choice=[('', _('None'))] blank_choice=[('', _('None'))]
) )
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs): def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
""" """
Get a form Field for a ForeignKey. Get a form Field for a ForeignKey.
@ -142,7 +142,7 @@ class BaseModelAdmin(object):
'class': get_ul_class(self.radio_fields[db_field.name]), 'class': get_ul_class(self.radio_fields[db_field.name]),
}) })
kwargs['empty_label'] = db_field.blank and _('None') or None kwargs['empty_label'] = db_field.blank and _('None') or None
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
def formfield_for_manytomany(self, db_field, request=None, **kwargs): def formfield_for_manytomany(self, db_field, request=None, **kwargs):
@ -152,15 +152,15 @@ class BaseModelAdmin(object):
# If it uses an intermediary model, don't show field in admin. # If it uses an intermediary model, don't show field in admin.
if db_field.rel.through is not None: if db_field.rel.through is not None:
return None return None
if db_field.name in self.raw_id_fields: if db_field.name in self.raw_id_fields:
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel) kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
kwargs['help_text'] = '' kwargs['help_text'] = ''
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
def _declared_fieldsets(self): def _declared_fieldsets(self):
if self.fieldsets: if self.fieldsets:
return self.fieldsets return self.fieldsets
@ -172,7 +172,7 @@ class BaseModelAdmin(object):
class ModelAdmin(BaseModelAdmin): class ModelAdmin(BaseModelAdmin):
"Encapsulates all admin options and functionality for a given model." "Encapsulates all admin options and functionality for a given model."
__metaclass__ = forms.MediaDefiningClass __metaclass__ = forms.MediaDefiningClass
list_display = ('__str__',) list_display = ('__str__',)
list_display_links = () list_display_links = ()
list_filter = () list_filter = ()
@ -185,13 +185,13 @@ class ModelAdmin(BaseModelAdmin):
save_on_top = False save_on_top = False
ordering = None ordering = None
inlines = [] inlines = []
# Custom templates (designed to be over-ridden in subclasses) # Custom templates (designed to be over-ridden in subclasses)
change_form_template = None change_form_template = None
change_list_template = None change_list_template = None
delete_confirmation_template = None delete_confirmation_template = None
object_history_template = None object_history_template = None
def __init__(self, model, admin_site): def __init__(self, model, admin_site):
self.model = model self.model = model
self.opts = model._meta self.opts = model._meta
@ -201,17 +201,17 @@ class ModelAdmin(BaseModelAdmin):
inline_instance = inline_class(self.model, self.admin_site) inline_instance = inline_class(self.model, self.admin_site)
self.inline_instances.append(inline_instance) self.inline_instances.append(inline_instance)
super(ModelAdmin, self).__init__() super(ModelAdmin, self).__init__()
def get_urls(self): def get_urls(self):
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
def wrap(view): def wrap(view):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs) return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view) return update_wrapper(wrapper, view)
info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', url(r'^$',
wrap(self.changelist_view), wrap(self.changelist_view),
@ -230,50 +230,50 @@ class ModelAdmin(BaseModelAdmin):
name='%sadmin_%s_%s_change' % info), name='%sadmin_%s_%s_change' % info),
) )
return urlpatterns return urlpatterns
def urls(self): def urls(self):
return self.get_urls() return self.get_urls()
urls = property(urls) urls = property(urls)
def _media(self): def _media(self):
from django.conf import settings from django.conf import settings
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
if self.prepopulated_fields: if self.prepopulated_fields:
js.append('js/urlify.js') js.append('js/urlify.js')
if self.opts.get_ordered_objects(): if self.opts.get_ordered_objects():
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
media = property(_media) media = property(_media)
def has_add_permission(self, request): def has_add_permission(self, request):
"Returns True if the given request has permission to add an object." "Returns True if the given request has permission to add an object."
opts = self.opts opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission()) return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission())
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
""" """
Returns True if the given request has permission to change the given Returns True if the given request has permission to change the given
Django model instance. Django model instance.
If `obj` is None, this should return True if the given request has If `obj` is None, this should return True if the given request has
permission to change *any* object of the given type. permission to change *any* object of the given type.
""" """
opts = self.opts opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission()) return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission())
def has_delete_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None):
""" """
Returns True if the given request has permission to change the given Returns True if the given request has permission to change the given
Django model instance. Django model instance.
If `obj` is None, this should return True if the given request has If `obj` is None, this should return True if the given request has
permission to delete *any* object of the given type. permission to delete *any* object of the given type.
""" """
opts = self.opts opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()) return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission())
def queryset(self, request): def queryset(self, request):
""" """
Returns a QuerySet of all model instances that can be edited by the Returns a QuerySet of all model instances that can be edited by the
@ -285,14 +285,14 @@ class ModelAdmin(BaseModelAdmin):
if ordering: if ordering:
qs = qs.order_by(*ordering) qs = qs.order_by(*ordering)
return qs return qs
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
"Hook for specifying fieldsets for the add form." "Hook for specifying fieldsets for the add form."
if self.declared_fieldsets: if self.declared_fieldsets:
return self.declared_fieldsets return self.declared_fieldsets
form = self.get_form(request, obj) form = self.get_form(request, obj)
return [(None, {'fields': form.base_fields.keys()})] return [(None, {'fields': form.base_fields.keys()})]
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
""" """
Returns a Form class for use in the admin add view. This is used by Returns a Form class for use in the admin add view. This is used by
@ -314,7 +314,7 @@ class ModelAdmin(BaseModelAdmin):
} }
defaults.update(kwargs) defaults.update(kwargs)
return modelform_factory(self.model, **defaults) return modelform_factory(self.model, **defaults)
def get_changelist_form(self, request, **kwargs): def get_changelist_form(self, request, **kwargs):
""" """
Returns a Form class for use in the Formset on the changelist page. Returns a Form class for use in the Formset on the changelist page.
@ -324,28 +324,28 @@ class ModelAdmin(BaseModelAdmin):
} }
defaults.update(kwargs) defaults.update(kwargs)
return modelform_factory(self.model, **defaults) return modelform_factory(self.model, **defaults)
def get_changelist_formset(self, request, **kwargs): def get_changelist_formset(self, request, **kwargs):
""" """
Returns a FormSet class for use on the changelist page if list_editable Returns a FormSet class for use on the changelist page if list_editable
is used. is used.
""" """
defaults = { defaults = {
"formfield_callback": curry(self.formfield_for_dbfield, request=request), "formfield_callback": curry(self.formfield_for_dbfield, request=request),
} }
defaults.update(kwargs) defaults.update(kwargs)
return modelformset_factory(self.model, return modelformset_factory(self.model,
self.get_changelist_form(request), extra=0, self.get_changelist_form(request), extra=0,
fields=self.list_editable, **defaults) fields=self.list_editable, **defaults)
def get_formsets(self, request, obj=None): def get_formsets(self, request, obj=None):
for inline in self.inline_instances: for inline in self.inline_instances:
yield inline.get_formset(request, obj) yield inline.get_formset(request, obj)
def log_addition(self, request, object): def log_addition(self, request, object):
""" """
Log that an object has been successfully added. Log that an object has been successfully added.
The default implementation creates an admin LogEntry object. The default implementation creates an admin LogEntry object.
""" """
from django.contrib.admin.models import LogEntry, ADDITION from django.contrib.admin.models import LogEntry, ADDITION
@ -356,11 +356,11 @@ class ModelAdmin(BaseModelAdmin):
object_repr = force_unicode(object), object_repr = force_unicode(object),
action_flag = ADDITION action_flag = ADDITION
) )
def log_change(self, request, object, message): def log_change(self, request, object, message):
""" """
Log that an object has been successfully changed. Log that an object has been successfully changed.
The default implementation creates an admin LogEntry object. The default implementation creates an admin LogEntry object.
""" """
from django.contrib.admin.models import LogEntry, CHANGE from django.contrib.admin.models import LogEntry, CHANGE
@ -372,13 +372,13 @@ class ModelAdmin(BaseModelAdmin):
action_flag = CHANGE, action_flag = CHANGE,
change_message = message change_message = message
) )
def log_deletion(self, request, object, object_repr): def log_deletion(self, request, object, object_repr):
""" """
Log that an object has been successfully deleted. Note that since the Log that an object has been successfully deleted. Note that since the
object is deleted, it might no longer be safe to call *any* methods object is deleted, it might no longer be safe to call *any* methods
on the object, hence this method getting object_repr. on the object, hence this method getting object_repr.
The default implementation creates an admin LogEntry object. The default implementation creates an admin LogEntry object.
""" """
from django.contrib.admin.models import LogEntry, DELETION from django.contrib.admin.models import LogEntry, DELETION
@ -389,8 +389,8 @@ class ModelAdmin(BaseModelAdmin):
object_repr = object_repr, object_repr = object_repr,
action_flag = DELETION action_flag = DELETION
) )
def construct_change_message(self, request, form, formsets): def construct_change_message(self, request, form, formsets):
""" """
Construct a change message from a changed object. Construct a change message from a changed object.
@ -398,7 +398,7 @@ class ModelAdmin(BaseModelAdmin):
change_message = [] change_message = []
if form.changed_data: if form.changed_data:
change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and'))) change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and')))
if formsets: if formsets:
for formset in formsets: for formset in formsets:
for added_object in formset.new_objects: for added_object in formset.new_objects:
@ -416,33 +416,33 @@ class ModelAdmin(BaseModelAdmin):
'object': force_unicode(deleted_object)}) 'object': force_unicode(deleted_object)})
change_message = ' '.join(change_message) change_message = ' '.join(change_message)
return change_message or _('No fields changed.') return change_message or _('No fields changed.')
def message_user(self, request, message): def message_user(self, request, message):
""" """
Send a message to the user. The default implementation Send a message to the user. The default implementation
posts a message using the auth Message object. posts a message using the auth Message object.
""" """
request.user.message_set.create(message=message) request.user.message_set.create(message=message)
def save_form(self, request, form, change): def save_form(self, request, form, change):
""" """
Given a ModelForm return an unsaved instance. ``change`` is True if Given a ModelForm return an unsaved instance. ``change`` is True if
the object is being changed, and False if it's being added. the object is being changed, and False if it's being added.
""" """
return form.save(commit=False) return form.save(commit=False)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
""" """
Given a model instance save it to the database. Given a model instance save it to the database.
""" """
obj.save() obj.save()
def save_formset(self, request, form, formset, change): def save_formset(self, request, form, formset, change):
""" """
Given an inline formset save it to the database. Given an inline formset save it to the database.
""" """
formset.save() formset.save()
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
opts = self.model._meta opts = self.model._meta
app_label = opts.app_label app_label = opts.app_label
@ -468,14 +468,14 @@ class ModelAdmin(BaseModelAdmin):
"admin/%s/change_form.html" % app_label, "admin/%s/change_form.html" % app_label,
"admin/change_form.html" "admin/change_form.html"
], context, context_instance=template.RequestContext(request)) ], context, context_instance=template.RequestContext(request))
def response_add(self, request, obj, post_url_continue='../%s/'): def response_add(self, request, obj, post_url_continue='../%s/'):
""" """
Determines the HttpResponse for the add_view stage. Determines the HttpResponse for the add_view stage.
""" """
opts = obj._meta opts = obj._meta
pk_value = obj._get_pk_val() pk_value = obj._get_pk_val()
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
# Here, we distinguish between different save types by checking for # Here, we distinguish between different save types by checking for
# the presence of keys in request.POST. # the presence of keys in request.POST.
@ -484,7 +484,7 @@ class ModelAdmin(BaseModelAdmin):
if request.POST.has_key("_popup"): if request.POST.has_key("_popup"):
post_url_continue += "?_popup=1" post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value) return HttpResponseRedirect(post_url_continue % pk_value)
if request.POST.has_key("_popup"): if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \ return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode. # escape() calls force_unicode.
@ -494,7 +494,7 @@ class ModelAdmin(BaseModelAdmin):
return HttpResponseRedirect(request.path) return HttpResponseRedirect(request.path)
else: else:
self.message_user(request, msg) self.message_user(request, msg)
# Figure out where to redirect. If the user has change permission, # Figure out where to redirect. If the user has change permission,
# redirect to the change-list page for this object. Otherwise, # redirect to the change-list page for this object. Otherwise,
# redirect to the admin index. # redirect to the admin index.
@ -503,14 +503,14 @@ class ModelAdmin(BaseModelAdmin):
else: else:
post_url = '../../../' post_url = '../../../'
return HttpResponseRedirect(post_url) return HttpResponseRedirect(post_url)
def response_change(self, request, obj): def response_change(self, request, obj):
""" """
Determines the HttpResponse for the change_view stage. Determines the HttpResponse for the change_view stage.
""" """
opts = obj._meta opts = obj._meta
pk_value = obj._get_pk_val() pk_value = obj._get_pk_val()
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
if request.POST.has_key("_continue"): if request.POST.has_key("_continue"):
self.message_user(request, msg + ' ' + _("You may edit it again below.")) self.message_user(request, msg + ' ' + _("You may edit it again below."))
@ -528,15 +528,15 @@ class ModelAdmin(BaseModelAdmin):
else: else:
self.message_user(request, msg) self.message_user(request, msg)
return HttpResponseRedirect("../") return HttpResponseRedirect("../")
def add_view(self, request, form_url='', extra_context=None): def add_view(self, request, form_url='', extra_context=None):
"The 'add' admin view for this model." "The 'add' admin view for this model."
model = self.model model = self.model
opts = model._meta opts = model._meta
if not self.has_add_permission(request): if not self.has_add_permission(request):
raise PermissionDenied raise PermissionDenied
ModelForm = self.get_form(request) ModelForm = self.get_form(request)
formsets = [] formsets = []
if request.method == 'POST': if request.method == 'POST':
@ -563,7 +563,7 @@ class ModelAdmin(BaseModelAdmin):
form.save_m2m() form.save_m2m()
for formset in formsets: for formset in formsets:
self.save_formset(request, form, formset, change=False) self.save_formset(request, form, formset, change=False)
self.log_addition(request, new_object) self.log_addition(request, new_object)
return self.response_add(request, new_object) return self.response_add(request, new_object)
else: else:
@ -586,17 +586,17 @@ class ModelAdmin(BaseModelAdmin):
prefix = "%s-%s" % (prefix, prefixes[prefix]) prefix = "%s-%s" % (prefix, prefixes[prefix])
formset = FormSet(instance=self.model(), prefix=prefix) formset = FormSet(instance=self.model(), prefix=prefix)
formsets.append(formset) formsets.append(formset)
adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields) adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields)
media = self.media + adminForm.media media = self.media + adminForm.media
inline_admin_formsets = [] inline_admin_formsets = []
for inline, formset in zip(self.inline_instances, formsets): for inline, formset in zip(self.inline_instances, formsets):
fieldsets = list(inline.get_fieldsets(request)) fieldsets = list(inline.get_fieldsets(request))
inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets) inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
inline_admin_formsets.append(inline_admin_formset) inline_admin_formsets.append(inline_admin_formset)
media = media + inline_admin_formset.media media = media + inline_admin_formset.media
context = { context = {
'title': _('Add %s') % force_unicode(opts.verbose_name), 'title': _('Add %s') % force_unicode(opts.verbose_name),
'adminform': adminForm, 'adminform': adminForm,
@ -611,12 +611,12 @@ class ModelAdmin(BaseModelAdmin):
context.update(extra_context or {}) context.update(extra_context or {})
return self.render_change_form(request, context, add=True) return self.render_change_form(request, context, add=True)
add_view = transaction.commit_on_success(add_view) add_view = transaction.commit_on_success(add_view)
def change_view(self, request, object_id, extra_context=None): def change_view(self, request, object_id, extra_context=None):
"The 'change' admin view for this model." "The 'change' admin view for this model."
model = self.model model = self.model
opts = model._meta opts = model._meta
try: try:
obj = model._default_manager.get(pk=unquote(object_id)) obj = model._default_manager.get(pk=unquote(object_id))
except model.DoesNotExist: except model.DoesNotExist:
@ -624,16 +624,16 @@ class ModelAdmin(BaseModelAdmin):
# permissions yet. We don't want an unauthenticated user to be able # permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists. # to determine whether a given object exists.
obj = None obj = None
if not self.has_change_permission(request, obj): if not self.has_change_permission(request, obj):
raise PermissionDenied raise PermissionDenied
if obj is None: if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)}) raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
if request.method == 'POST' and request.POST.has_key("_saveasnew"): if request.method == 'POST' and request.POST.has_key("_saveasnew"):
return self.add_view(request, form_url='../../add/') return self.add_view(request, form_url='../../add/')
ModelForm = self.get_form(request, obj) ModelForm = self.get_form(request, obj)
formsets = [] formsets = []
if request.method == 'POST': if request.method == 'POST':
@ -653,17 +653,17 @@ class ModelAdmin(BaseModelAdmin):
formset = FormSet(request.POST, request.FILES, formset = FormSet(request.POST, request.FILES,
instance=new_object, prefix=prefix) instance=new_object, prefix=prefix)
formsets.append(formset) formsets.append(formset)
if all_valid(formsets) and form_validated: if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, change=True) self.save_model(request, new_object, form, change=True)
form.save_m2m() form.save_m2m()
for formset in formsets: for formset in formsets:
self.save_formset(request, form, formset, change=True) self.save_formset(request, form, formset, change=True)
change_message = self.construct_change_message(request, form, formsets) change_message = self.construct_change_message(request, form, formsets)
self.log_change(request, new_object, change_message) self.log_change(request, new_object, change_message)
return self.response_change(request, new_object) return self.response_change(request, new_object)
else: else:
form = ModelForm(instance=obj) form = ModelForm(instance=obj)
prefixes = {} prefixes = {}
@ -674,17 +674,17 @@ class ModelAdmin(BaseModelAdmin):
prefix = "%s-%s" % (prefix, prefixes[prefix]) prefix = "%s-%s" % (prefix, prefixes[prefix])
formset = FormSet(instance=obj, prefix=prefix) formset = FormSet(instance=obj, prefix=prefix)
formsets.append(formset) formsets.append(formset)
adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields) adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields)
media = self.media + adminForm.media media = self.media + adminForm.media
inline_admin_formsets = [] inline_admin_formsets = []
for inline, formset in zip(self.inline_instances, formsets): for inline, formset in zip(self.inline_instances, formsets):
fieldsets = list(inline.get_fieldsets(request, obj)) fieldsets = list(inline.get_fieldsets(request, obj))
inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets) inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
inline_admin_formsets.append(inline_admin_formset) inline_admin_formsets.append(inline_admin_formset)
media = media + inline_admin_formset.media media = media + inline_admin_formset.media
context = { context = {
'title': _('Change %s') % force_unicode(opts.verbose_name), 'title': _('Change %s') % force_unicode(opts.verbose_name),
'adminform': adminForm, 'adminform': adminForm,
@ -700,7 +700,7 @@ class ModelAdmin(BaseModelAdmin):
context.update(extra_context or {}) context.update(extra_context or {})
return self.render_change_form(request, context, change=True, obj=obj) return self.render_change_form(request, context, change=True, obj=obj)
change_view = transaction.commit_on_success(change_view) change_view = transaction.commit_on_success(change_view)
def changelist_view(self, request, extra_context=None): def changelist_view(self, request, extra_context=None):
"The 'change list' admin view for this model." "The 'change list' admin view for this model."
from django.contrib.admin.views.main import ChangeList, ERROR_FLAG from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
@ -720,12 +720,12 @@ class ModelAdmin(BaseModelAdmin):
if ERROR_FLAG in request.GET.keys(): if ERROR_FLAG in request.GET.keys():
return render_to_response('admin/invalid_setup.html', {'title': _('Database error')}) return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
# If we're allowing changelist editing, we need to construct a formset # If we're allowing changelist editing, we need to construct a formset
# for the changelist given all the fields to be edited. Then we'll # for the changelist given all the fields to be edited. Then we'll
# use the formset to validate/process POSTed data. # use the formset to validate/process POSTed data.
formset = cl.formset = None formset = cl.formset = None
# Handle POSTed bulk-edit data. # Handle POSTed bulk-edit data.
if request.method == "POST" and self.list_editable: if request.method == "POST" and self.list_editable:
FormSet = self.get_changelist_formset(request) FormSet = self.get_changelist_formset(request)
@ -740,23 +740,26 @@ class ModelAdmin(BaseModelAdmin):
change_msg = self.construct_change_message(request, form, None) change_msg = self.construct_change_message(request, form, None)
self.log_change(request, obj, change_msg) self.log_change(request, obj, change_msg)
changecount += 1 changecount += 1
if changecount: if changecount:
msg = ngettext("%(count)s %(singular)s was changed successfully.", if changecount == 1:
"%(count)s %(plural)s were changed successfully.", name = force_unicode(opts.verbose_name)
else:
name = force_unicode(opts.verbose_name_plural)
msg = ngettext("%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount, changecount) % {'count': changecount,
'singular': force_unicode(opts.verbose_name), 'name': name,
'plural': force_unicode(opts.verbose_name_plural),
'obj': force_unicode(obj)} 'obj': force_unicode(obj)}
self.message_user(request, msg) self.message_user(request, msg)
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
# Handle GET -- construct a formset for display. # Handle GET -- construct a formset for display.
elif self.list_editable: elif self.list_editable:
FormSet = self.get_changelist_formset(request) FormSet = self.get_changelist_formset(request)
formset = cl.formset = FormSet(queryset=cl.result_list) formset = cl.formset = FormSet(queryset=cl.result_list)
# Build the list of media to be used by the formset. # Build the list of media to be used by the formset.
if formset: if formset:
media = self.media + formset.media media = self.media + formset.media
@ -778,12 +781,12 @@ class ModelAdmin(BaseModelAdmin):
'admin/%s/change_list.html' % app_label, 'admin/%s/change_list.html' % app_label,
'admin/change_list.html' 'admin/change_list.html'
], context, context_instance=template.RequestContext(request)) ], context, context_instance=template.RequestContext(request))
def delete_view(self, request, object_id, extra_context=None): def delete_view(self, request, object_id, extra_context=None):
"The 'delete' admin view for this model." "The 'delete' admin view for this model."
opts = self.model._meta opts = self.model._meta
app_label = opts.app_label app_label = opts.app_label
try: try:
obj = self.model._default_manager.get(pk=unquote(object_id)) obj = self.model._default_manager.get(pk=unquote(object_id))
except self.model.DoesNotExist: except self.model.DoesNotExist:
@ -791,32 +794,32 @@ class ModelAdmin(BaseModelAdmin):
# permissions yet. We don't want an unauthenticated user to be able # permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists. # to determine whether a given object exists.
obj = None obj = None
if not self.has_delete_permission(request, obj): if not self.has_delete_permission(request, obj):
raise PermissionDenied raise PermissionDenied
if obj is None: if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)}) raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
# Populate deleted_objects, a data structure of all related objects that # Populate deleted_objects, a data structure of all related objects that
# will also be deleted. # will also be deleted.
deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), object_id, escape(obj))), []] deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), object_id, escape(obj))), []]
perms_needed = set() perms_needed = set()
get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site) get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site)
if request.POST: # The user has already confirmed the deletion. if request.POST: # The user has already confirmed the deletion.
if perms_needed: if perms_needed:
raise PermissionDenied raise PermissionDenied
obj_display = force_unicode(obj) obj_display = force_unicode(obj)
obj.delete() obj.delete()
self.log_deletion(request, obj, obj_display) self.log_deletion(request, obj, obj_display)
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)}) self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
if not self.has_change_permission(request, None): if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../") return HttpResponseRedirect("../../../../")
return HttpResponseRedirect("../../") return HttpResponseRedirect("../../")
context = { context = {
"title": _("Are you sure?"), "title": _("Are you sure?"),
"object_name": force_unicode(opts.verbose_name), "object_name": force_unicode(opts.verbose_name),
@ -833,7 +836,7 @@ class ModelAdmin(BaseModelAdmin):
"admin/%s/delete_confirmation.html" % app_label, "admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html" "admin/delete_confirmation.html"
], context, context_instance=template.RequestContext(request)) ], context, context_instance=template.RequestContext(request))
def history_view(self, request, object_id, extra_context=None): def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model." "The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry from django.contrib.admin.models import LogEntry
@ -869,11 +872,11 @@ class ModelAdmin(BaseModelAdmin):
DEPRECATED: this is the old way of URL resolution, replaced by DEPRECATED: this is the old way of URL resolution, replaced by
``get_urls()``. This only called by AdminSite.root(), which is also ``get_urls()``. This only called by AdminSite.root(), which is also
deprecated. deprecated.
Again, remember that the following code only exists for Again, remember that the following code only exists for
backwards-compatibility. Any new URLs, changes to existing URLs, or backwards-compatibility. Any new URLs, changes to existing URLs, or
whatever need to be done up in get_urls(), above! whatever need to be done up in get_urls(), above!
This function still exists for backwards-compatibility; it will be This function still exists for backwards-compatibility; it will be
removed in Django 1.3. removed in Django 1.3.
""" """
@ -892,7 +895,7 @@ class ModelAdmin(BaseModelAdmin):
class InlineModelAdmin(BaseModelAdmin): class InlineModelAdmin(BaseModelAdmin):
""" """
Options for inline editing of ``model`` instances. Options for inline editing of ``model`` instances.
Provide ``name`` to specify the attribute name of the ``ForeignKey`` from Provide ``name`` to specify the attribute name of the ``ForeignKey`` from
``model`` to its parent. This is required if ``model`` has more than one ``model`` to its parent. This is required if ``model`` has more than one
``ForeignKey`` to its parent. ``ForeignKey`` to its parent.
@ -905,7 +908,7 @@ class InlineModelAdmin(BaseModelAdmin):
template = None template = None
verbose_name = None verbose_name = None
verbose_name_plural = None verbose_name_plural = None
def __init__(self, parent_model, admin_site): def __init__(self, parent_model, admin_site):
self.admin_site = admin_site self.admin_site = admin_site
self.parent_model = parent_model self.parent_model = parent_model
@ -915,7 +918,7 @@ class InlineModelAdmin(BaseModelAdmin):
self.verbose_name = self.model._meta.verbose_name self.verbose_name = self.model._meta.verbose_name
if self.verbose_name_plural is None: if self.verbose_name_plural is None:
self.verbose_name_plural = self.model._meta.verbose_name_plural self.verbose_name_plural = self.model._meta.verbose_name_plural
def _media(self): def _media(self):
from django.conf import settings from django.conf import settings
js = [] js = []
@ -925,7 +928,7 @@ class InlineModelAdmin(BaseModelAdmin):
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
media = property(_media) media = property(_media)
def get_formset(self, request, obj=None, **kwargs): def get_formset(self, request, obj=None, **kwargs):
"""Returns a BaseInlineFormSet class for use in admin add/change views.""" """Returns a BaseInlineFormSet class for use in admin add/change views."""
if self.declared_fieldsets: if self.declared_fieldsets:
@ -948,13 +951,13 @@ class InlineModelAdmin(BaseModelAdmin):
} }
defaults.update(kwargs) defaults.update(kwargs)
return inlineformset_factory(self.parent_model, self.model, **defaults) return inlineformset_factory(self.parent_model, self.model, **defaults)
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
if self.declared_fieldsets: if self.declared_fieldsets:
return self.declared_fieldsets return self.declared_fieldsets
form = self.get_formset(request).form form = self.get_formset(request).form
return [(None, {'fields': form.base_fields.keys()})] return [(None, {'fields': form.base_fields.keys()})]
class StackedInline(InlineModelAdmin): class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html' template = 'admin/edit_inline/stacked.html'