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

boulder-oracle-sprint: Merged to trunk [4276]

git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4279 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Boulder Sprinters 2007-01-03 20:46:26 +00:00
parent 499bfc1f4a
commit 92b7851424
28 changed files with 776 additions and 37 deletions

View File

@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
Clint Ecker
Enrico <rico.bl@gmail.com>
favo@exoweb.net
Eric Floehr <eric@intellovations.com>
gandalf@owca.info
Baishampayan Ghose
martin.glueck@gmail.com
@ -127,6 +128,7 @@ answer newbie questions, and generally made Django that much better:
mmarshall
Eric Moritz <http://eric.themoritzfamily.com/>
Robin Munn <http://www.geekforgod.com/>
Robert Myers <myer0052@gmail.com>
Nebojša Dorđević
Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/>
@ -151,6 +153,7 @@ answer newbie questions, and generally made Django that much better:
serbaut@gmail.com
Pete Shinners <pete@shinners.org>
SmileyChris <smileychris@gmail.com>
smurf@smurf.noris.de
sopel
Thomas Steinacher <tom@eggdrop.ch>
nowell strite

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
"""
Daily cleanup job.

View File

@ -0,0 +1,52 @@
{% extends "admin/base_site.html" %}
{% 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 %}
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}{% if not is_popup %}
<div class="breadcrumbs">
<a href="../../../../">{% trans "Home" %}</a> &rsaquo;
<a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
<a href="../">{{ original|truncatewords:"18"|escape }}</a> &rsaquo;
{% trans 'Change password' %}
</div>
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if form.error_dict %}
<p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{% endif %}
<p>{% blocktrans with original.username|escape as username %}Enter a new username and password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
<fieldset class="module aligned">
<div class="form-row">
{{ form.password1.html_error_list }}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div>
<div class="form-row">
{{ form.password2.html_error_list }}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div>
</fieldset>
<div class="submit-row">
<input type="submit" value="{% trans 'Change password' %}" class="default" />
</div>
<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script>
</div>
</form></div>
{% endblock %}

View File

@ -29,6 +29,8 @@ urlpatterns = patterns('',
# "Add user" -- a special-case view
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
# "Change user password" -- another special-case view
('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'),
# Add/change/delete/history
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),

View File

@ -1,10 +1,11 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django import oldforms, template
from django.shortcuts import render_to_response
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django.utils.html import escape
def user_add_stage(request):
if not request.user.has_perm('auth.change_user'):
@ -42,3 +43,35 @@ def user_add_stage(request):
'username_help_text': User._meta.get_field('username').help_text,
}, context_instance=template.RequestContext(request))
user_add_stage = staff_member_required(user_add_stage)
def user_change_password(request, id):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
user = get_object_or_404(User, pk=id)
manipulator = AdminPasswordChangeForm(user)
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('..')
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/change_password.html', {
'title': _('Change password: %s') % escape(user.username),
'form': form,
'is_popup': request.REQUEST.has_key('_popup'),
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_absolute_url': False,
'first_form_field_id': 'id_password1',
'opts': User._meta,
'original': user,
'show_save': True,
}, context_instance=template.RequestContext(request))
user_change_password = staff_member_required(user_change_password)

View File

@ -46,8 +46,8 @@ def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
quoting is slightly different so that it doesn't get autoamtically
unquoted by the web browser.
quoting is slightly different so that it doesn't get automatically
unquoted by the Web browser.
"""
if type(s) != type(''):
return s

View File

@ -126,3 +126,18 @@ class PasswordChangeForm(oldforms.Manipulator):
"Saves the new password."
self.user.set_password(new_data['new_password1'])
self.user.save()
class AdminPasswordChangeForm(oldforms.Manipulator):
"A form used to change the password of a user in the admin interface."
def __init__(self, user):
self.user = user
self.fields = (
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
def save(self, new_data):
"Saves the new password."
self.user.set_password(new_data['password1'])
self.user.save()

View File

@ -91,7 +91,7 @@ class User(models.Model):
first_name = models.CharField(_('first name'), maxlength=30, blank=True)
last_name = models.CharField(_('last name'), maxlength=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]'"))
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))

View File

