Permalink
Browse files

Merged the newforms-admin branch into trunk.

This is a backward incompatible change. The admin contrib app has been
refactored. The newforms module has several improvements including FormSets
and Media definitions.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7967 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent dc375fb commit a19ed8aea395e8e07164ff7d85bd7dff2f24edca @brosner brosner committed Jul 18, 2008
Showing with 8,050 additions and 2,680 deletions.
  1. +10 −0 AUTHORS
  2. +1 −1 django/__init__.py
  3. +7 −1 django/conf/project_template/urls.py
  4. +16 −0 django/contrib/admin/__init__.py
  5. +14 −14 django/contrib/admin/filterspecs.py
  6. +21 −0 django/contrib/admin/media/css/forms.css
  7. +0 −81 django/contrib/admin/media/js/SelectFilter.js
  8. +1 −1 django/contrib/admin/media/js/admin/CollapsedFieldsets.js
  9. +2 −2 django/contrib/admin/media/js/admin/RelatedObjectLookups.js
  10. +2 −1 django/contrib/admin/models.py
  11. +795 −0 django/contrib/admin/options.py
  12. +349 −0 django/contrib/admin/sites.py
  13. +9 −4 django/contrib/admin/templates/admin/auth/user/add_form.html
  14. +7 −6 django/contrib/admin/templates/admin/auth/user/change_password.html
  15. +1 −8 django/contrib/admin/templates/admin/base.html
  16. +39 −30 django/contrib/admin/templates/admin/change_form.html
  17. +17 −1 django/contrib/admin/templates/admin/change_list.html
  18. +2 −0 django/contrib/admin/templates/admin/delete_confirmation.html
  19. +26 −0 django/contrib/admin/templates/admin/edit_inline/stacked.html
  20. +64 −0 django/contrib/admin/templates/admin/edit_inline/tabular.html
  21. +0 −16 django/contrib/admin/templates/admin/edit_inline_stacked.html
  22. +0 −44 django/contrib/admin/templates/admin/edit_inline_tabular.html
  23. +0 −10 django/contrib/admin/templates/admin/field_line.html
  24. +0 −7 django/contrib/admin/templates/admin/filters.html
  25. +17 −0 django/contrib/admin/templates/admin/includes/fieldset.html
  26. +4 −3 django/contrib/admin/templates/admin/index.html
  27. +0 −2 django/contrib/admin/templates/admin/invalid_setup.html
  28. +3 −1 django/contrib/admin/templates/admin/login.html
  29. +1 −7 django/contrib/admin/templates/admin/object_history.html
  30. +1 −1 django/contrib/admin/templates/admin/search_form.html
  31. +1 −0 django/contrib/admin/templates/admin_doc/index.html
  32. +1 −0 django/contrib/admin/templates/admin_doc/view_index.html
  33. +1 −0 django/contrib/admin/templates/registration/password_change_done.html
  34. +1 −0 django/contrib/admin/templates/registration/password_change_form.html
  35. +1 −1 django/contrib/admin/templates/registration/password_reset_form.html
  36. +0 −5 django/contrib/admin/templates/widget/date_time.html
  37. +0 −1 django/contrib/admin/templates/widget/default.html
  38. +0 −4 django/contrib/admin/templates/widget/file.html
  39. +0 −20 django/contrib/admin/templates/widget/foreign.html
  40. +0 −1 django/contrib/admin/templates/widget/many_to_many.html
  41. +0 −2 django/contrib/admin/templates/widget/one_to_one.html
  42. +7 −11 django/contrib/admin/templatetags/admin_list.py
  43. +4 −236 django/contrib/admin/templatetags/admin_modify.py
  44. +0 −81 django/contrib/admin/templatetags/adminapplist.py
  45. +0 −43 django/contrib/admin/urls.py
  46. +139 −0 django/contrib/admin/util.py
  47. +280 −0 django/contrib/admin/validation.py
  48. +0 −78 django/contrib/admin/views/auth.py
  49. +0 −1 django/contrib/admin/views/decorators.py
  50. +40 −582 django/contrib/admin/views/main.py
  51. +215 −0 django/contrib/admin/widgets.py
  52. 0 {tests/regressiontests/invalid_admin_options → django/contrib/admindocs}/__init__.py
  53. +15 −0 django/contrib/admindocs/urls.py
  54. 0 django/contrib/{admin → admindocs}/utils.py
  55. +38 −14 django/contrib/{admin/views/doc.py → admindocs/views.py}
  56. +66 −0 django/contrib/auth/admin.py
  57. +140 −95 django/contrib/auth/forms.py
  58. +4 −21 django/contrib/auth/models.py
  59. +8 −0 django/contrib/auth/tests/__init__.py
  60. +5 −3 django/contrib/auth/{tests.py → tests/basic.py}
  61. +135 −0 django/contrib/auth/tests/forms.py
  62. +61 −30 django/contrib/auth/views.py
  63. +30 −0 django/contrib/comments/admin.py
  64. +2 −24 django/contrib/comments/models.py
  65. +50 −2 django/contrib/comments/views/comments.py
  66. +15 −0 django/contrib/flatpages/admin.py
  67. +1 −10 django/contrib/flatpages/models.py
  68. +17 −7 django/contrib/redirects/models.py
  69. +9 −0 django/contrib/sites/admin.py
  70. +2 −5 django/contrib/sites/models.py
  71. +0 −62 django/core/management/validation.py
  72. +1 −1 django/db/models/__init__.py
  73. +1 −4 django/db/models/base.py
  74. +11 −22 django/db/models/fields/__init__.py
  75. +21 −56 django/db/models/fields/related.py
  76. +1 −6 django/db/models/manipulators.py
  77. +0 −75 django/db/models/options.py
  78. +1 −0 django/newforms/__init__.py
  79. +48 −3 django/newforms/forms.py
  80. +292 −0 django/newforms/formsets.py
  81. +215 −7 django/newforms/models.py
  82. +168 −2 django/newforms/widgets.py
  83. +678 −0 docs/admin.txt
  84. +22 −22 docs/authentication.txt
  85. +0 −1 docs/custom_model_fields.txt
  86. +2 −2 docs/localflavor.txt
  87. +0 −478 docs/model-api.txt
  88. +122 −0 docs/modelforms.txt
  89. +640 −0 docs/newforms.txt
  90. +82 −81 docs/tutorial02.txt
  91. +1 −3 tests/modeltests/invalid_models/models.py
  92. +18 −0 tests/modeltests/model_forms/models.py
  93. 0 tests/modeltests/model_formsets/__init__.py
  94. +324 −0 tests/modeltests/model_formsets/models.py
  95. 0 tests/regressiontests/admin_ordering/__init__.py
  96. +46 −0 tests/regressiontests/admin_ordering/models.py
  97. 0 tests/regressiontests/admin_views/__init__.py
  98. +81 −0 tests/regressiontests/admin_views/fixtures/admin-views-users.xml
  99. +6 −0 tests/regressiontests/admin_views/fixtures/string-primary-key.xml
  100. +61 −0 tests/regressiontests/admin_views/models.py
  101. +362 −0 tests/regressiontests/admin_views/tests.py
  102. +7 −0 tests/regressiontests/admin_views/urls.py
  103. 0 tests/regressiontests/admin_widgets/__init__.py
  104. +85 −0 tests/regressiontests/admin_widgets/models.py
  105. +72 −0 tests/regressiontests/forms/forms.py
  106. +575 −0 tests/regressiontests/forms/formsets.py
  107. +359 −0 tests/regressiontests/forms/media.py
  108. +4 −0 tests/regressiontests/forms/tests.py
  109. +91 −0 tests/regressiontests/forms/widgets.py
  110. 0 tests/regressiontests/inline_formsets/__init__.py
  111. +55 −0 tests/regressiontests/inline_formsets/models.py
  112. +0 −337 tests/regressiontests/invalid_admin_options/models.py
  113. 0 tests/regressiontests/modeladmin/__init__.py
  114. +876 −0 tests/regressiontests/modeladmin/models.py
  115. +1 −0 tests/templates/custom_admin/change_form.html
  116. +7 −0 tests/templates/custom_admin/change_list.html
  117. +1 −0 tests/templates/custom_admin/delete_confirmation.html
  118. +6 −0 tests/templates/custom_admin/index.html
  119. +6 −0 tests/templates/custom_admin/login.html
  120. +1 −0 tests/templates/custom_admin/object_history.html
  121. +3 −0 tests/urls.py
