Permalink
Browse files

newforms-admin: Fixed #4418 -- Added the ability for widgets, forms, …

…and Admin declarations to have media definitions.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 28e3503 commit 549198b714ba0b032904639af0c388e739261fe6 @freakboy3742 freakboy3742 committed Aug 18, 2007
@@ -2,6 +2,7 @@
from django import newforms as forms
from django.newforms.formsets import all_valid
from django.newforms.models import inline_formset
+from django.newforms.widgets import Media, MediaDefiningClass
from django.contrib.admin import widgets
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.db import models
@@ -48,13 +49,27 @@ def __iter__(self):
def first_field(self):
for bf in self.form:
return bf
+
+ def _media(self):
+ media = self.form.media
+ for fs in self.fieldsets:
+ media = media + fs.media
+ return media
+ media = property(_media)
class Fieldset(object):
def __init__(self, name=None, fields=(), classes=(), description=None):
self.name, self.fields = name, fields
self.classes = u' '.join(classes)
self.description = description
+ def _media(self):
+ from django.conf import settings
+ if 'collapse' in self.classes:
+ return Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
+ return Media()
+ media = property(_media)
+
class BoundFieldset(object):
def __init__(self, form, fieldset):
self.form, self.fieldset = form, fieldset
@@ -123,12 +138,12 @@ def formfield_for_dbfield(self, db_field, **kwargs):
# For DateFields, add a custom CSS class.
if isinstance(db_field, models.DateField):
- kwargs['widget'] = forms.TextInput(attrs={'class': 'vDateField', 'size': '10'})
+ kwargs['widget'] = widgets.AdminDateWidget
return db_field.formfield(**kwargs)
# For TimeFields, add a custom CSS class.
if isinstance(db_field, models.TimeField):
- kwargs['widget'] = forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})
+ kwargs['widget'] = widgets.AdminTimeWidget
return db_field.formfield(**kwargs)
# For ForeignKey or ManyToManyFields, use a special widget.
@@ -148,7 +163,8 @@ def formfield_for_dbfield(self, db_field, **kwargs):
class ModelAdmin(BaseModelAdmin):
"Encapsulates all admin options and functionality for a given model."
-
+ __metaclass__ = MediaDefiningClass
+
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
@@ -159,7 +175,6 @@ class ModelAdmin(BaseModelAdmin):
save_as = False
save_on_top = False
ordering = None
- js = None
prepopulated_fields = {}
filter_vertical = ()
filter_horizontal = ()
@@ -194,38 +209,20 @@ def __call__(self, request, url):
else:
return self.change_view(request, unquote(url))
- def javascript(self, request, fieldsets):
- """
- Returns a list of URLs to include via <script> statements.
-
- The URLs can be absolute ('/js/admin/') or explicit
- ('http://example.com/foo.js').
- """
+ def _media(self):
from django.conf import settings
+
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
if self.prepopulated_fields:
js.append('js/urlify.js')
- if self.opts.has_field_type(models.DateTimeField) or self.opts.has_field_type(models.TimeField) or self.opts.has_field_type(models.DateField):
- js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
if self.opts.get_ordered_objects():
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
- if self.js:
- js.extend(self.js)
if self.filter_vertical or self.filter_horizontal:
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
- for fs in fieldsets:
- if 'collapse' in fs.classes:
- js.append('js/admin/CollapsedFieldsets.js')
- break
- prefix = settings.ADMIN_MEDIA_PREFIX
- return ['%s%s' % (prefix, url) for url in js]
-
- def javascript_add(self, request):
- return self.javascript(request, self.fieldsets_add(request))
-
- def javascript_change(self, request, obj):
- return self.javascript(request, self.fieldsets_change(request, obj))
-
+
+ return Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
+ media = property(_media)
+
def fieldsets(self, request):
"""
Generator that yields Fieldset objects for use on add and change admin
@@ -244,13 +241,11 @@ def fieldsets(self, request):
def fieldsets_add(self, request):
"Hook for specifying Fieldsets for the add form."
- for fs in self.fieldsets(request):
- yield fs
+ return list(self.fieldsets(request))
def fieldsets_change(self, request, obj):
"Hook for specifying Fieldsets for the change form."
- for fs in self.fieldsets(request):
- yield fs
+ return list(self.fieldsets(request))
def has_add_permission(self, request):
"Returns True if the given request has permission to add an object."
@@ -430,12 +425,17 @@ def add_view(self, request, form_url=''):
inline_formset = FormSet()
inline_formsets.append(inline_formset)
+ adminForm = AdminForm(form, list(self.fieldsets_add(request)), self.prepopulated_fields)
+ media = self.media + adminForm.media
+ for fs in inline_formsets:
+ media = media + fs.media
+
c = template.RequestContext(request, {
'title': _('Add %s') % opts.verbose_name,
- 'adminform': AdminForm(form, self.fieldsets_add(request), self.prepopulated_fields),
+ 'adminform': adminForm,
'is_popup': request.REQUEST.has_key('_popup'),
'show_delete': False,
- 'javascript_imports': self.javascript_add(request),
+ 'media': media,
'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)],
})
return render_change_form(self, model, model.AddManipulator(), c, add=True)
@@ -494,13 +494,19 @@ def change_view(self, request, object_id):
#related.get_accessor_name())
#orig_list = func()
#oldform.order_objects.extend(orig_list)
+
+ adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
+ media = self.media + adminForm.media
+ for fs in inline_formsets:
+ media = media + fs.media
+
c = template.RequestContext(request, {
'title': _('Change %s') % opts.verbose_name,
- 'adminform': AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields),
+ 'adminform': adminForm,
'object_id': object_id,
'original': obj,
'is_popup': request.REQUEST.has_key('_popup'),
- 'javascript_imports': self.javascript_change(request, obj),
+ 'media': media,
'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)],
})
return render_change_form(self, model, model.ChangeManipulator(object_id), c, change=True)
@@ -2,7 +2,6 @@
{% load i18n admin_modify adminmedia %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../../jsi18n/"></script>
-{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
{% endblock %}
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
@@ -3,8 +3,7 @@
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../jsi18n/"></script>
-{% for js in javascript_imports %}<script type="text/javascript" src="{{ js }}"></script>
-{% endfor %}
+{{ media }}
{% endblock %}
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
@@ -5,6 +5,7 @@
from django import newforms as forms
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
+from django.conf import settings
class FilteredSelectMultiple(forms.SelectMultiple):
"""
@@ -28,13 +29,28 @@ def render(self, name, value, attrs=None, choices=()):
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
return u''.join(output)
+class AdminDateWidget(forms.TextInput):
+ class Media:
+ js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
+ settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
+
+ def __init__(self, attrs={}):
+ super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
+
+class AdminTimeWidget(forms.TextInput):
+ class Media:
+ js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
+ settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
+
+ def __init__(self, attrs={}):
+ super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
+
class AdminSplitDateTime(forms.SplitDateTimeWidget):
"""
A SplitDateTime Widget that has some admin-specific styling.
"""
def __init__(self, attrs=None):
- widgets = [forms.TextInput(attrs={'class': 'vDateField', 'size': '10'}),
- forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})]
+ widgets = [AdminDateWidget, AdminTimeWidget]
# Note that we're calling MultiWidget, not SplitDateTimeWidget, because
# we want to define widgets.
forms.MultiWidget.__init__(self, widgets, attrs)
@@ -9,7 +9,7 @@
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
from fields import Field
-from widgets import TextInput, Textarea
+from widgets import Media, media_property, TextInput, Textarea
from util import flatatt, ErrorDict, ErrorList, ValidationError
__all__ = ('BaseForm', 'Form')
@@ -37,6 +37,7 @@ class DeclarativeFieldsMetaclass(type):
"""
Metaclass that converts Field attributes to a dictionary called
'base_fields', taking into account parent class 'base_fields' as well.
+ Also integrates any additional media definitions
"""
def __new__(cls, name, bases, attrs):
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
@@ -50,7 +51,11 @@ def __new__(cls, name, bases, attrs):
fields = base.base_fields.items() + fields
attrs['base_fields'] = SortedDictFromList(fields)
- return type.__new__(cls, name, bases, attrs)
+
+ new_class = type.__new__(cls, name, bases, attrs)
+ if 'media' not in attrs:
+ new_class.media = media_property(new_class)
+ return new_class
class BaseForm(StrAndUnicode):
# This is the main implementation of all the Form logic. Note that this
@@ -235,6 +240,16 @@ def reset(self):
self.is_bound = False
self.__errors = None
+ def _get_media(self):
+ """
+ Provide a description of all media required to render the widgets on this form
+ """
+ media = Media()
+ for field in self.fields.values():
+ media = media + field.widget.media
+ return media
+ media = property(_get_media)
+
class Form(BaseForm):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
@@ -1,6 +1,6 @@
from forms import Form, ValidationError
from fields import IntegerField, BooleanField
-from widgets import HiddenInput
+from widgets import HiddenInput, Media
# special field names
FORM_COUNT_FIELD_NAME = 'COUNT'
@@ -154,6 +154,15 @@ def is_valid(self):
self.full_clean()
return self._is_valid
+ def _get_media(self):
+ # All the forms on a FormSet are the same, so you only need to
+ # interrogate the first form for media.
+ if self.forms:
+ return self.forms[0].media
+ else:
+ return Media()
+ media = property(_get_media)
+
def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False):
"""Return a FormSet for the given form class."""
attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable}
Oops, something went wrong.

0 comments on commit 549198b

Please sign in to comment.