@ -48,6 +48,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.shortcuts import render_to_response
from django.template.context import RequestContext
import cPickle as pickle
import md5
@ -91,7 +92,9 @@ class FormPreview(object):
def preview_get(self, request):
"Displays the form"
f = self.form(auto_id=AUTO_ID)
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
return render_to_response(self.form_template,
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
context_instance=RequestContext(request))
def preview_post(self, request):
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
@ -100,9 +103,9 @@ class FormPreview(object):
if f.is_valid():
context['hash_field'] = self.unused_name('hash')
context['hash_value'] = self.security_hash(request, f)
return render_to_response(self.preview_template, context)
return render_to_response(self.preview_template, context, context_instance=RequestContext(request))
else:
return render_to_response(self.form_template, context)
return render_to_response(self.form_template, context, context_instance=RequestContext(request))
def post_post(self, request):
"Validates the POST data. If valid, calls done(). Else, redisplays form."
@ -112,7 +115,9 @@ class FormPreview(object):
return self.failed_hash(request) # Security hash failed.
return self.done(request, f.clean_data)
else:
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
return render_to_response(self.form_template,
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
context_instance=RequestContext(request))
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################

View File

@ -98,9 +98,11 @@ class DatabaseWrapper(local):
kwargs['port'] = int(settings.DATABASE_PORT)
kwargs.update(self.options)
self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES 'utf8'")
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES 'utf8'")
else:
cursor = self.connection.cursor()
if settings.DEBUG:
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
return cursor

View File

@ -43,7 +43,7 @@ class UnicodeCursorWrapper(object):
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
def executemany(self, sql, param_list):
new_param_list = [[smart_basestring(p, self.charset) for p in params] for params in param_list]
new_param_list = [tuple([smart_basestring(p, self.charset) for p in params]) for params in param_list]
return self.cursor.executemany(sql, new_param_list)
def __getattr__(self, attr):

View File

@ -729,6 +729,10 @@ class ManyToManyField(RelatedField, Field):
return getattr(obj, self.attname).all()
def formfield(self, initial=None):
# If initial is passed in, it's a list of related objects, but the
# MultipleChoiceField takes a list of IDs.
if initial is not None:
initial = [i._get_pk_val() for i in initial]
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class ManyToOneRel(object):

View File

@ -4,20 +4,29 @@
from django.template import loader
from django.http import HttpResponse, Http404
from django.db.models.manager import Manager
def render_to_response(*args, **kwargs):
return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility.
def get_object_or_404(klass, *args, **kwargs):
if isinstance(klass, Manager):
manager = klass
klass = manager.model
else:
manager = klass._default_manager
try:
return klass._default_manager.get(*args, **kwargs)
return manager.get(*args, **kwargs)
except klass.DoesNotExist:
raise Http404
def get_list_or_404(klass, *args, **kwargs):
obj_list = list(klass._default_manager.filter(*args, **kwargs))
if isinstance(klass, Manager):
manager = klass
else:
manager = klass._default_manager
obj_list = list(manager.filter(*args, **kwargs))
if not obj_list:
raise Http404
return obj_list

View File

@ -27,20 +27,38 @@ def fix_ampersands(value):
from django.utils.html import fix_ampersands
return fix_ampersands(value)
def floatformat(text):
def floatformat(text, arg=-1):
"""
Displays a floating point number as 34.2 (with one decimal place) -- but
only if there's a point to be displayed
If called without an argument, displays a floating point
number as 34.2 -- but only if there's a point to be displayed.
With a positive numeric argument, it displays that many decimal places
always.
With a negative numeric argument, it will display that many decimal
places -- but only if there's places to be displayed.
Examples:
num1 = 34.23234
num2 = 34.00000
num1|floatformat results in 34.2
num2|floatformat is 34
num1|floatformat:3 is 34.232
num2|floatformat:3 is 34.000
num1|floatformat:-3 is 34.232
num2|floatformat:-3 is 34
"""
try:
f = float(text)
except ValueError:
return ''
try:
d = int(arg)
except ValueError:
return str(f)
m = f - int(f)
if m:
return '%.1f' % f
else:
if not m and d < 0:
return '%d' % int(f)
else:
formatstr = '%%.%df' % abs(d)
return formatstr % f
def linenumbers(value):
"Displays text with line numbers"

View File