View
@@ -74,6 +74,7 @@ answer newbie questions, and generally made Django that much better:
Arvis Bickovskis <viestards.lists@gmail.com>
Paul Bissex <http://e-scribe.com/>
Simon Blanchard
+ David Blewett <david@dawninglight.net>
Matt Boersma <ogghead@gmail.com>
boobsd@gmail.com
Andrew Brehaut <http://brehaut.net/blog>
@@ -172,6 +173,7 @@ answer newbie questions, and generally made Django that much better:
Espen Grindhaug <http://grindhaug.org/>
Thomas Güttler <hv@tbz-pariv.de>
dAniel hAhler
+ hambaloney
Brian Harring <ferringb@gmail.com>
Brant Harris
Hawkeye
@@ -194,6 +196,7 @@ answer newbie questions, and generally made Django that much better:
Baurzhan Ismagulov <ibr@radix50.net>
james_027@yahoo.com
jcrasta@gmail.com
+ jdetaeye
Zak Johnson <zakj@nox.cx>
Nis Jørgensen <nis@superlativ.dk>
Michael Josephson <http://www.sdjournal.com/>
@@ -241,11 +244,13 @@ answer newbie questions, and generally made Django that much better:
Waylan Limberg <waylan@gmail.com>
limodou
Philip Lindborg <philip.lindborg@gmail.com>
+ Simon Litchfield <simon@quo.com.au>
Daniel Lindsley <polarcowz@gmail.com>
Trey Long <trey@ktrl.com>
msaelices <msaelices@gmail.com>
Matt McClanahan <http://mmcc.cx/>
Martin Maney <http://www.chipy.org/Martin_Maney>
+ Petr Marhoun <petr.marhoun@gmail.com>
masonsimon+django@gmail.com
Manuzhai
Petr Marhoun <petr.marhoun@gmail.com>
@@ -258,6 +263,7 @@ answer newbie questions, and generally made Django that much better:
mattycakes@gmail.com
Jason McBrayer <http://www.carcosa.net/jason/>
mccutchen@gmail.com
+ Christian Metts
michael.mcewan@gmail.com
michal@plovarna.cz
Slawek Mikula <slawek dot mikula at gmail dot com>
@@ -270,6 +276,7 @@ answer newbie questions, and generally made Django that much better:
Eric Moritz <http://eric.themoritzfamily.com/>
mrmachine <real.human@mrmachine.net>
Robin Munn <http://www.geekforgod.com/>
+ msundstr
Robert Myers <myer0052@gmail.com>
Nebojša Dorđević
Doug Napoleone <doug@dougma.com>
@@ -290,6 +297,7 @@ answer newbie questions, and generally made Django that much better:
peter@mymart.com
pgross@thoughtworks.com
phaedo <http://phaedo.cx/>
+ Julien Phalip <http://www.julienphalip.com>
phil@produxion.net
phil.h.smith@gmail.com
Gustavo Picon
@@ -298,6 +306,7 @@ answer newbie questions, and generally made Django that much better:
Mihai Preda <mihai_preda@yahoo.com>
Daniel Poelzleithner <http://poelzi.org/>
polpak@yahoo.com
+ Matthias Pronk <django@masida.nl>
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
Johann Queuniet <johann.queuniet@adh.naellia.eu>
Jan Rademaker
@@ -314,6 +323,7 @@ answer newbie questions, and generally made Django that much better:
Matt Riggott
Henrique Romano <onaiort@gmail.com>
Armin Ronacher
+ Daniel Roseman <http://roseman.org.uk/>
Brian Rosner <brosner@gmail.com>
Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno
View
@@ -1,4 +1,4 @@
-VERSION = (0, 97, 'pre')
+VERSION = (0, 97, 'newforms-admin')
def get_version():
"Returns the version as a human-format string."
@@ -1,9 +1,15 @@
from django.conf.urls.defaults import *
+# Uncomment this for admin:
+#from django.contrib import admin
+
urlpatterns = patterns('',
# Example:
# (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
+ # Uncomment this for admin docs:
+ #(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
# Uncomment this for admin:
-# (r'^admin/', include('django.contrib.admin.urls')),
+ #('^admin/(.*)', admin.site.root),
)
@@ -0,0 +1,16 @@
+from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
+from django.contrib.admin.options import StackedInline, TabularInline
+from django.contrib.admin.sites import AdminSite, site
+
+def autodiscover():
+ """
+ Auto-discover INSTALLED_APPS admin.py modules and fail silently when
+ not present. This forces an import on them to register any admin bits they
+ may want.
+ """
+ from django.conf import settings
+ for app in settings.INSTALLED_APPS:
+ try:
+ __import__("%s.admin" % app)
+ except ImportError:
+ pass
@@ -15,18 +15,18 @@
class FilterSpec(object):
filter_specs = []
- def __init__(self, f, request, params, model):
+ def __init__(self, f, request, params, model, model_admin):
self.field = f
self.params = params
def register(cls, test, factory):
cls.filter_specs.append((test, factory))
register = classmethod(register)
- def create(cls, f, request, params, model):
+ def create(cls, f, request, params, model, model_admin):
for test, factory in cls.filter_specs:
if test(f):
- return factory(f, request, params, model)
+ return factory(f, request, params, model, model_admin)
create = classmethod(create)
def has_output(self):
@@ -52,8 +52,8 @@ def output(self, cl):
return mark_safe("".join(t))
class RelatedFilterSpec(FilterSpec):
- def __init__(self, f, request, params, model):
- super(RelatedFilterSpec, self).__init__(f, request, params, model)
+ def __init__(self, f, request, params, model, model_admin):
+ super(RelatedFilterSpec, self).__init__(f, request, params, model, model_admin)
if isinstance(f, models.ManyToManyField):
self.lookup_title = f.rel.to._meta.verbose_name
else:
@@ -81,8 +81,8 @@ def choices(self, cl):
FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
class ChoicesFilterSpec(FilterSpec):
- def __init__(self, f, request, params, model):
- super(ChoicesFilterSpec, self).__init__(f, request, params, model)
+ def __init__(self, f, request, params, model, model_admin):
+ super(ChoicesFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_kwarg = '%s__exact' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@@ -98,8 +98,8 @@ def choices(self, cl):
FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
class DateFieldFilterSpec(FilterSpec):
- def __init__(self, f, request, params, model):
- super(DateFieldFilterSpec, self).__init__(f, request, params, model)
+ def __init__(self, f, request, params, model, model_admin):
+ super(DateFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
self.field_generic = '%s__' % self.field.name
@@ -133,8 +133,8 @@ def choices(self, cl):
FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
class BooleanFieldFilterSpec(FilterSpec):
- def __init__(self, f, request, params, model):
- super(BooleanFieldFilterSpec, self).__init__(f, request, params, model)
+ def __init__(self, f, request, params, model, model_admin):
+ super(BooleanFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_kwarg = '%s__exact' % f.name
self.lookup_kwarg2 = '%s__isnull' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@@ -159,10 +159,10 @@ def choices(self, cl):
# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much
# more appropriate, and the AllValuesFilterSpec won't get used for it.
class AllValuesFilterSpec(FilterSpec):
- def __init__(self, f, request, params, model):
- super(AllValuesFilterSpec, self).__init__(f, request, params, model)
+ def __init__(self, f, request, params, model, model_admin):
+ super(AllValuesFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_val = request.GET.get(f.name, None)
- self.lookup_choices = model._meta.admin.manager.distinct().order_by(f.name).values(f.name)
+ self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name)
def title(self):
return self.field.verbose_name
@@ -58,3 +58,24 @@ fieldset.monospace textarea { font-family:"Bitstream Vera Sans Mono",Monaco,"Cou
.vLargeTextField, .vXMLLargeTextField { width:48em; }
.flatpages-flatpage #id_content { height:40.2em; }
.module table .vPositiveSmallIntegerField { width:2.2em; }
+
+/* x unsorted */
+.inline-group {padding:10px; padding-bottom:5px; background:#eee; margin:10px 0;}
+.inline-group h3.header {margin:-5px -10px 5px -10px; background:#bbb; color:#fff; padding:2px 5px 3px 5px; font-size:11px}
+.inline-related {margin-bottom:15px; position:relative;}
+.last-related {margin-bottom:0px;}
+.inline-related h2 { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; font-weight:bold; color:#888; }
+.inline-related h2 b {font-weight:normal; color:#aaa;}
+.inline-related h2 span.delete {padding-left:20px; position:absolute; top:0px; right:5px;}
+.inline-related h2 span.delete label {margin-left:2px; padding-top:1px;}
+.inline-related fieldset {background:#fbfbfb;}
+.inline-related fieldset.module h2 { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; font-weight:bold; background:#bcd; color:#fff; }
+.inline-related.tabular fieldset.module table {width:100%;}
+
+.inline-group .tabular tr.has_original td {padding-top:2em;}
+.inline-group .tabular tr td.original { padding:2px 0 0 0; width:0; _position:relative; }
+.inline-group .tabular th.original {width:0px; padding:0;}
+.inline-group .tabular td.original p {position:absolute; left:0; height:1.1em; padding:2px 7px; overflow:hidden; font-size:9px; font-weight:bold; color:#666; _width:700px; }
+.inline-group ul.tools {padding:0; margin: 0; list-style:none;}
+.inline-group ul.tools li {display:inline; padding:0 5px;}
+.inline-group ul.tools a.add {background:url(../img/admin/icon_addlink.gif) 0 50% no-repeat; padding-left:14px;}
@@ -1,81 +0,0 @@
-/*
-SelectFilter - Turns a multiple-select box into a filter interface.
-
-Requires SelectBox.js and addevent.js.
-*/
-
-function findForm(node) {
- // returns the node of the form containing the given node
- if (node.tagName.toLowerCase() != 'form') {
- return findForm(node.parentNode);
- }
- return node;
-}
-
-var SelectFilter = {
- init: function(field_id) {
- var from_box = document.getElementById(field_id);
- from_box.id += '_from'; // change its ID
- // Create the INPUT input box
- var input_box = document.createElement('input');
- input_box.id = field_id + '_input';
- input_box.setAttribute('type', 'text');
- from_box.parentNode.insertBefore(input_box, from_box);
- from_box.parentNode.insertBefore(document.createElement('br'), input_box.nextSibling);
- // Create the TO box
- var to_box = document.createElement('select');
- to_box.id = field_id + '_to';
- to_box.setAttribute('multiple', 'multiple');
- to_box.setAttribute('size', from_box.size);
- from_box.parentNode.insertBefore(to_box, from_box.nextSibling);
- to_box.setAttribute('name', from_box.getAttribute('name'));
- from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
- // Give the filters a CSS hook
- from_box.setAttribute('class', 'filtered');
- to_box.setAttribute('class', 'filtered');
- // Set up the JavaScript event handlers for the select box filter interface
- addEvent(input_box, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
- addEvent(input_box, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
- addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
- addEvent(from_box, 'focus', function() { input_box.focus(); });
- addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
- addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
- SelectBox.init(field_id + '_from');
- SelectBox.init(field_id + '_to');
- // Move selected from_box options to to_box
- SelectBox.move(field_id + '_from', field_id + '_to');
- },
- filter_key_up: function(event, field_id) {
- from = document.getElementById(field_id + '_from');
- // don't submit form if user pressed Enter
- if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
- from.selectedIndex = 0;
- SelectBox.move(field_id + '_from', field_id + '_to');
- from.selectedIndex = 0;
- return false;
- }
- var temp = from.selectedIndex;
- SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
- from.selectedIndex = temp;
- return true;
- },
- filter_key_down: function(event, field_id) {
- from = document.getElementById(field_id + '_from');
- // right arrow -- move across
- if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
- var old_index = from.selectedIndex;
- SelectBox.move(field_id + '_from', field_id + '_to');
- from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
- return false;
- }
- // down arrow -- wrap around
- if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
- from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
- }
- // up arrow -- wrap around
- if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
- from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
- }
- return true;
- }
-}
@@ -47,7 +47,7 @@ var CollapsedFieldsets = {
// Returns true if any fields in the fieldset have validation errors.
var divs = fs.getElementsByTagName('div');
for (var i=0; i<divs.length; i++) {
- if (divs[i].className.match(/\berror\b/)) {
+ if (divs[i].className.match(/\berrors\b/)) {
return true;
}
}
@@ -1,4 +1,4 @@
-// Handles related-objects functionality: lookup link for raw_id_admin=True
+// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.
function html_unescape(text) {
@@ -29,7 +29,7 @@ function showRelatedObjectLookupPopup(triggeringLink) {
function dismissRelatedLookupPopup(win, chosenId) {
var name = win.name.replace(/___/g, '.');
var elem = document.getElementById(name);
- if (elem.className.indexOf('vRawIdAdminField') != -1 && elem.value) {
+ if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
@@ -1,6 +1,7 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
+from django.contrib.admin.util import quote
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
from django.utils.safestring import mark_safe
@@ -50,4 +51,4 @@ def get_admin_url(self):
Returns the admin URL to edit the object represented by this log entry.
This is relative to the Django admin index page.
"""
- return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id))
+ return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id)))
Oops, something went wrong.

0 comments on commit a19ed8a

Please sign in to comment.