mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
schema-evolution: updated from trunk/HEAD (r5787)
git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5788 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
fd77e42509
commit
0f5a5a0594
4
AUTHORS
4
AUTHORS
@ -117,7 +117,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
glin@seznam.cz
|
||||
martin.glueck@gmail.com
|
||||
GomoX <gomo@datafull.com>
|
||||
Mario Gonzalez <gonzalemario @t gmail.com>
|
||||
Mario Gonzalez <gonzalemario@gmail.com>
|
||||
Simon Greenhill <dev@simon.net.nz>
|
||||
Owen Griffiths
|
||||
Espen Grindhaug <http://grindhaug.org/>
|
||||
@ -214,6 +214,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
plisk
|
||||
Daniel Poelzleithner <http://poelzi.org/>
|
||||
polpak@yahoo.com
|
||||
Johann Queuniet <johann.queuniet@adh.naellia.eu>
|
||||
J. Rademaker
|
||||
Michael Radziej <mir@noris.de>
|
||||
Ramiro Morales <rm0@gmx.net>
|
||||
@ -242,6 +243,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Thomas Steinacher <http://www.eggdrop.ch/>
|
||||
nowell strite
|
||||
Sundance
|
||||
SuperJared
|
||||
Radek Švarz <http://www.svarz.cz/translate/>
|
||||
Swaroop C H <http://www.swaroopch.info>
|
||||
Aaron Swartz <http://www.aaronsw.com/>
|
||||
|
@ -7,6 +7,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
import datetime
|
||||
import urllib
|
||||
|
||||
UNUSABLE_PASSWORD = '!' # This will never be a valid hash
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
@ -83,11 +85,14 @@ class Group(models.Model):
|
||||
return self.name
|
||||
|
||||
class UserManager(models.Manager):
|
||||
def create_user(self, username, email, password):
|
||||
def create_user(self, username, email, password=None):
|
||||
"Creates and saves a User with the given username, e-mail and password."
|
||||
now = datetime.datetime.now()
|
||||
user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
|
||||
if password:
|
||||
user.set_password(password)
|
||||
else:
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
return user
|
||||
|
||||
@ -179,6 +184,13 @@ class User(models.Model):
|
||||
return is_correct
|
||||
return check_password(raw_password, self.password)
|
||||
|
||||
def set_unusable_password(self):
|
||||
# Sets a value that will never be a valid hash
|
||||
self.password = UNUSABLE_PASSWORD
|
||||
|
||||
def has_usable_password(self):
|
||||
return self.password != UNUSABLE_PASSWORD
|
||||
|
||||
def get_group_permissions(self):
|
||||
"Returns a list of permission strings that this user has through his/her groups."
|
||||
if not hasattr(self, '_group_perm_cache'):
|
||||
@ -268,7 +280,8 @@ class User(models.Model):
|
||||
return self._profile_cache
|
||||
|
||||
class Message(models.Model):
|
||||
"""The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message.
|
||||
"""
|
||||
The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message.
|
||||
"""
|
||||
user = models.ForeignKey(User)
|
||||
message = models.TextField(_('message'))
|
||||
|
19
django/contrib/auth/tests.py
Normal file
19
django/contrib/auth/tests.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""
|
||||
>>> from models import User
|
||||
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
|
||||
>>> u.has_usable_password()
|
||||
True
|
||||
>>> u.check_password('bad')
|
||||
False
|
||||
>>> u.check_password('testpw')
|
||||
True
|
||||
>>> u.set_unusable_password()
|
||||
>>> u.save()
|
||||
>>> u.check_password('testpw')
|
||||
False
|
||||
>>> u.has_usable_password()
|
||||
False
|
||||
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||
>>> u2.has_usable_password()
|
||||
False
|
||||
"""
|
@ -37,9 +37,10 @@ class FieldChoicePlugin(DatabrowsePlugin):
|
||||
|
||||
def urls(self, plugin_name, easy_instance_field):
|
||||
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
|
||||
field_value = smart_str(easy_instance_field.raw_value)
|
||||
return [u'%s%s/%s/%s/' % (easy_instance_field.model.url(),
|
||||
plugin_name, easy_instance_field.field.name,
|
||||
urllib.quote(smart_str(easy_instance_field.raw_value)))]
|
||||
urllib.quote(field_value, safe=''))]
|
||||
|
||||
def model_view(self, request, model_databrowse, url):
|
||||
self.model, self.site = model_databrowse.model, model_databrowse.site
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ul class="objectlist">
|
||||
{% for object in object_list %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ul class="objectlist">
|
||||
{% for object in object_list %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ul class="objectlist">
|
||||
{% for object in object_list %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ul class="objectlist">
|
||||
{% for choice in field.choices %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
<h2><a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a></h2>
|
||||
<p>
|
||||
{% for object in model.sample_objects %}
|
||||
<a href="{{ object.url }}">{{ object }}</a>,
|
||||
<a href="{{ object.url }}">{{ object|escape }}</a>,
|
||||
{% endfor %}
|
||||
<a class="more" href="{{ model.url }}">More →</a>
|
||||
</p>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
<ul class="objectlist">
|
||||
{% for object in model.objects %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object }}</div>
|
||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object|escape }}</div>
|
||||
|
||||
<h1>{{ object.model.verbose_name|capfirst }}: {{ object }}</h1>
|
||||
<h1>{{ object.model.verbose_name|capfirst }}: {{ object|escape }}</h1>
|
||||
|
||||
<table class="objectinfo">
|
||||
{% for field in object.fields %}
|
||||
@ -14,8 +14,8 @@
|
||||
<th>{{ field.field.verbose_name|capfirst }}</th>
|
||||
<td>
|
||||
{% if field.urls %}
|
||||
{% for urlvalue in field.urls %}
|
||||
{% if urlvalue.1 %}<a href="{{ urlvalue.1 }}">{% endif %}{{ urlvalue.0 }}{% if urlvalue.1 %}</a>{% endif %}{% if not forloop.last %}, {% endif %}
|
||||
{% for value, url in field.urls %}
|
||||
{% if url %}<a href="{{ url }}">{% endif %}{{ value|escape }}{% if url %}</a>{% endif %}{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% else %}None{% endif %}
|
||||
</td>
|
||||
@ -29,7 +29,7 @@
|
||||
{% if related_object.object_list %}
|
||||
<ul class="objectlist">
|
||||
{% for object in related_object.object_list %}
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
@ -1454,7 +1454,8 @@ def runserver(addr, port, use_reloader=True, admin_media_dir=''):
|
||||
except (AttributeError, KeyError):
|
||||
error_text = str(e)
|
||||
sys.stderr.write(style.ERROR("Error: %s" % error_text) + '\n')
|
||||
sys.exit(1)
|
||||
# Need to use an OS exit because sys.exit doesn't work in a thread
|
||||
os._exit(1)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
if use_reloader:
|
||||
@ -1548,16 +1549,11 @@ def runfcgi(args):
|
||||
runfastcgi(args)
|
||||
runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
|
||||
|
||||
def test(app_labels, verbosity=1):
|
||||
def test(test_labels, verbosity=1, interactive=True):
|
||||
"Runs the test suite for the specified applications"
|
||||
from django.conf import settings
|
||||
from django.db.models import get_app, get_apps
|
||||
|
||||
if len(app_labels) == 0:
|
||||
app_list = get_apps()
|
||||
else:
|
||||
app_list = [get_app(app_label) for app_label in app_labels]
|
||||
|
||||
test_path = settings.TEST_RUNNER.split('.')
|
||||
# Allow for Python 2.5 relative paths
|
||||
if len(test_path) > 1:
|
||||
@ -1567,12 +1563,12 @@ def test(app_labels, verbosity=1):
|
||||
test_module = __import__(test_module_name, {}, {}, test_path[-1])
|
||||
test_runner = getattr(test_module, test_path[-1])
|
||||
|
||||
failures = test_runner(app_list, verbosity)
|
||||
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
|
||||
if failures:
|
||||
sys.exit(failures)
|
||||
|
||||
test.help_doc = 'Runs the test suite for the specified applications, or the entire site if no apps are specified'
|
||||
test.args = '[--verbosity] ' + APP_ARGS
|
||||
test.args = '[--verbosity] [--noinput]' + APP_ARGS
|
||||
|
||||
def load_data(fixture_labels, verbosity=1):
|
||||
"Installs the provided fixture file(s) as data in the database."
|
||||
@ -1849,7 +1845,12 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
|
||||
action_mapping[action](args[1])
|
||||
except IndexError:
|
||||
parser.print_usage_and_exit()
|
||||
elif action in ('test', 'loaddata'):
|
||||
elif action == 'test':
|
||||
try:
|
||||
action_mapping[action](args[1:], int(options.verbosity), options.interactive)
|
||||
except IndexError:
|
||||
parser.print_usage_and_exit()
|
||||
elif action == 'loaddata':
|
||||
try:
|
||||
action_mapping[action](args[1:], int(options.verbosity))
|
||||
except IndexError:
|
||||
|
@ -621,7 +621,8 @@ class DecimalField(Field):
|
||||
try:
|
||||
return decimal.Decimal(value)
|
||||
except decimal.InvalidOperation:
|
||||
raise validators.ValidationError, ugettext("This value must be a decimal number.")
|
||||
raise validators.ValidationError(
|
||||
_("This value must be a decimal number."))
|
||||
|
||||
def _format(self, value):
|
||||
if isinstance(value, basestring):
|
||||
|
@ -3,10 +3,6 @@ from django.dispatch import dispatcher
|
||||
from django.db.models import signals
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
|
||||
# Size of each "chunk" for get_iterator calls.
|
||||
# Larger values are slightly faster at the expense of more storage space.
|
||||
GET_ITERATOR_CHUNK_SIZE = 100
|
||||
|
||||
def ensure_default_manager(sender):
|
||||
cls = sender
|
||||
if not hasattr(cls, '_default_manager'):
|
||||
|
@ -579,28 +579,36 @@ class ValuesQuerySet(QuerySet):
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
# self._fields is a list of field names to fetch.
|
||||
# self._select is a dictionary, and dictionaries' key order is
|
||||
# undefined, so we convert it to a list of tuples.
|
||||
extra_select = self._select.items()
|
||||
|
||||
# Construct two objects -- fields and field_names.
|
||||
# fields is a list of Field objects to fetch.
|
||||
# field_names is a list of field names, which will be the keys in the
|
||||
# resulting dictionaries.
|
||||
if self._fields:
|
||||
if not self._select:
|
||||
if not extra_select:
|
||||
fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
|
||||
field_names = self._fields
|
||||
else:
|
||||
fields = []
|
||||
field_names = []
|
||||
for f in self._fields:
|
||||
if f in [field.name for field in self.model._meta.fields]:
|
||||
fields.append(self.model._meta.get_field(f, many_to_many=False))
|
||||
field_names.append(f)
|
||||
elif not self._select.has_key(f):
|
||||
raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f )
|
||||
|
||||
field_names = self._fields
|
||||
raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f))
|
||||
else: # Default to all fields.
|
||||
fields = self.model._meta.fields
|
||||
field_names = [f.attname for f in fields]
|
||||
|
||||
columns = [f.column for f in fields]
|
||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||
# Add any additional SELECTs.
|
||||
if self._select:
|
||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
|
||||
if extra_select:
|
||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in extra_select])
|
||||
field_names.extend([f[0] for f in extra_select])
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
|
@ -232,16 +232,8 @@ class BoundField(StrAndUnicode):
|
||||
self.help_text = field.help_text or ''
|
||||
|
||||
def __unicode__(self):
|
||||
"Renders this field as an HTML widget."
|
||||
# Use the 'widget' attribute on the field to determine which type
|
||||
# of HTML widget to use.
|
||||
value = self.as_widget(self.field.widget)
|
||||
if not isinstance(value, basestring):
|
||||
# Some Widget render() methods -- notably RadioSelect -- return a
|
||||
# "special" object rather than a string. Call __unicode__() on that
|
||||
# object to get its rendered value.
|
||||
value = unicode(value)
|
||||
return value
|
||||
"""Renders this field as an HTML widget."""
|
||||
return self.as_widget()
|
||||
|
||||
def _errors(self):
|
||||
"""
|
||||
@ -251,7 +243,14 @@ class BoundField(StrAndUnicode):
|
||||
return self.form.errors.get(self.name, ErrorList())
|
||||
errors = property(_errors)
|
||||
|
||||
def as_widget(self, widget, attrs=None):
|
||||
def as_widget(self, widget=None, attrs=None):
|
||||
"""
|
||||
Renders the field by rendering the passed widget, adding any HTML
|
||||
attributes passed as attrs. If no widget is specified, then the
|
||||
field's default widget will be used.
|
||||
"""
|
||||
if not widget:
|
||||
widget = self.field.widget
|
||||
attrs = attrs or {}
|
||||
auto_id = self.auto_id
|
||||
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
|
||||
|
@ -216,7 +216,11 @@ class SelectMultiple(Widget):
|
||||
return data.get(name, None)
|
||||
|
||||
class RadioInput(StrAndUnicode):
|
||||
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
||||
"""
|
||||
An object used by RadioFieldRenderer that represents a single
|
||||
<input type='radio'>.
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, attrs, choice, index):
|
||||
self.name, self.value = name, value
|
||||
self.attrs = attrs
|
||||
@ -239,7 +243,10 @@ class RadioInput(StrAndUnicode):
|
||||
return u'<input%s />' % flatatt(final_attrs)
|
||||
|
||||
class RadioFieldRenderer(StrAndUnicode):
|
||||
"An object used by RadioSelect to enable customization of radio widgets."
|
||||
"""
|
||||
An object used by RadioSelect to enable customization of radio widgets.
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, attrs, choices):
|
||||
self.name, self.value, self.attrs = name, value, attrs
|
||||
self.choices = choices
|
||||
@ -253,16 +260,30 @@ class RadioFieldRenderer(StrAndUnicode):
|
||||
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
|
||||
|
||||
def __unicode__(self):
|
||||
"Outputs a <ul> for this set of radio fields."
|
||||
return self.render()
|
||||
|
||||
def render(self):
|
||||
"""Outputs a <ul> for this set of radio fields."""
|
||||
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])
|
||||
|
||||
class RadioSelect(Select):
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.renderer = kwargs.pop('renderer', None)
|
||||
if not self.renderer:
|
||||
self.renderer = RadioFieldRenderer
|
||||
super(RadioSelect, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_renderer(self, name, value, attrs=None, choices=()):
|
||||
"""Returns an instance of the renderer."""
|
||||
if value is None: value = ''
|
||||
str_value = force_unicode(value) # Normalize to string.
|
||||
final_attrs = self.build_attrs(attrs)
|
||||
return RadioFieldRenderer(name, str_value, final_attrs, list(chain(self.choices, choices)))
|
||||
choices = list(chain(self.choices, choices))
|
||||
return self.renderer(name, str_value, final_attrs, choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
return self.get_renderer(name, value, attrs, choices).render()
|
||||
|
||||
def id_for_label(self, id_):
|
||||
# RadioSelect is represented by multiple <input type="radio"> fields,
|
||||
|
@ -1,32 +1,62 @@
|
||||
# This module collects helper functions and classes that "span" multiple levels
|
||||
# of MVC. In other words, these functions/classes introduce controlled coupling
|
||||
# for convenience's sake.
|
||||
"""
|
||||
This module collects helper functions and classes that "span" multiple levels
|
||||
of MVC. In other words, these functions/classes introduce controlled coupling
|
||||
for convenience's sake.
|
||||
"""
|
||||
|
||||
from django.template import loader
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.db.models.manager import Manager
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
def render_to_response(*args, **kwargs):
|
||||
"""
|
||||
Returns a HttpResponse whose content is filled with the result of calling
|
||||
django.template.loader.render_to_string() with the passed arguments.
|
||||
"""
|
||||
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):
|
||||
def _get_queryset(klass):
|
||||
"""
|
||||
Returns a QuerySet from a Model, Manager, or QuerySet. Created to make
|
||||
get_object_or_404 and get_list_or_404 more DRY.
|
||||
"""
|
||||
if isinstance(klass, QuerySet):
|
||||
return klass
|
||||
elif isinstance(klass, Manager):
|
||||
manager = klass
|
||||
klass = manager.model
|
||||
else:
|
||||
manager = klass._default_manager
|
||||
return manager.all()
|
||||
|
||||
def get_object_or_404(klass, *args, **kwargs):
|
||||
"""
|
||||
Uses get() to return an object, or raises a Http404 exception if the object
|
||||
does not exist.
|
||||
|
||||
klass may be a Model, Manager, or QuerySet object. All other passed
|
||||
arguments and keyword arguments are used in the get() query.
|
||||
|
||||
Note: Like with get(), an AssertionError will be raised if more than one
|
||||
object is found.
|
||||
"""
|
||||
queryset = _get_queryset(klass)
|
||||
try:
|
||||
return manager.get(*args, **kwargs)
|
||||
except klass.DoesNotExist:
|
||||
raise Http404('No %s matches the given query.' % klass._meta.object_name)
|
||||
return queryset.get(*args, **kwargs)
|
||||
except queryset.model.DoesNotExist:
|
||||
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
|
||||
|
||||
def get_list_or_404(klass, *args, **kwargs):
|
||||
if isinstance(klass, Manager):
|
||||
manager = klass
|
||||
else:
|
||||
manager = klass._default_manager
|
||||
obj_list = list(manager.filter(*args, **kwargs))
|
||||
"""
|
||||
Uses filter() to return a list of objects, or raise a Http404 exception if
|
||||
the list is empty.
|
||||
|
||||
klass may be a Model, Manager, or QuerySet object. All other passed
|
||||
arguments and keyword arguments are used in the filter() query.
|
||||
"""
|
||||
queryset = _get_queryset(klass)
|
||||
obj_list = list(queryset.filter(*args, **kwargs))
|
||||
if not obj_list:
|
||||
raise Http404('No %s matches the given query.' % manager.model._meta.object_name)
|
||||
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
|
||||
return obj_list
|
||||
|
@ -1,9 +1,14 @@
|
||||
# Wrapper for loading templates from "template" directories in installed app packages.
|
||||
"""
|
||||
Wrapper for loading templates from "template" directories in INSTALLED_APPS
|
||||
packages.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template import TemplateDoesNotExist
|
||||
import os
|
||||
from django.utils._os import safe_join
|
||||
|
||||
# At compile time, cache the directories to search.
|
||||
app_template_dirs = []
|
||||
@ -28,8 +33,14 @@ for app in settings.INSTALLED_APPS:
|
||||
app_template_dirs = tuple(app_template_dirs)
|
||||
|
||||
def get_template_sources(template_name, template_dirs=None):
|
||||
for template_dir in app_template_dirs:
|
||||
yield os.path.join(template_dir, template_name)
|
||||
if not template_dirs:
|
||||
template_dirs = app_template_dirs
|
||||
for template_dir in template_dirs:
|
||||
try:
|
||||
yield safe_join(template_dir, template_name)
|
||||
except ValueError:
|
||||
# The joined path was located outside of template_dir.
|
||||
pass
|
||||
|
||||
def load_template_source(template_name, template_dirs=None):
|
||||
for filepath in get_template_sources(template_name, template_dirs):
|
||||
|
@ -1,14 +1,20 @@
|
||||
# Wrapper for loading templates from the filesystem.
|
||||
"""
|
||||
Wrapper for loading templates from the filesystem.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import TemplateDoesNotExist
|
||||
import os
|
||||
from django.utils._os import safe_join
|
||||
|
||||
def get_template_sources(template_name, template_dirs=None):
|
||||
if not template_dirs:
|
||||
template_dirs = settings.TEMPLATE_DIRS
|
||||
for template_dir in template_dirs:
|
||||
yield os.path.join(template_dir, template_name)
|
||||
try:
|
||||
yield safe_join(template_dir, template_name)
|
||||
except ValueError:
|
||||
# The joined path was located outside of template_dir.
|
||||
pass
|
||||
|
||||
def load_template_source(template_name, template_dirs=None):
|
||||
tried = []
|
||||
|
@ -195,7 +195,7 @@ class Client:
|
||||
'CONTENT_LENGTH': None,
|
||||
'CONTENT_TYPE': 'text/html; charset=utf-8',
|
||||
'PATH_INFO': path,
|
||||
'QUERY_STRING': urlencode(data),
|
||||
'QUERY_STRING': urlencode(data, doseq=True),
|
||||
'REQUEST_METHOD': 'GET',
|
||||
}
|
||||
r.update(extra)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import unittest
|
||||
from django.conf import settings
|
||||
from django.db.models import get_app, get_apps
|
||||
from django.test import _doctest as doctest
|
||||
from django.test.utils import setup_test_environment, teardown_test_environment
|
||||
from django.test.utils import create_test_db, destroy_test_db
|
||||
@ -10,6 +11,31 @@ TEST_MODULE = 'tests'
|
||||
|
||||
doctestOutputChecker = OutputChecker()
|
||||
|
||||
def get_tests(app_module):
|
||||
try:
|
||||
app_path = app_module.__name__.split('.')[:-1]
|
||||
test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
|
||||
except ImportError, e:
|
||||
# Couldn't import tests.py. Was it due to a missing file, or
|
||||
# due to an import error in a tests.py that actually exists?
|
||||
import os.path
|
||||
from imp import find_module
|
||||
try:
|
||||
mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
|
||||
except ImportError:
|
||||
# 'tests' module doesn't exist. Move on.
|
||||
test_module = None
|
||||
else:
|
||||
# The module exists, so there must be an import error in the
|
||||
# test module itself. We don't need the module; so if the
|
||||
# module was a single file module (i.e., tests.py), close the file
|
||||
# handle returned by find_module. Otherwise, the test module
|
||||
# is a directory, and there is nothing to close.
|
||||
if mod[0]:
|
||||
mod[0].close()
|
||||
raise
|
||||
return test_module
|
||||
|
||||
def build_suite(app_module):
|
||||
"Create a complete Django test suite for the provided application module"
|
||||
suite = unittest.TestSuite()
|
||||
@ -30,10 +56,8 @@ def build_suite(app_module):
|
||||
|
||||
# Check to see if a separate 'tests' module exists parallel to the
|
||||
# models module
|
||||
try:
|
||||
app_path = app_module.__name__.split('.')[:-1]
|
||||
test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
|
||||
|
||||
test_module = get_tests(app_module)
|
||||
if test_module:
|
||||
# Load unit and doctests in the tests.py module. If module has
|
||||
# a suite() method, use it. Otherwise build the test suite ourselves.
|
||||
if hasattr(test_module, 'suite'):
|
||||
@ -47,34 +71,50 @@ def build_suite(app_module):
|
||||
except ValueError:
|
||||
# No doc tests in tests.py
|
||||
pass
|
||||
except ImportError, e:
|
||||
# Couldn't import tests.py. Was it due to a missing file, or
|
||||
# due to an import error in a tests.py that actually exists?
|
||||
import os.path
|
||||
from imp import find_module
|
||||
try:
|
||||
mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
|
||||
except ImportError:
|
||||
# 'tests' module doesn't exist. Move on.
|
||||
pass
|
||||
else:
|
||||
# The module exists, so there must be an import error in the
|
||||
# test module itself. We don't need the module; so if the
|
||||
# module was a single file module (i.e., tests.py), close the file
|
||||
# handle returned by find_module. Otherwise, the test module
|
||||
# is a directory, and there is nothing to close.
|
||||
if mod[0]:
|
||||
mod[0].close()
|
||||
raise
|
||||
|
||||
return suite
|
||||
|
||||
def run_tests(module_list, verbosity=1, extra_tests=[]):
|
||||
def build_test(label):
|
||||
"""Construct a test case a test with the specified label. Label should
|
||||
be of the form model.TestClass or model.TestClass.test_method. Returns
|
||||
an instantiated test or test suite corresponding to the label provided.
|
||||
|
||||
"""
|
||||
Run the unit tests for all the modules in the provided list.
|
||||
This testrunner will search each of the modules in the provided list,
|
||||
looking for doctests and unittests in models.py or tests.py within
|
||||
the module. A list of 'extra' tests may also be provided; these tests
|
||||
parts = label.split('.')
|
||||
if len(parts) < 2 or len(parts) > 3:
|
||||
raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
|
||||
|
||||
app_module = get_app(parts[0])
|
||||
TestClass = getattr(app_module, parts[1], None)
|
||||
|
||||
# Couldn't find the test class in models.py; look in tests.py
|
||||
if TestClass is None:
|
||||
test_module = get_tests(app_module)
|
||||
if test_module:
|
||||
TestClass = getattr(test_module, parts[1], None)
|
||||
|
||||
if len(parts) == 2: # label is app.TestClass
|
||||
try:
|
||||
return unittest.TestLoader().loadTestsFromTestCase(TestClass)
|
||||
except TypeError:
|
||||
raise ValueError("Test label '%s' does not refer to a test class" % label)
|
||||
else: # label is app.TestClass.test_method
|
||||
return TestClass(parts[2])
|
||||
|
||||
def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
|
||||
"""
|
||||
Run the unit tests for all the test labels in the provided list.
|
||||
Labels must be of the form:
|
||||
- app.TestClass.test_method
|
||||
Run a single specific test method
|
||||
- app.TestClass
|
||||
Run all the test methods in a given class
|
||||
- app
|
||||
Search for doctests and unittests in the named application.
|
||||
|
||||
When looking for tests, the test runner will look in the models and
|
||||
tests modules for the application.
|
||||
|
||||
A list of 'extra' tests may also be provided; these tests
|
||||
will be added to the test suite.
|
||||
|
||||
Returns the number of tests that failed.
|
||||
@ -84,14 +124,22 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
|
||||
settings.DEBUG = False
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
for module in module_list:
|
||||
suite.addTest(build_suite(module))
|
||||
if test_labels:
|
||||
for label in test_labels:
|
||||
if '.' in label:
|
||||
suite.addTest(build_test(label))
|
||||
else:
|
||||
app = get_app(label)
|
||||
suite.addTest(build_suite(app))
|
||||
else:
|
||||
for app in get_apps():
|
||||
suite.addTest(build_suite(app))
|
||||
|
||||
for test in extra_tests:
|
||||
suite.addTest(test)
|
||||
|
||||
old_name = settings.DATABASE_NAME
|
||||
create_test_db(verbosity)
|
||||
create_test_db(verbosity, autoclobber=not interactive)
|
||||
result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
|
||||
destroy_test_db(old_name, verbosity)
|
||||
|
||||
|
@ -84,7 +84,7 @@ class TestCase(unittest.TestCase):
|
||||
"Couldn't retrieve page: Response code was %d (expected %d)'" %
|
||||
(response.status_code, status_code))
|
||||
real_count = response.content.count(text)
|
||||
if count:
|
||||
if count is not None:
|
||||
self.assertEqual(real_count, count,
|
||||
"Found %d instances of '%s' in response (expected %d)" % (real_count, text, count))
|
||||
else:
|
||||
|
@ -144,6 +144,10 @@ def create_test_db(verbosity=1, autoclobber=False):
|
||||
|
||||
management.syncdb(verbosity, interactive=False)
|
||||
|
||||
if settings.CACHE_BACKEND.startswith('db://'):
|
||||
cache_name = settings.CACHE_BACKEND[len('db://'):]
|
||||
management.createcachetable(cache_name)
|
||||
|
||||
# Get a cursor (even though we don't need one yet). This has
|
||||
# the side effect of initializing the test database.
|
||||
cursor = connection.cursor()
|
||||
|
23
django/utils/_os.py
Normal file
23
django/utils/_os.py
Normal file
@ -0,0 +1,23 @@
|
||||
from os.path import join, normcase, abspath, sep
|
||||
|
||||
def safe_join(base, *paths):
|
||||
"""
|
||||
Joins one or more path components to the base path component intelligently.
|
||||
Returns a normalized, absolute version of the final path.
|
||||
|
||||
The final path must be located inside of the base path component (otherwise
|
||||
a ValueError is raised).
|
||||
"""
|
||||
# We need to use normcase to ensure we don't false-negative on case
|
||||
# insensitive operating systems (like Windows).
|
||||
final_path = normcase(abspath(join(base, *paths)))
|
||||
base_path = normcase(abspath(base))
|
||||
base_path_len = len(base_path)
|
||||
# Ensure final_path starts with base_path and that the next character after
|
||||
# the final path is os.sep (or nothing, in which case final_path must be
|
||||
# equal to base_path).
|
||||
if not final_path.startswith(base_path) \
|
||||
or final_path[base_path_len:base_path_len+1] not in ('', sep):
|
||||
raise ValueError('the joined path is located outside of the base path'
|
||||
' component')
|
||||
return final_path
|
@ -30,6 +30,9 @@ def urlencode(query, doseq=0):
|
||||
"""
|
||||
if hasattr(query, 'items'):
|
||||
query = query.items()
|
||||
return urllib.urlencode([(smart_str(k), smart_str(v)) for k,
|
||||
v in query], doseq)
|
||||
return urllib.urlencode(
|
||||
[(smart_str(k),
|
||||
isinstance(v, (list,tuple)) and [smart_str(i) for i in v] or smart_str(v))
|
||||
for k, v in query],
|
||||
doseq)
|
||||
|
||||
|
@ -114,6 +114,18 @@ custom methods:
|
||||
string is the correct password for the user. (This takes care of the
|
||||
password hashing in making the comparison.)
|
||||
|
||||
* ``set_unusable_password()`` -- **New in Django development version.**
|
||||
Marks the user as having no password set. This isn't the same as having
|
||||
a blank string for a password. ``check_password()`` for this user will
|
||||
never return ``True``. Doesn't save the ``User`` object.
|
||||
|
||||
You may need this if authentication for your application takes place
|
||||
against an existing external source such as an LDAP directory.
|
||||
|
||||
* ``has_usable_password()`` -- **New in Django development version.**
|
||||
Returns ``False`` if ``set_unusable_password()`` has been called for this
|
||||
user.
|
||||
|
||||
* ``get_group_permissions()`` -- Returns a list of permission strings that
|
||||
the user has, through his/her groups.
|
||||
|
||||
@ -152,9 +164,11 @@ Manager functions
|
||||
|
||||
The ``User`` model has a custom manager that has the following helper functions:
|
||||
|
||||
* ``create_user(username, email, password)`` -- Creates, saves and returns
|
||||
a ``User``. The ``username``, ``email`` and ``password`` are set as
|
||||
given, and the ``User`` gets ``is_active=True``.
|
||||
* ``create_user(username, email, password=None)`` -- Creates, saves and
|
||||
returns a ``User``. The ``username``, ``email`` and ``password`` are set
|
||||
as given, and the ``User`` gets ``is_active=True``.
|
||||
|
||||
If no password is provided, ``set_unusable_password()`` will be called.
|
||||
|
||||
See _`Creating users` for example usage.
|
||||
|
||||
|
@ -279,6 +279,22 @@ Please follow these coding standards when writing code for inclusion in Django:
|
||||
* Mark all strings for internationalization; see the `i18n documentation`_
|
||||
for details.
|
||||
|
||||
* In docstrings, use "action words," like so::
|
||||
|
||||
def foo():
|
||||
"""
|
||||
Calculates something and returns the result.
|
||||
"""
|
||||
pass
|
||||
|
||||
Here's an example of what not to do::
|
||||
|
||||
def foo():
|
||||
"""
|
||||
Calculate something and return the result.
|
||||
"""
|
||||
pass
|
||||
|
||||
* Please don't put your name in the code you contribute. Our policy is to
|
||||
keep contributors' names in the ``AUTHORS`` file distributed with Django
|
||||
-- not scattered throughout the codebase itself. Feel free to include a
|
||||
|
@ -20,7 +20,7 @@ a weblog application::
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(maxlength=50)
|
||||
email = models.URLField()
|
||||
email = models.EmailField()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
@ -1891,8 +1891,8 @@ 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
|
||||
arbitrary number of keyword arguments, which it passes to the default
|
||||
manager's ``get()`` function. It raises ``Http404`` if the object doesn't
|
||||
exist. For example::
|
||||
|
||||
# Get the Entry with a primary key of 3
|
||||
@ -1901,7 +1901,7 @@ exist. For example::
|
||||
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 if you want to search a list of related objects,
|
||||
you can provide ``get_object_or_404()`` with a manager object instead.
|
||||
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'
|
||||
@ -1911,6 +1911,14 @@ For example::
|
||||
# entry with a primary key of 3
|
||||
e = get_object_or_404(Entry.recent_entries, pk=3)
|
||||
|
||||
**New in Django development version:** The first argument to
|
||||
``get_object_or_404()`` can be a ``QuerySet`` object. This is useful in cases
|
||||
where you've defined a custom manager method. For example::
|
||||
|
||||
# Use a QuerySet returned from a 'published' method of a custom manager
|
||||
# in the search for an entry with primary key of 5
|
||||
e = get_object_or_404(Entry.objects.published(), pk=5)
|
||||
|
||||
get_list_or_404()
|
||||
-----------------
|
||||
|
||||
|
@ -1052,6 +1052,44 @@ create your tables. It's not called at any other time, so it can afford to
|
||||
execute slightly complex code, such as the ``DATABASE_ENGINE`` check in the
|
||||
above example.
|
||||
|
||||
Some database column types accept parameters, such as ``CHAR(25)``, where the
|
||||
parameter ``25`` represents the maximum column length. In cases like these,
|
||||
it's more flexible if the parameter is specified in the model rather than being
|
||||
hard-coded in the ``db_type()`` method. For example, it wouldn't make much
|
||||
sense to have a ``CharMaxlength25Field``, shown here::
|
||||
|
||||
# This is a silly example of hard-coded parameters.
|
||||
class CharMaxlength25Field(models.Field):
|
||||
def db_type(self):
|
||||
return 'char(25)'
|
||||
|
||||
# In the model:
|
||||
class MyModel(models.Model):
|
||||
# ...
|
||||
my_field = CharMaxlength25Field()
|
||||
|
||||
The better way of doing this would be to make the parameter specifiable at run
|
||||
time -- i.e., when the class is instantiated. To do that, just implement
|
||||
``__init__()``, like so::
|
||||
|
||||
# This is a much more flexible example.
|
||||
class BetterCharField(models.Field):
|
||||
def __init__(self, maxlength, *args, **kwargs):
|
||||
self.maxlength = maxlength
|
||||
super(BetterCharField, self).__init__(*args, **kwargs)
|
||||
|
||||
def db_type(self):
|
||||
return 'char(%s)' % self.maxlength
|
||||
|
||||
# In the model:
|
||||
class MyModel(models.Model):
|
||||
# ...
|
||||
my_field = BetterCharField(25)
|
||||
|
||||
Note that if you implement ``__init__()`` on a ``Field`` subclass, it's
|
||||
important to call ``Field.__init__()`` -- i.e., the parent class'
|
||||
``__init__()`` method.
|
||||
|
||||
Meta options
|
||||
============
|
||||
|
||||
|
@ -450,6 +450,9 @@ look like::
|
||||
def setUp(self):
|
||||
# test definitions as before
|
||||
|
||||
def testFluffyAnimals(self):
|
||||
# A test that uses the fixtures
|
||||
|
||||
At the start of each test case, before ``setUp()`` is run, Django will
|
||||
flush the database, returning the database the state it was in directly
|
||||
after ``syncdb`` was called. Then, all the named fixtures are installed.
|
||||
@ -571,6 +574,18 @@ but you only want to run the animals unit tests, run::
|
||||
|
||||
$ ./manage.py test animals
|
||||
|
||||
**New in Django development version:** If you use unit tests, you can be more
|
||||
specific in the tests that are executed. To run a single test case in an
|
||||
application (for example, the AnimalTestCase described previously), add the
|
||||
name of the test case to the label on the command line::
|
||||
|
||||
$ ./manage.py test animals.AnimalTestCase
|
||||
|
||||
**New in Django development version:** To run a single test method inside a
|
||||
test case, add the name of the test method to the label::
|
||||
|
||||
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
|
||||
|
||||
When you run your tests, you'll see a bunch of text flow by as the test
|
||||
database is created and models are initialized. This test database is
|
||||
created from scratch every time you run your tests.
|
||||
@ -662,17 +677,36 @@ framework that can be executed from Python code.
|
||||
Defining a test runner
|
||||
----------------------
|
||||
By convention, a test runner should be called ``run_tests``; however, you
|
||||
can call it anything you want. The only requirement is that it accept two
|
||||
arguments:
|
||||
can call it anything you want. The only requirement is that it has the
|
||||
same arguments as the Django test runner:
|
||||
|
||||
``run_tests(module_list, verbosity=1)``
|
||||
The module list is the list of Python modules that contain the models to be
|
||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||
``run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[])``
|
||||
|
||||
**New in Django development version:** ``test_labels`` is a list of
|
||||
strings describing the tests to be run. A test label can take one of
|
||||
three forms:
|
||||
|
||||
* ``app.TestCase.test_method`` - Run a single test method in a test case
|
||||
* ``app.TestCase`` - Run all the test methods in a test case
|
||||
* ``app`` - Search for and run all tests in the named application.
|
||||
|
||||
If ``test_labels`` has a value of ``None``, the test runner should run
|
||||
search for tests in all the applications in ``INSTALLED_APPS``.
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console; ``0`` is no output, ``1`` is normal output,
|
||||
and ``2`` is verbose output.
|
||||
|
||||
**New in Django development version:** If ``interactive`` is ``True``, the
|
||||
test suite may ask the user for instructions when the test suite is
|
||||
executed. An example of this behavior would be asking for permission to
|
||||
delete an existing test database. If ``interactive`` is ``False``, the
|
||||
test suite must be able to run without any manual intervention.
|
||||
|
||||
``extra_tests`` is a list of extra ``TestCase`` instances to add to the
|
||||
suite that is executed by the test runner. These extra tests are run
|
||||
in addition to those discovered in the modules listed in ``module_list``.
|
||||
|
||||
This method should return the number of tests that failed.
|
||||
|
||||
Testing utilities
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
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.
|
||||
exception was raised 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.
|
||||
exception was raised during the filter() call.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
@ -69,11 +69,28 @@ Http404: No Article matches the given query.
|
||||
>>> get_object_or_404(Article.by_a_sir, title="Run away!")
|
||||
<Article: Run away!>
|
||||
|
||||
# QuerySets can be used too.
|
||||
>>> get_object_or_404(Article.objects.all(), title__contains="Run")
|
||||
<Article: Run away!>
|
||||
|
||||
# Just as when using a get() lookup, you will get an error if more than one
|
||||
# object is returned.
|
||||
>>> get_object_or_404(Author.objects.all())
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {}
|
||||
|
||||
# Using an EmptyQuerySet raises a Http404 error.
|
||||
>>> get_object_or_404(Article.objects.none(), title__contains="Run")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
Http404: No Article matches the given query.
|
||||
|
||||
# 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
|
||||
# Http404 is returned if the list is empty.
|
||||
>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
@ -83,4 +100,8 @@ Http404: No Article matches the given query.
|
||||
>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
|
||||
[<Article: Run away!>]
|
||||
|
||||
# QuerySets can be used too.
|
||||
>>> get_list_or_404(Article.objects.all(), title__icontains="Run")
|
||||
[<Article: Run away!>]
|
||||
|
||||
"""}
|
||||
|
@ -132,8 +132,7 @@ True
|
||||
[('headline', u'Article 7'), ('id', 7)]
|
||||
[('headline', u'Article 1'), ('id', 1)]
|
||||
|
||||
|
||||
# you can use values() even on extra fields
|
||||
# The values() method works with "extra" fields specified in extra(select).
|
||||
>>> for d in Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'):
|
||||
... i = d.items()
|
||||
... i.sort()
|
||||
@ -145,15 +144,24 @@ True
|
||||
[('id', 3), ('id_plus_one', 4)]
|
||||
[('id', 7), ('id_plus_one', 8)]
|
||||
[('id', 1), ('id_plus_one', 2)]
|
||||
>>> data = {'id_plus_one': 'id+1', 'id_plus_two': 'id+2', 'id_plus_three': 'id+3',
|
||||
... 'id_plus_four': 'id+4', 'id_plus_five': 'id+5', 'id_plus_six': 'id+6',
|
||||
... 'id_plus_seven': 'id+7', 'id_plus_eight': 'id+8'}
|
||||
>>> result = list(Article.objects.filter(id=1).extra(select=data).values(*data.keys()))[0]
|
||||
>>> result = result.items()
|
||||
>>> result.sort()
|
||||
>>> result
|
||||
[('id_plus_eight', 9), ('id_plus_five', 6), ('id_plus_four', 5), ('id_plus_one', 2), ('id_plus_seven', 8), ('id_plus_six', 7), ('id_plus_three', 4), ('id_plus_two', 3)]
|
||||
|
||||
# however, an exception FieldDoesNotExist will still be thrown
|
||||
# if you try to access non-existent field (field that is neither on the model nor extra)
|
||||
# However, an exception FieldDoesNotExist will be thrown if you specify a
|
||||
# non-existent field name in values() (a field that is neither in the model
|
||||
# nor in extra(select)).
|
||||
>>> Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
FieldDoesNotExist: Article has no field named 'id_plus_two'
|
||||
|
||||
# if you don't specify which fields, all are returned
|
||||
# If you don't specify field names to values(), all are returned.
|
||||
>>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}]
|
||||
True
|
||||
|
||||
|
@ -122,6 +122,18 @@ class ClientTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "Valid POST Template")
|
||||
|
||||
def test_valid_form_with_hints(self):
|
||||
"GET a form, providing hints in the GET data"
|
||||
hints = {
|
||||
'text': 'Hello World',
|
||||
'multi': ('b','c','e')
|
||||
}
|
||||
response = self.client.get('/test_client/form_view/', data=hints)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "Form GET Template")
|
||||
# Check that the multi-value data has been rolled out ok
|
||||
self.assertContains(response, 'Select a valid choice.', 0)
|
||||
|
||||
def test_incomplete_data_form(self):
|
||||
"POST incomplete data to a form"
|
||||
post_data = {
|
||||
|
@ -84,7 +84,7 @@ def form_view(request):
|
||||
t = Template('Invalid POST data. {{ form.errors }}', name='Invalid POST Template')
|
||||
c = Context({'form': form})
|
||||
else:
|
||||
form = TestForm()
|
||||
form = TestForm(request.GET)
|
||||
t = Template('Viewing base form. {{ form }}.', name='Form GET Template')
|
||||
c = Context({'form': form})
|
||||
|
||||
@ -108,7 +108,6 @@ def form_view_with_template(request):
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def login_protected_view(request):
|
||||
"A simple view that is login protected."
|
||||
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||
|
@ -4,6 +4,7 @@ from regressions import regression_tests
|
||||
|
||||
form_tests = r"""
|
||||
>>> from django.newforms import *
|
||||
>>> from django.newforms.widgets import RadioFieldRenderer
|
||||
>>> import datetime
|
||||
>>> import time
|
||||
>>> import re
|
||||
@ -614,11 +615,11 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
||||
<li><label><input type="radio" name="num" value="5" /> 5</label></li>
|
||||
</ul>
|
||||
|
||||
The render() method returns a RadioFieldRenderer object, whose str() is a <ul>.
|
||||
RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
|
||||
You can manipulate that object directly to customize the way the RadioSelect
|
||||
is rendered.
|
||||
>>> w = RadioSelect()
|
||||
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
|
||||
>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
|
||||
>>> for inp in r:
|
||||
... print inp
|
||||
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
|
||||
@ -644,10 +645,21 @@ beatle J P Paul False
|
||||
beatle J G George False
|
||||
beatle J R Ringo False
|
||||
|
||||
You can create your own custom renderers for RadioSelect to use.
|
||||
>>> class MyRenderer(RadioFieldRenderer):
|
||||
... def render(self):
|
||||
... return u'<br />\n'.join([unicode(choice) for choice in self])
|
||||
>>> w = RadioSelect(renderer=MyRenderer)
|
||||
>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
|
||||
<label><input type="radio" name="beatle" value="J" /> John</label><br />
|
||||
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
|
||||
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
|
||||
<label><input type="radio" name="beatle" value="R" /> Ringo</label>
|
||||
|
||||
A RadioFieldRenderer object also allows index access to individual RadioInput
|
||||
objects.
|
||||
>>> w = RadioSelect()
|
||||
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
|
||||
>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
|
||||
>>> print r[1]
|
||||
<label><input type="radio" name="beatle" value="P" /> Paul</label>
|
||||
>>> print r[0]
|
||||
|
0
tests/regressiontests/model_fields/__init__.py
Normal file
0
tests/regressiontests/model_fields/__init__.py
Normal file
0
tests/regressiontests/model_fields/models.py
Normal file
0
tests/regressiontests/model_fields/models.py
Normal file
18
tests/regressiontests/model_fields/tests.py
Normal file
18
tests/regressiontests/model_fields/tests.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""
|
||||
>>> from django.db.models.fields import *
|
||||
|
||||
# DecimalField
|
||||
|
||||
>>> f = DecimalField()
|
||||
|
||||
>>> f.to_python(3)
|
||||
Decimal("3")
|
||||
|
||||
>>> f.to_python("3.14")
|
||||
Decimal("3.14")
|
||||
|
||||
>>> f.to_python("abc")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This value must be a decimal number.']
|
||||
"""
|
@ -6,13 +6,17 @@ if __name__ == '__main__':
|
||||
# before importing 'template'.
|
||||
settings.configure()
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django import template
|
||||
from django.template import loader
|
||||
from django.template.loaders import app_directories, filesystem
|
||||
from django.utils.translation import activate, deactivate, install, ugettext as _
|
||||
from django.utils.tzinfo import LocalTimezone
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from unicode import unicode_tests
|
||||
import unittest
|
||||
|
||||
# Some other tests we would like to run
|
||||
__test__ = {
|
||||
@ -75,6 +79,46 @@ class UTF8Class:
|
||||
return u'ŠĐĆŽćžšđ'.encode('utf-8')
|
||||
|
||||
class Templates(unittest.TestCase):
|
||||
def test_loaders_security(self):
|
||||
def test_template_sources(path, template_dirs, expected_sources):
|
||||
# Fix expected sources so they are normcased and abspathed
|
||||
expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
|
||||
# Test app_directories loader
|
||||
sources = app_directories.get_template_sources(path, template_dirs)
|
||||
self.assertEqual(list(sources), expected_sources)
|
||||
# Test filesystem loader
|
||||
sources = filesystem.get_template_sources(path, template_dirs)
|
||||
self.assertEqual(list(sources), expected_sources)
|
||||
|
||||
template_dirs = ['/dir1', '/dir2']
|
||||
test_template_sources('index.html', template_dirs,
|
||||
['/dir1/index.html', '/dir2/index.html'])
|
||||
test_template_sources('/etc/passwd', template_dirs,
|
||||
[])
|
||||
test_template_sources('etc/passwd', template_dirs,
|
||||
['/dir1/etc/passwd', '/dir2/etc/passwd'])
|
||||
test_template_sources('../etc/passwd', template_dirs,
|
||||
[])
|
||||
test_template_sources('../../../etc/passwd', template_dirs,
|
||||
[])
|
||||
test_template_sources('/dir1/index.html', template_dirs,
|
||||
['/dir1/index.html'])
|
||||
test_template_sources('../dir2/index.html', template_dirs,
|
||||
['/dir2/index.html'])
|
||||
test_template_sources('/dir1blah', template_dirs,
|
||||
[])
|
||||
test_template_sources('../dir1blah', template_dirs,
|
||||
[])
|
||||
|
||||
# Case insensitive tests (for win32). Not run unless we're on
|
||||
# a case insensitive operating system.
|
||||
if os.path.normcase('/TEST') == os.path.normpath('/test'):
|
||||
template_dirs = ['/dir1', '/DIR2']
|
||||
test_template_sources('index.html', template_dirs,
|
||||
['/dir1/index.html', '/dir2/index.html'])
|
||||
test_template_sources('/DIR1/index.HTML', template_dirs,
|
||||
['/dir1/index.html'])
|
||||
|
||||
def test_templates(self):
|
||||
# NOW and NOW_tz are used by timesince tag tests.
|
||||
NOW = datetime.now()
|
||||
|
@ -11,11 +11,22 @@ class AssertContainsTests(TestCase):
|
||||
"Responses can be inspected for content, including counting repeated substrings"
|
||||
response = self.client.get('/test_client_regress/no_template_view/')
|
||||
|
||||
self.assertContains(response, 'never', 0)
|
||||
self.assertContains(response, 'once')
|
||||
self.assertContains(response, 'once', 1)
|
||||
self.assertContains(response, 'twice')
|
||||
self.assertContains(response, 'twice', 2)
|
||||
|
||||
try:
|
||||
self.assertContains(response, 'never', 1)
|
||||
except AssertionError, e:
|
||||
self.assertEquals(str(e), "Found 0 instances of 'never' in response (expected 1)")
|
||||
|
||||
try:
|
||||
self.assertContains(response, 'once', 0)
|
||||
except AssertionError, e:
|
||||
self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 0)")
|
||||
|
||||
try:
|
||||
self.assertContains(response, 'once', 2)
|
||||
except AssertionError, e:
|
||||
|
@ -73,7 +73,7 @@ class InvalidModelTestCase(unittest.TestCase):
|
||||
self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
|
||||
self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
|
||||
|
||||
def django_tests(verbosity, tests_to_run):
|
||||
def django_tests(verbosity, interactive, test_labels):
|
||||
from django.conf import settings
|
||||
|
||||
old_installed_apps = settings.INSTALLED_APPS
|
||||
@ -109,14 +109,13 @@ def django_tests(verbosity, tests_to_run):
|
||||
# if the model was named on the command line, or
|
||||
# no models were named (i.e., run all), import
|
||||
# this model and add it to the list to test.
|
||||
if not tests_to_run or model_name in tests_to_run:
|
||||
if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]):
|
||||
if verbosity >= 1:
|
||||
print "Importing model %s" % model_name
|
||||
mod = load_app(model_label)
|
||||
if mod:
|
||||
if model_label not in settings.INSTALLED_APPS:
|
||||
settings.INSTALLED_APPS.append(model_label)
|
||||
test_models.append(mod)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
|
||||
continue
|
||||
@ -125,12 +124,12 @@ def django_tests(verbosity, tests_to_run):
|
||||
extra_tests = []
|
||||
for model_dir, model_name in get_invalid_models():
|
||||
model_label = '.'.join([model_dir, model_name])
|
||||
if not tests_to_run or model_name in tests_to_run:
|
||||
if not test_labels or model_name in test_labels:
|
||||
extra_tests.append(InvalidModelTestCase(model_label))
|
||||
|
||||
# Run the test suite, including the extra validation tests.
|
||||
from django.test.simple import run_tests
|
||||
failures = run_tests(test_models, verbosity, extra_tests=extra_tests)
|
||||
failures = run_tests(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
|
||||
if failures:
|
||||
sys.exit(failures)
|
||||
|
||||
@ -149,6 +148,8 @@ if __name__ == "__main__":
|
||||
parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0',
|
||||
type='choice', choices=['0', '1', '2'],
|
||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
||||
parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
|
||||
help='Tells Django to NOT prompt the user for input of any kind.')
|
||||
parser.add_option('--settings',
|
||||
help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
|
||||
options, args = parser.parse_args()
|
||||
@ -157,4 +158,4 @@ if __name__ == "__main__":
|
||||
elif "DJANGO_SETTINGS_MODULE" not in os.environ:
|
||||
parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
|
||||
"Set it or use --settings.")
|
||||
django_tests(int(options.verbosity), args)
|
||||
django_tests(int(options.verbosity), options.interactive, args)
|
||||
|
Loading…
x
Reference in New Issue
Block a user