@ -1704,6 +1704,46 @@ For every ``ImageField``, the object will have ``get_FOO_height()`` and
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
returns the height (or width) of the image, as an integer, in pixels.
Shortcuts
=========
As you develop views, you will discover a number of common idioms in the
way you use the database API. Django encodes some of these idioms as
shortcuts that can be used to simplify the process of writing views.
get_object_or_404()
-------------------
One common idiom to use ``get()`` and raise ``Http404`` if the
object doesn't exist. This idiom is captured by ``get_object_or_404()``.
This function takes a Django model as its first argument and an
arbitrary number of keyword arguments, which it passes to the manager's
``get()`` function. It raises ``Http404`` if the object doesn't
exist. For example::
# Get the Entry with a primary key of 3
e = get_object_or_404(Entry, pk=3)
When you provide a model to this shortcut function, the default manager
is used to execute the underlying ``get()`` query. If you don't want to
use the default manager, or you want to search a list of related objects,
you can provide ``get_object_or_404()`` with a manager object, instead.
For example::
# Get the author of blog instance `e` with a name of 'Fred'
a = get_object_or_404(e.authors, name='Fred')
# Use a custom manager 'recent_entries' in the search for an
# entry with a primary key of 3
e = get_object_or_404(Entry.recent_entries, pk=3)
get_list_or_404()
-----------------
``get_list_or_404`` behaves the same was as ``get_object_or_404()``
-- except the it uses using ``filter()`` instead of ``get()``. It raises
``Http404`` if the list is empty.
Falling back to raw SQL
=======================

View File

@ -94,7 +94,7 @@ Django places only two restrictions on model field names:
the way Django's query lookup syntax works. For example::
class Example(models.Model):
foo__bar = models.IntegerField() 'foo__bar' has two underscores!
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
These limitations can be worked around, though, because your field name doesn't
necessarily have to match your database column name. See `db_column`_ below.

View File

@ -72,6 +72,10 @@ The library deals with these concepts:
* **Form** -- A collection of fields that knows how to validate itself and
display itself as HTML.
The library is decoupled from the other Django components, such as the database
layer, views and templates. It relies only on Django settings, a couple of
``django.utils`` helper functions and Django's internationalization system.
Form objects
============
@ -282,12 +286,41 @@ example, in the ``ContactForm`` example, the fields are defined in the order
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
output, just change the order in which those fields are listed in the class.
Using forms to validate data
----------------------------
More granular output
~~~~~~~~~~~~~~~~~~~~
In addition to HTML form display, a ``Form`` class is responsible for
validating data.
The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for
lazy developers -- they're not the only way a form object can be displayed.
To display the HTML for a single field in your form, use dictionary lookup
syntax using the field's name as the key, and print the resulting object::
>>> f = ContactForm()
>>> print f['subject']
<input id="id_subject" type="text" name="subject" maxlength="100" />
>>> print f['message']
<input type="text" name="message" id="id_message" />
>>> print f['sender']
<input type="text" name="sender" id="id_sender" />
>>> print f['cc_myself']
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a
string or Unicode object, respectively::
>>> str(f['subject'])
'<input id="id_subject" type="text" name="subject" maxlength="100" />'
>>> unicode(f['subject'])
u'<input id="id_subject" type="text" name="subject" maxlength="100" />'
The field-specific output honors the form object's ``auto_id`` setting::
>>> f = ContactForm(auto_id=False)
>>> print f['message']
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print f['message']
<input type="text" name="message" id="id_message" />
More coming soon
================
@ -297,6 +330,9 @@ http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/t
-- the unit tests for ``django.newforms``. This can give you a good idea of
what's possible.
If you're really itching to learn and use this library, please be patient.
We're working hard on finishing both the code and documentation.
Using forms with templates
==========================

View File

@ -217,6 +217,23 @@ browser-length cookies -- cookies that expire as soon as the user closes his or
her browser. Use this if you want people to have to log in every time they open
a browser.
Clearing the session table
==========================
Note that session data can accumulate in the ``django_session`` database table
and Django does *not* provide automatic purging. Therefore, it's your job to
purge expired sessions on a regular basis.
To understand this problem, consider what happens when a user uses a session.
When a user logs in, Django adds a row to the ``django_session`` database
table. Django updates this row each time the session data changes. If the user
logs out manually, Django deletes the row. But if the user does *not* log out,
the row never gets deleted.
Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``.
That script deletes any session in the session table whose ``expire_date`` is
in the past -- but your application may have different requirements.
Settings
========

View File

@ -924,13 +924,31 @@ Replaces ampersands with ``&amp;`` entities.
floatformat
~~~~~~~~~~~
Rounds a floating-point number to one decimal place -- but only if there's a
decimal part to be displayed. For example:
When used without an argument, rounds a floating-point number to one decimal
place -- but only if there's a decimal part to be displayed. For example:
* ``36.123`` gets converted to ``36.1``
* ``36.15`` gets converted to ``36.2``
* ``36`` gets converted to ``36``
**New in Django development version**
If used with a numeric integer argument, ``floatformat`` rounds a number to that
many decimal places. For example:
* ``36.1234`` with floatformat:3 gets converted to ``36.123``
* ``36`` with floatformat:4 gets converted to ``36.0000``
If the argument passed to ``floatformat`` is negative, it will round a number to
that many decimal places -- but only if there's a decimal part to be displayed.
For example:
* ``36.1234`` with floatformat:-3 gets converted to ``36.123``
* ``36`` with floatformat:-4 gets converted to ``36``
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with
an argument of ``-1``.
get_digit
~~~~~~~~~

View File

@ -300,7 +300,7 @@ rewritten::
The ``get_object_or_404()`` function takes a Django model module as its first
argument and an arbitrary number of keyword arguments, which it passes to the
module's ``get_object()`` function. It raises ``Http404`` if the object doesn't
module's ``get()`` function. It raises ``Http404`` if the object doesn't
exist.
.. admonition:: Philosophy

View File

@ -1,6 +1,7 @@
from distutils.core import setup
from distutils.command.install import INSTALL_SCHEMES
import os
import sys
# Tell distutils to put the data_files in platform-specific installation
# locations. See here for an explanation:
@ -23,7 +24,13 @@ for dirpath, dirnames, filenames in os.walk(django_dir):
package = dirpath[len_root_dir:].lstrip('/').replace('/', '.')
packages.append(package)
else:
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))
data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
# Small hack for working with bdist_wininst.
# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
if sys.argv[1] == 'bdist_wininst':
for file_info in data_files:
file_info[0] = '/PURELIB/%s' % file_info[0]
# Dynamically calculate the version based on django.VERSION.
version = "%d.%d-%s" % (__import__('django').VERSION)

View File

@ -0,0 +1,86 @@
"""
34. DB-API Shortcuts
get_object_or_404 is a shortcut function to be used in view functions for
performing a get() lookup and raising a Http404 exception if a DoesNotExist
exception was rasied during the get() call.
get_list_or_404 is a shortcut function to be used in view functions for
performing a filter() lookup and raising a Http404 exception if a DoesNotExist
exception was rasied during the filter() call.
"""
from django.db import models
from django.http import Http404
from django.shortcuts import get_object_or_404, get_list_or_404
class Author(models.Model):
name = models.CharField(maxlength=50)
def __str__(self):
return self.name
class ArticleManager(models.Manager):
def get_query_set(self):
return super(ArticleManager, self).get_query_set().filter(authors__name__icontains='sir')
class Article(models.Model):
authors = models.ManyToManyField(Author)
title = models.CharField(maxlength=50)
objects = models.Manager()
by_a_sir = ArticleManager()
def __str__(self):
return self.title
__test__ = {'API_TESTS':"""
# Create some Authors.
>>> a = Author.objects.create(name="Brave Sir Robin")
>>> a.save()
>>> a2 = Author.objects.create(name="Patsy")
>>> a2.save()
# No Articles yet, so we should get a Http404 error.
>>> get_object_or_404(Article, title="Foo")
Traceback (most recent call last):
...
Http404
# Create an Article.
>>> article = Article.objects.create(title="Run away!")
>>> article.authors = [a, a2]
>>> article.save()
# get_object_or_404 can be passed a Model to query.
>>> get_object_or_404(Article, title__contains="Run")
<Article: Run away!>
# We can also use the the Article manager through an Author object.
>>> get_object_or_404(a.article_set, title__contains="Run")
<Article: Run away!>
# No articles containing "Camelot". This should raise a Http404 error.
>>> get_object_or_404(a.article_set, title__contains="Camelot")
Traceback (most recent call last):
...
Http404
# Custom managers can be used too.
>>> get_object_or_404(Article.by_a_sir, title="Run away!")
<Article: Run away!>
# get_list_or_404 can be used to get lists of objects
>>> get_list_or_404(a.article_set, title__icontains='Run')
[<Article: Run away!>]
# Http404 is returned if the list is empty
>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
Traceback (most recent call last):
...
Http404
# Custom managers can be used too.
>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
[<Article: Run away!>]
"""}

View File

@ -1,7 +1,8 @@
"""
34. Generating HTML forms from models
Django provides shortcuts for creating Form objects from a model class.
Django provides shortcuts for creating Form objects from a model class and a
model instance.
The function django.newforms.form_for_model() takes a model class and returns
a Form that is tied to the model. This Form works just like any other Form,
@ -9,6 +10,13 @@ with one additional method: create(). The create() method creates an instance
of the model and returns that newly created instance. It saves the instance to
the database if create(save=True), which is default. If you pass
create(save=False), then you'll get the object without saving it.
The function django.newforms.form_for_instance() takes a model instance and
returns a Form that is tied to the instance. This form works just like any
other Form, with one additional method: apply_changes(). The apply_changes()
method updates the model instance. It saves the changes to the database if
apply_changes(save=True), which is default. If you pass save=False, then you'll
get the object without saving it.
"""
from django.db import models
@ -28,7 +36,7 @@ class Writer(models.Model):
class Article(models.Model):
headline = models.CharField(maxlength=50)
pub_date = models.DateTimeField()
pub_date = models.DateField()
writer = models.ForeignKey(Writer)
categories = models.ManyToManyField(Category, blank=True)
@ -80,6 +88,8 @@ __test__ = {'API_TESTS': """
>>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>]
If you call create() with save=False, then it will return an object that hasn't
yet been saved. In this case, it's up to you to save it.
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
>>> f.errors
{}
@ -94,6 +104,7 @@ __test__ = {'API_TESTS': """
>>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
If you call create() with invalid data, you'll get a ValueError.
>>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.errors
{'name': [u'This field is required.']}
@ -102,7 +113,6 @@ __test__ = {'API_TESTS': """
Traceback (most recent call last):
...
ValueError: The Category could not be created because the data didn't validate.
>>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.create()
Traceback (most recent call last):
@ -181,4 +191,27 @@ True
>>> new_art = Article.objects.get(id=1)
>>> new_art.headline
'New headline'
Add some categories and test the many-to-many form output.
>>> new_art.categories.all()
[]
>>> new_art.categories.add(Category.objects.get(name='Entertainment'))
>>> new_art.categories.all()
[<Category: Entertainment>]
>>> TestArticleForm = form_for_instance(new_art)
>>> f = TestArticleForm(auto_id=False)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
<li>Writer: <select name="writer">
<option value="">---------</option>
<option value="1" selected="selected">Mike Royko</option>
<option value="2">Bob Woodward</option>
</select></li>
<li>Categories: <select multiple="multiple" name="categories">
<option value="1" selected="selected">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
</select></li>
"""}

View File

@ -11,6 +11,26 @@ r"""
'0.0'
>>> floatformat(0.0)
'0'
>>> floatformat(7.7,3)
'7.700'
>>> floatformat(6.000000,3)
'6.000'
>>> floatformat(13.1031,-3)
'13.103'
>>> floatformat(11.1197, -2)
'11.12'
>>> floatformat(11.0000, -2)
'11'
>>> floatformat(11.000001, -2)
'11.00'
>>> floatformat(8.2798, 3)
'8.280'
>>> floatformat('foo')
''
>>> floatformat(13.1031, 'bar')
'13.1031'
>>> floatformat('foo', 'bar')
''
>>> addslashes('"double quotes" and \'single quotes\'')
'\\"double quotes\\" and \\\'single quotes\\\''

View File

@ -2482,7 +2482,7 @@ demonstrate some of the library's abilities.
# SelectDateWidget ############################################################
>>> from django.newforms.extras import SelectDateWidget
>>> w = SelectDateWidget()
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
>>> print w.render('mydate', '')
<select name="mydate_month">
<option value="1">January</option>
@ -2532,7 +2532,6 @@ demonstrate some of the library's abilities.
<option value="31">31</option>
</select>
<select name="mydate_year">
<option value="2006">2006</option>
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@ -2542,6 +2541,7 @@ demonstrate some of the library's abilities.
<option value="2013">2013</option>
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
>>> w.render('mydate', None) == w.render('mydate', '')
True
@ -2594,7 +2594,6 @@ True
<option value="31">31</option>
</select>
<select name="mydate_year">
<option value="2006">2006</option>
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@ -2604,6 +2603,7 @@ True
<option value="2013">2013</option>
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
"""

View File

@ -0,0 +1,337 @@
"""
Admin options
Test invalid and valid admin options to make sure that
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)
class Admin:
list_display = 'first_name'
model_errors += """invalid_admin_options.listdisplaybadone: "admin.list_display", if given, must be set to a list or tuple.
"""
class ListDisplayBadTwo(models.Model):
"Test list_display, list_display items must be attributes, methods or properties."
first_name = models.CharField(maxlength=30)
class Admin:
list_display = ['first_name','nonexistent']
model_errors += """invalid_admin_options.listdisplaybadtwo: "admin.list_display" refers to 'nonexistent', which isn't an attribute, method or property.
"""
class ListDisplayBadThree(models.Model):
"Test list_display, list_display items can not be a ManyToManyField."
first_name = models.CharField(maxlength=30)
nick_names = models.ManyToManyField('ListDisplayGood')
class Admin:
list_display = ['first_name','nick_names']
model_errors += """invalid_admin_options.listdisplaybadthree: "admin.list_display" doesn't support ManyToManyFields ('nick_names').
"""
class ListDisplayGood(models.Model):
"Test list_display, Admin list_display can be a attribute, method or property."
first_name = models.CharField(maxlength=30)
def _last_name(self):
return self.first_name
last_name = property(_last_name)
def full_name(self):
return "%s %s" % (self.first_name, self.last_name)
class Admin:
list_display = ['first_name','last_name','full_name']
class ListDisplayLinksBadOne(models.Model):
"Test list_display_links, item must be included in list_display."
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
list_display = ['last_name']
list_display_links = ['first_name']
model_errors += """invalid_admin_options.listdisplaylinksbadone: "admin.list_display_links" refers to 'first_name', which is not defined in "admin.list_display".
"""
class ListDisplayLinksBadTwo(models.Model):
"Test list_display_links, must be a list or tuple."
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
list_display = ['first_name','last_name']
list_display_links = 'last_name'
model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_display_links", if given, must be set to a list or tuple.
"""
# TODO: Fix list_display_links validation or remove the check for list_display
## This is failing but the validation which should fail is not.
#class ListDisplayLinksBadThree(models.Model):
# "Test list_display_links, must define list_display to use list_display_links."
# first_name = models.CharField(maxlength=30)
# last_name = models.CharField(maxlength=30)
#
# class Admin:
# list_display_links = ('first_name',)
#
#model_errors += """invalid_admin_options.listdisplaylinksbadthree: "admin.list_display" must be defined for "admin.list_display_links" to be used.
#"""
class ListDisplayLinksGood(models.Model):
"Test list_display_links, Admin list_display_list can be a attribute, method or property."
first_name = models.CharField(maxlength=30)
def _last_name(self):
return self.first_name
last_name = property(_last_name)
def full_name(self):
return "%s %s" % (self.first_name, self.last_name)
class Admin:
list_display = ['first_name','last_name','full_name']
list_display_links = ['first_name','last_name','full_name']
class ListFilterBadOne(models.Model):
"Test list_filter, must be a list or tuple."
first_name = models.CharField(maxlength=30)
class Admin:
list_filter = 'first_name'
model_errors += """invalid_admin_options.listfilterbadone: "admin.list_filter", if given, must be set to a list or tuple.
"""
class ListFilterBadTwo(models.Model):
"Test list_filter, must be a field not a property or method."
first_name = models.CharField(maxlength=30)
def _last_name(self):
return self.first_name
last_name = property(_last_name)
def full_name(self):
return "%s %s" % (self.first_name, self.last_name)
class Admin:
list_filter = ['first_name','last_name','full_name']
model_errors += """invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'last_name', which isn't a field.
invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'full_name', which isn't a field.
"""
class DateHierarchyBadOne(models.Model):
"Test date_hierarchy, must be a date or datetime field."
first_name = models.CharField(maxlength=30)
birth_day = models.DateField()
class Admin:
date_hierarchy = 'first_name'
# TODO: Date Hierarchy needs to check if field is a date/datetime field.
#model_errors += """invalid_admin_options.datehierarchybadone: "admin.date_hierarchy" refers to 'first_name', which isn't a date field or datetime field.
#"""
class DateHierarchyBadTwo(models.Model):
"Test date_hieracrhy, must be a field."
first_name = models.CharField(maxlength=30)
birth_day = models.DateField()
class Admin:
date_hierarchy = 'nonexistent'
model_errors += """invalid_admin_options.datehierarchybadtwo: "admin.date_hierarchy" refers to 'nonexistent', which isn't a field.
"""
class DateHierarchyGood(models.Model):
"Test date_hieracrhy, must be a field."
first_name = models.CharField(maxlength=30)
birth_day = models.DateField()
class Admin:
date_hierarchy = 'birth_day'
class SearchFieldsBadOne(models.Model):
"Test search_fields, must be a list or tuple."
first_name = models.CharField(maxlength=30)
class Admin:
search_fields = ('nonexistent')
# TODO: Add search_fields validation
#model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields", if given, must be set to a list or tuple.
#"""
class SearchFieldsBadTwo(models.Model):
"Test search_fields, must be a field."
first_name = models.CharField(maxlength=30)
def _last_name(self):
return self.first_name
last_name = property(_last_name)
class Admin:
search_fields = ['first_name','last_name']
# TODO: Add search_fields validation
#model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields" refers to 'last_name', which isn't a field.
#"""
class SearchFieldsGood(models.Model):
"Test search_fields, must be a list or tuple."
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
search_fields = ['first_name','last_name']
class JsBadOne(models.Model):
"Test js, must be a list or tuple"
name = models.CharField(maxlength=30)
class Admin:
js = 'test.js'
# TODO: Add a js validator
#model_errors += """invalid_admin_options.jsbadone: "admin.js", if given, must be set to a list or tuple.
#"""
class SaveAsBad(models.Model):
"Test save_as, should be True or False"
name = models.CharField(maxlength=30)
class Admin:
save_as = 'not True or False'
# TODO: Add a save_as validator.
#model_errors += """invalid_admin_options.saveasbad: "admin.save_as", if given, must be set to True or False.
#"""
class SaveOnTopBad(models.Model):
"Test save_on_top, should be True or False"
name = models.CharField(maxlength=30)
class Admin:
save_on_top = 'not True or False'
# TODO: Add a save_on_top validator.
#model_errors += """invalid_admin_options.saveontopbad: "admin.save_on_top", if given, must be set to True or False.
#"""
class ListSelectRelatedBad(models.Model):
"Test list_select_related, should be True or False"
name = models.CharField(maxlength=30)
class Admin:
list_select_related = 'not True or False'
# TODO: Add a list_select_related validator.
#model_errors += """invalid_admin_options.listselectrelatebad: "admin.list_select_related", if given, must be set to True or False.
#"""
class ListPerPageBad(models.Model):
"Test list_per_page, should be a positive integer value."
name = models.CharField(maxlength=30)
class Admin:
list_per_page = 89.3
# TODO: Add a list_per_page validator.
#model_errors += """invalid_admin_options.listperpagebad: "admin.list_per_page", if given, must be a positive integer.
#"""
class FieldsBadOne(models.Model):
"Test fields, should be a tuple"
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
fields = 'not a tuple'
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadone: "admin.fields", if given, must be a tuple.
#"""
class FieldsBadTwo(models.Model):
"""Test fields, 'fields' dict option is required."""
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
fields = ('Name', {'description': 'this fieldset needs fields'})
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadtwo: "admin.fields" each fieldset must include a 'fields' dict.
#"""
class FieldsBadThree(models.Model):
"""Test fields, 'classes' and 'description' are the only allowable extra dict options."""
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
fields = ('Name', {'fields': ('first_name','last_name'),'badoption': 'verybadoption'})
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadthree: "admin.fields" fieldset options must be either 'classes' or 'description'.
#"""
class FieldsGood(models.Model):
"Test fields, working example"
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
birth_day = models.DateField()
class Admin:
fields = (
('Name', {'fields': ('first_name','last_name'),'classes': 'collapse'}),
(None, {'fields': ('birth_day',),'description': 'enter your b-day'})
)
class OrderingBad(models.Model):
"Test ordering, must be a field."
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
class Admin:
ordering = 'nonexistent'
# TODO: Add a ordering validator.
#model_errors += """invalid_admin_options.orderingbad: "admin.ordering" refers to 'nonexistent', which isn't a field.
#"""
## TODO: Add a manager validator, this should fail gracefully.
#class ManagerBad(models.Model):
# "Test manager, must be a manager object."
# first_name = models.CharField(maxlength=30)
#
# class Admin:
# manager = 'nonexistent'
#
#model_errors += """invalid_admin_options.managerbad: "admin.manager" refers to 'nonexistent', which isn't a Manager().
#"""