mirror of
https://github.com/django/django.git
synced 2025-07-05 02:09:13 +00:00
[soc2009/multidb] Merged up to trunk r11205
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11209 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
f73cbaea67
commit
94e002c6e4
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,9 @@ from django.contrib.gis.gdal.prototypes.errcheck import \
|
|||||||
check_arg_errcode, check_errcode, check_geom, check_geom_offset, \
|
check_arg_errcode, check_errcode, check_geom, check_geom_offset, \
|
||||||
check_pointer, check_srs, check_str_arg, check_string, check_const_string
|
check_pointer, check_srs, check_str_arg, check_string, check_const_string
|
||||||
|
|
||||||
|
class gdal_char_p(c_char_p):
|
||||||
|
pass
|
||||||
|
|
||||||
def double_output(func, argtypes, errcheck=False, strarg=False):
|
def double_output(func, argtypes, errcheck=False, strarg=False):
|
||||||
"Generates a ctypes function that returns a double value."
|
"Generates a ctypes function that returns a double value."
|
||||||
func.argtypes = argtypes
|
func.argtypes = argtypes
|
||||||
@ -77,9 +80,9 @@ def string_output(func, argtypes, offset=-1, str_result=False):
|
|||||||
"""
|
"""
|
||||||
func.argtypes = argtypes
|
func.argtypes = argtypes
|
||||||
if str_result:
|
if str_result:
|
||||||
# String is the result, don't explicitly define
|
# Use subclass of c_char_p so the error checking routine
|
||||||
# the argument type so we can get the pointer.
|
# can free the memory at the pointer's address.
|
||||||
pass
|
func.restype = gdal_char_p
|
||||||
else:
|
else:
|
||||||
# Error code is returned
|
# Error code is returned
|
||||||
func.restype = c_int
|
func.restype = c_int
|
||||||
|
@ -10,6 +10,7 @@ __all__ = ['geos_boundary', 'geos_buffer', 'geos_centroid', 'geos_convexhull',
|
|||||||
from ctypes import c_char_p, c_double, c_int
|
from ctypes import c_char_p, c_double, c_int
|
||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR, GEOS_PREPARE
|
from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR, GEOS_PREPARE
|
||||||
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string
|
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string
|
||||||
|
from django.contrib.gis.geos.prototypes.geom import geos_char_p
|
||||||
|
|
||||||
def topology(func, *args):
|
def topology(func, *args):
|
||||||
"For GEOS unary topology functions."
|
"For GEOS unary topology functions."
|
||||||
@ -38,6 +39,7 @@ geos_union = topology(lgeos.GEOSUnion, GEOM_PTR)
|
|||||||
# GEOSRelate returns a string, not a geometry.
|
# GEOSRelate returns a string, not a geometry.
|
||||||
geos_relate = lgeos.GEOSRelate
|
geos_relate = lgeos.GEOSRelate
|
||||||
geos_relate.argtypes = [GEOM_PTR, GEOM_PTR]
|
geos_relate.argtypes = [GEOM_PTR, GEOM_PTR]
|
||||||
|
geos_relate.restype = geos_char_p
|
||||||
geos_relate.errcheck = check_string
|
geos_relate.errcheck = check_string
|
||||||
|
|
||||||
# Routines only in GEOS 3.1+
|
# Routines only in GEOS 3.1+
|
||||||
|
@ -185,7 +185,11 @@ class RegexURLResolver(object):
|
|||||||
try:
|
try:
|
||||||
sub_match = pattern.resolve(new_path)
|
sub_match = pattern.resolve(new_path)
|
||||||
except Resolver404, e:
|
except Resolver404, e:
|
||||||
tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['tried']])
|
sub_tried = e.args[0].get('tried')
|
||||||
|
if sub_tried is not None:
|
||||||
|
tried.extend([(pattern.regex.pattern + ' ' + t) for t in sub_tried])
|
||||||
|
else:
|
||||||
|
tried.append(pattern.regex.pattern)
|
||||||
else:
|
else:
|
||||||
if sub_match:
|
if sub_match:
|
||||||
sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
|
sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
|
||||||
@ -195,7 +199,7 @@ class RegexURLResolver(object):
|
|||||||
return sub_match[0], sub_match[1], sub_match_dict
|
return sub_match[0], sub_match[1], sub_match_dict
|
||||||
tried.append(pattern.regex.pattern)
|
tried.append(pattern.regex.pattern)
|
||||||
raise Resolver404, {'tried': tried, 'path': new_path}
|
raise Resolver404, {'tried': tried, 'path': new_path}
|
||||||
raise Resolver404, {'tried': [], 'path' : path}
|
raise Resolver404, {'path' : path}
|
||||||
|
|
||||||
def _get_urlconf_module(self):
|
def _get_urlconf_module(self):
|
||||||
try:
|
try:
|
||||||
@ -286,4 +290,3 @@ def get_script_prefix():
|
|||||||
instance is normally going to be a lot cleaner).
|
instance is normally going to be a lot cleaner).
|
||||||
"""
|
"""
|
||||||
return _prefixes.get(currentThread(), u'/')
|
return _prefixes.get(currentThread(), u'/')
|
||||||
|
|
||||||
|
@ -464,8 +464,21 @@ class BaseModelFormSet(BaseFormSet):
|
|||||||
return len(self.get_queryset())
|
return len(self.get_queryset())
|
||||||
return super(BaseModelFormSet, self).initial_form_count()
|
return super(BaseModelFormSet, self).initial_form_count()
|
||||||
|
|
||||||
|
def _existing_object(self, pk):
|
||||||
|
if not hasattr(self, '_object_dict'):
|
||||||
|
self._object_dict = dict([(o.pk, o) for o in self.get_queryset()])
|
||||||
|
return self._object_dict.get(pk)
|
||||||
|
|
||||||
def _construct_form(self, i, **kwargs):
|
def _construct_form(self, i, **kwargs):
|
||||||
if i < self.initial_form_count():
|
if self.is_bound and i < self.initial_form_count():
|
||||||
|
pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
|
||||||
|
pk = self.data[pk_key]
|
||||||
|
pk_field = self.model._meta.pk
|
||||||
|
pk = pk_field.get_db_prep_lookup('exact', pk)
|
||||||
|
if isinstance(pk, list):
|
||||||
|
pk = pk[0]
|
||||||
|
kwargs['instance'] = self._existing_object(pk)
|
||||||
|
if i < self.initial_form_count() and not kwargs.get('instance'):
|
||||||
kwargs['instance'] = self.get_queryset()[i]
|
kwargs['instance'] = self.get_queryset()[i]
|
||||||
return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
|
return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
|
||||||
|
|
||||||
@ -604,10 +617,6 @@ class BaseModelFormSet(BaseFormSet):
|
|||||||
if not self.get_queryset():
|
if not self.get_queryset():
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
|
|
||||||
existing_objects = {}
|
|
||||||
for obj in self.get_queryset():
|
|
||||||
existing_objects[obj.pk] = obj
|
|
||||||
saved_instances = []
|
saved_instances = []
|
||||||
for form in self.initial_forms:
|
for form in self.initial_forms:
|
||||||
pk_name = self._pk_field.name
|
pk_name = self._pk_field.name
|
||||||
@ -618,7 +627,7 @@ class BaseModelFormSet(BaseFormSet):
|
|||||||
pk_value = form.fields[pk_name].clean(raw_pk_value)
|
pk_value = form.fields[pk_name].clean(raw_pk_value)
|
||||||
pk_value = getattr(pk_value, 'pk', pk_value)
|
pk_value = getattr(pk_value, 'pk', pk_value)
|
||||||
|
|
||||||
obj = existing_objects[pk_value]
|
obj = self._existing_object(pk_value)
|
||||||
if self.can_delete:
|
if self.can_delete:
|
||||||
raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
|
raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
|
||||||
should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
|
should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
|
||||||
@ -663,10 +672,13 @@ class BaseModelFormSet(BaseFormSet):
|
|||||||
return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
|
return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
|
||||||
or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
|
or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
|
||||||
if pk_is_not_editable(pk) or pk.name not in form.fields:
|
if pk_is_not_editable(pk) or pk.name not in form.fields:
|
||||||
try:
|
if form.is_bound:
|
||||||
pk_value = self.get_queryset()[index].pk
|
pk_value = form.instance.pk
|
||||||
except IndexError:
|
else:
|
||||||
pk_value = None
|
try:
|
||||||
|
pk_value = self.get_queryset()[index].pk
|
||||||
|
except IndexError:
|
||||||
|
pk_value = None
|
||||||
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
|
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
|
||||||
qs = pk.rel.to._default_manager.get_query_set()
|
qs = pk.rel.to._default_manager.get_query_set()
|
||||||
else:
|
else:
|
||||||
|
@ -564,7 +564,7 @@ do_filter = register.tag("filter", do_filter)
|
|||||||
#@register.tag
|
#@register.tag
|
||||||
def firstof(parser, token):
|
def firstof(parser, token):
|
||||||
"""
|
"""
|
||||||
Outputs the first variable passed that is not False.
|
Outputs the first variable passed that is not False, without escaping.
|
||||||
|
|
||||||
Outputs nothing if all the passed variables are False.
|
Outputs nothing if all the passed variables are False.
|
||||||
|
|
||||||
@ -575,11 +575,11 @@ def firstof(parser, token):
|
|||||||
This is equivalent to::
|
This is equivalent to::
|
||||||
|
|
||||||
{% if var1 %}
|
{% if var1 %}
|
||||||
{{ var1 }}
|
{{ var1|safe }}
|
||||||
{% else %}{% if var2 %}
|
{% else %}{% if var2 %}
|
||||||
{{ var2 }}
|
{{ var2|safe }}
|
||||||
{% else %}{% if var3 %}
|
{% else %}{% if var3 %}
|
||||||
{{ var3 }}
|
{{ var3|safe }}
|
||||||
{% endif %}{% endif %}{% endif %}
|
{% endif %}{% endif %}{% endif %}
|
||||||
|
|
||||||
but obviously much cleaner!
|
but obviously much cleaner!
|
||||||
@ -589,6 +589,12 @@ def firstof(parser, token):
|
|||||||
|
|
||||||
{% firstof var1 var2 var3 "fallback value" %}
|
{% firstof var1 var2 var3 "fallback value" %}
|
||||||
|
|
||||||
|
If you want to escape the output, use a filter tag::
|
||||||
|
|
||||||
|
{% filter force_escape %}
|
||||||
|
{% firstof var1 var2 var3 "fallback value" %}
|
||||||
|
{% endfilter %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
bits = token.split_contents()[1:]
|
bits = token.split_contents()[1:]
|
||||||
if len(bits) < 1:
|
if len(bits) < 1:
|
||||||
|
@ -6,10 +6,16 @@ import docutils.nodes
|
|||||||
import docutils.transforms
|
import docutils.transforms
|
||||||
import sphinx
|
import sphinx
|
||||||
import sphinx.addnodes
|
import sphinx.addnodes
|
||||||
import sphinx.builder
|
try:
|
||||||
|
from sphinx import builders
|
||||||
|
except ImportError:
|
||||||
|
import sphinx.builder as builders
|
||||||
import sphinx.directives
|
import sphinx.directives
|
||||||
import sphinx.environment
|
import sphinx.environment
|
||||||
import sphinx.htmlwriter
|
try:
|
||||||
|
import sphinx.writers.html as sphinx_htmlwriter
|
||||||
|
except ImportError:
|
||||||
|
import sphinx.htmlwriter as sphinx_htmlwriter
|
||||||
import sphinx.roles
|
import sphinx.roles
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
|
|
||||||
@ -44,7 +50,7 @@ def setup(app):
|
|||||||
directivename = "django-admin-option",
|
directivename = "django-admin-option",
|
||||||
rolename = "djadminopt",
|
rolename = "djadminopt",
|
||||||
indextemplate = "pair: %s; django-admin command-line option",
|
indextemplate = "pair: %s; django-admin command-line option",
|
||||||
parse_node = lambda env, sig, signode: sphinx.directives.parse_option_desc(signode, sig),
|
parse_node = parse_django_adminopt_node,
|
||||||
)
|
)
|
||||||
app.add_config_value('django_next_version', '0.0', True)
|
app.add_config_value('django_next_version', '0.0', True)
|
||||||
app.add_directive('versionadded', parse_version_directive, 1, (1, 1, 1))
|
app.add_directive('versionadded', parse_version_directive, 1, (1, 1, 1))
|
||||||
@ -102,7 +108,7 @@ class SuppressBlockquotes(docutils.transforms.Transform):
|
|||||||
if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
|
if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
|
||||||
node.replace_self(node.children[0])
|
node.replace_self(node.children[0])
|
||||||
|
|
||||||
class DjangoHTMLTranslator(sphinx.htmlwriter.SmartyPantsHTMLTranslator):
|
class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator):
|
||||||
"""
|
"""
|
||||||
Django-specific reST to HTML tweaks.
|
Django-specific reST to HTML tweaks.
|
||||||
"""
|
"""
|
||||||
@ -125,10 +131,10 @@ class DjangoHTMLTranslator(sphinx.htmlwriter.SmartyPantsHTMLTranslator):
|
|||||||
#
|
#
|
||||||
def visit_literal_block(self, node):
|
def visit_literal_block(self, node):
|
||||||
self.no_smarty += 1
|
self.no_smarty += 1
|
||||||
sphinx.htmlwriter.SmartyPantsHTMLTranslator.visit_literal_block(self, node)
|
sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_literal_block(self, node)
|
||||||
|
|
||||||
def depart_literal_block(self, node):
|
def depart_literal_block(self, node):
|
||||||
sphinx.htmlwriter.SmartyPantsHTMLTranslator.depart_literal_block(self, node)
|
sphinx_htmlwriter.SmartyPantsHTMLTranslator.depart_literal_block(self, node)
|
||||||
self.no_smarty -= 1
|
self.no_smarty -= 1
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -162,7 +168,7 @@ class DjangoHTMLTranslator(sphinx.htmlwriter.SmartyPantsHTMLTranslator):
|
|||||||
# Give each section a unique ID -- nice for custom CSS hooks
|
# Give each section a unique ID -- nice for custom CSS hooks
|
||||||
# This is different on docutils 0.5 vs. 0.4...
|
# This is different on docutils 0.5 vs. 0.4...
|
||||||
|
|
||||||
if hasattr(sphinx.htmlwriter.SmartyPantsHTMLTranslator, 'start_tag_with_title') and sphinx.__version__ == '0.4.2':
|
if hasattr(sphinx_htmlwriter.SmartyPantsHTMLTranslator, 'start_tag_with_title') and sphinx.__version__ == '0.4.2':
|
||||||
def start_tag_with_title(self, node, tagname, **atts):
|
def start_tag_with_title(self, node, tagname, **atts):
|
||||||
node = {
|
node = {
|
||||||
'classes': node.get('classes', []),
|
'classes': node.get('classes', []),
|
||||||
@ -176,7 +182,7 @@ class DjangoHTMLTranslator(sphinx.htmlwriter.SmartyPantsHTMLTranslator):
|
|||||||
node['ids'] = ['s-' + i for i in old_ids]
|
node['ids'] = ['s-' + i for i in old_ids]
|
||||||
if sphinx.__version__ != '0.4.2':
|
if sphinx.__version__ != '0.4.2':
|
||||||
node['ids'].extend(old_ids)
|
node['ids'].extend(old_ids)
|
||||||
sphinx.htmlwriter.SmartyPantsHTMLTranslator.visit_section(self, node)
|
sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_section(self, node)
|
||||||
node['ids'] = old_ids
|
node['ids'] = old_ids
|
||||||
|
|
||||||
def parse_django_admin_node(env, sig, signode):
|
def parse_django_admin_node(env, sig, signode):
|
||||||
@ -186,6 +192,25 @@ def parse_django_admin_node(env, sig, signode):
|
|||||||
signode += sphinx.addnodes.desc_name(title, title)
|
signode += sphinx.addnodes.desc_name(title, title)
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
|
def parse_django_adminopt_node(env, sig, signode):
|
||||||
|
"""A copy of sphinx.directives.CmdoptionDesc.parse_signature()"""
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.directives.desc import option_desc_re
|
||||||
|
count = 0
|
||||||
|
firstname = ''
|
||||||
|
for m in option_desc_re.finditer(sig):
|
||||||
|
optname, args = m.groups()
|
||||||
|
if count:
|
||||||
|
signode += addnodes.desc_addname(', ', ', ')
|
||||||
|
signode += addnodes.desc_name(optname, optname)
|
||||||
|
signode += addnodes.desc_addname(args, args)
|
||||||
|
if not count:
|
||||||
|
firstname = optname
|
||||||
|
count += 1
|
||||||
|
if not firstname:
|
||||||
|
raise ValueError
|
||||||
|
return firstname
|
||||||
|
|
||||||
def monkeypatch_pickle_builder():
|
def monkeypatch_pickle_builder():
|
||||||
import shutil
|
import shutil
|
||||||
from os import path
|
from os import path
|
||||||
@ -214,12 +239,12 @@ def monkeypatch_pickle_builder():
|
|||||||
|
|
||||||
# copy the environment file from the doctree dir to the output dir
|
# copy the environment file from the doctree dir to the output dir
|
||||||
# as needed by the web app
|
# as needed by the web app
|
||||||
shutil.copyfile(path.join(self.doctreedir, sphinx.builder.ENV_PICKLE_FILENAME),
|
shutil.copyfile(path.join(self.doctreedir, builders.ENV_PICKLE_FILENAME),
|
||||||
path.join(self.outdir, sphinx.builder.ENV_PICKLE_FILENAME))
|
path.join(self.outdir, builders.ENV_PICKLE_FILENAME))
|
||||||
|
|
||||||
# touch 'last build' file, used by the web application to determine
|
# touch 'last build' file, used by the web application to determine
|
||||||
# when to reload its environment and clear the cache
|
# when to reload its environment and clear the cache
|
||||||
open(path.join(self.outdir, sphinx.builder.LAST_BUILD_FILENAME), 'w').close()
|
open(path.join(self.outdir, builders.LAST_BUILD_FILENAME), 'w').close()
|
||||||
|
|
||||||
|
builders.PickleHTMLBuilder.handle_finish = handle_finish
|
||||||
|
|
||||||
sphinx.builder.PickleHTMLBuilder.handle_finish = handle_finish
|
|
||||||
|
|
||||||
|
2
docs/_templates/layout.html
vendored
2
docs/_templates/layout.html
vendored
@ -1,6 +1,6 @@
|
|||||||
{% extends "!layout.html" %}
|
{% extends "!layout.html" %}
|
||||||
|
|
||||||
{%- macro secondnav %}
|
{%- macro secondnav() %}
|
||||||
{%- if prev %}
|
{%- if prev %}
|
||||||
« <a href="{{ prev.link|e }}" title="{{ prev.title|e }}">previous</a>
|
« <a href="{{ prev.link|e }}" title="{{ prev.title|e }}">previous</a>
|
||||||
{{ reldelim2 }}
|
{{ reldelim2 }}
|
||||||
|
@ -37,20 +37,19 @@ Set the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY` setting to ``True``. See the
|
|||||||
How do I automatically set a field's value to the user who last edited the object in the admin?
|
How do I automatically set a field's value to the user who last edited the object in the admin?
|
||||||
-----------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
At this point, Django doesn't have an official way to do this. But it's an oft-requested
|
The :class:`ModelAdmin` class provides customization hooks that allow you to transform
|
||||||
feature, so we're discussing how it can be implemented. The problem is we don't want to couple
|
an object as it saved, using details from the request. By extracting the current user
|
||||||
the model layer with the admin layer with the request layer (to get the current user). It's a
|
from the request, and customizing the :meth:`ModelAdmin.save_model` hook, you can update
|
||||||
tricky problem.
|
an object to reflect the user that edited it. See :ref:`the documentation on ModelAdmin
|
||||||
|
methods <model-admin-methods>` for an example.
|
||||||
One person hacked up a `solution that doesn't require patching Django`_, but note that it's an
|
|
||||||
unofficial solution, and there's no guarantee it won't break at some point.
|
|
||||||
|
|
||||||
.. _solution that doesn't require patching Django: http://lukeplant.me.uk/blog.php?id=1107301634
|
|
||||||
|
|
||||||
How do I limit admin access so that objects can only be edited by the users who created them?
|
How do I limit admin access so that objects can only be edited by the users who created them?
|
||||||
---------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
See the answer to the previous question.
|
The :class:`ModelAdmin` class also provides customization hooks that allow you to control the
|
||||||
|
visibility and editability of objects in the admin. Using the same trick of extracting the
|
||||||
|
user from the request, the :meth:`ModelAdmin.queryset` and :meth:`ModelAdmin.has_change_permission`
|
||||||
|
can be used to control the visibility and editability of objects in the admin.
|
||||||
|
|
||||||
My admin-site CSS and images showed up fine using the development server, but they're not displaying when using mod_python.
|
My admin-site CSS and images showed up fine using the development server, but they're not displaying when using mod_python.
|
||||||
---------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -6,8 +6,8 @@ How to use Django with Apache and mod_python
|
|||||||
|
|
||||||
.. highlight:: apache
|
.. highlight:: apache
|
||||||
|
|
||||||
The `mod_python`_ module for Apache_ can be used to deploy Django to a
|
The `mod_python`_ module for Apache_ can be used to deploy Django to a
|
||||||
production server, although it has been mostly superseded by the simpler
|
production server, although it has been mostly superseded by the simpler
|
||||||
:ref:`mod_wsgi deployment option <howto-deployment-modwsgi>`.
|
:ref:`mod_wsgi deployment option <howto-deployment-modwsgi>`.
|
||||||
|
|
||||||
mod_python is similar to (and inspired by) `mod_perl`_ : It embeds Python within
|
mod_python is similar to (and inspired by) `mod_perl`_ : It embeds Python within
|
||||||
@ -378,3 +378,24 @@ as necessary.
|
|||||||
.. _Expat Causing Apache Crash: http://www.dscpl.com.au/articles/modpython-006.html
|
.. _Expat Causing Apache Crash: http://www.dscpl.com.au/articles/modpython-006.html
|
||||||
.. _mod_python FAQ entry: http://modpython.org/FAQ/faqw.py?req=show&file=faq02.013.htp
|
.. _mod_python FAQ entry: http://modpython.org/FAQ/faqw.py?req=show&file=faq02.013.htp
|
||||||
.. _Getting mod_python Working: http://www.dscpl.com.au/articles/modpython-001.html
|
.. _Getting mod_python Working: http://www.dscpl.com.au/articles/modpython-001.html
|
||||||
|
|
||||||
|
If you get a UnicodeEncodeError
|
||||||
|
===============================
|
||||||
|
|
||||||
|
If you're taking advantage of the internationalization features of Django (see
|
||||||
|
:ref:`topics-i18n`) and you intend to allow users to upload files, you must
|
||||||
|
ensure that the environment used to start Apache is configured to accept
|
||||||
|
non-ASCII file names. If your environment is not correctly configured, you
|
||||||
|
will trigger ``UnicodeEncodeError`` exceptions when calling functions like
|
||||||
|
``os.path()`` on filenames that contain non-ASCII characters.
|
||||||
|
|
||||||
|
To avoid these problems, the environment used to start Apache should contain
|
||||||
|
settings analogous to the following::
|
||||||
|
|
||||||
|
export LANG='en_US.UTF-8'
|
||||||
|
export LC_ALL='en_US.UTF-8'
|
||||||
|
|
||||||
|
Consult the documentation for your operating system for the appropriate syntax
|
||||||
|
and location to put these configuration items; ``/etc/apache2/envvars`` is a
|
||||||
|
common location on Unix platforms. Once you have added these statements
|
||||||
|
to your environment, restart Apache.
|
||||||
|
@ -704,6 +704,8 @@ objects. Templates can override or extend base admin templates as described in
|
|||||||
If you don't specify this attribute, a default template shipped with Django
|
If you don't specify this attribute, a default template shipped with Django
|
||||||
that provides the standard appearance is used.
|
that provides the standard appearance is used.
|
||||||
|
|
||||||
|
.. _model-admin-methods:
|
||||||
|
|
||||||
``ModelAdmin`` methods
|
``ModelAdmin`` methods
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -792,7 +794,7 @@ return a subset of objects for this foreign key field based on the user::
|
|||||||
class MyModelAdmin(admin.ModelAdmin):
|
class MyModelAdmin(admin.ModelAdmin):
|
||||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
if db_field.name == "car":
|
if db_field.name == "car":
|
||||||
kwargs["queryset"] = Car.object.filter(owner=request.user)
|
kwargs["queryset"] = Car.objects.filter(owner=request.user)
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
|
return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ bytestrings (which shouldn't be too difficult) is the recommended solution.
|
|||||||
Should you decide to use ``utf8_bin`` collation for some of your tables with
|
Should you decide to use ``utf8_bin`` collation for some of your tables with
|
||||||
MySQLdb 1.2.1p2, you should still use ``utf8_collation_ci_swedish`` (the
|
MySQLdb 1.2.1p2, you should still use ``utf8_collation_ci_swedish`` (the
|
||||||
default) collation for the :class:`django.contrib.sessions.models.Session`
|
default) collation for the :class:`django.contrib.sessions.models.Session`
|
||||||
table (usually called ``django_session`` and the table
|
table (usually called ``django_session``) and the
|
||||||
:class:`django.contrib.admin.models.LogEntry` table (usually called
|
:class:`django.contrib.admin.models.LogEntry` table (usually called
|
||||||
``django_admin_log``). Those are the two standard tables that use
|
``django_admin_log``). Those are the two standard tables that use
|
||||||
:class:`~django.db.model.TextField` internally.
|
:class:`~django.db.model.TextField` internally.
|
||||||
|
@ -101,6 +101,14 @@ You can use any number of values in a ``{% cycle %}`` tag, separated by spaces.
|
|||||||
Values enclosed in single (``'``) or double quotes (``"``) are treated as
|
Values enclosed in single (``'``) or double quotes (``"``) are treated as
|
||||||
string literals, while values without quotes are treated as template variables.
|
string literals, while values without quotes are treated as template variables.
|
||||||
|
|
||||||
|
Note that the variables included in the cycle will not be escaped. This is
|
||||||
|
because template tags do not escape their content. If you want to escape the
|
||||||
|
variables in the cycle, you must do so explicitly::
|
||||||
|
|
||||||
|
{% filter force_escape %}
|
||||||
|
{% cycle var1 var2 var3 %}
|
||||||
|
{% endfilter %}
|
||||||
|
|
||||||
For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
|
For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
|
||||||
old syntax from previous Django versions. You shouldn't use this in any new
|
old syntax from previous Django versions. You shouldn't use this in any new
|
||||||
projects, but for the sake of the people who are still using it, here's what it
|
projects, but for the sake of the people who are still using it, here's what it
|
||||||
@ -160,8 +168,9 @@ Sample usage::
|
|||||||
firstof
|
firstof
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
Outputs the first variable passed that is not False. Outputs nothing if all the
|
Outputs the first variable passed that is not False, without escaping.
|
||||||
passed variables are False.
|
|
||||||
|
Outputs nothing if all the passed variables are False.
|
||||||
|
|
||||||
Sample usage::
|
Sample usage::
|
||||||
|
|
||||||
@ -170,11 +179,11 @@ Sample usage::
|
|||||||
This is equivalent to::
|
This is equivalent to::
|
||||||
|
|
||||||
{% if var1 %}
|
{% if var1 %}
|
||||||
{{ var1 }}
|
{{ var1|safe }}
|
||||||
{% else %}{% if var2 %}
|
{% else %}{% if var2 %}
|
||||||
{{ var2 }}
|
{{ var2|safe }}
|
||||||
{% else %}{% if var3 %}
|
{% else %}{% if var3 %}
|
||||||
{{ var3 }}
|
{{ var3|safe }}
|
||||||
{% endif %}{% endif %}{% endif %}
|
{% endif %}{% endif %}{% endif %}
|
||||||
|
|
||||||
You can also use a literal string as a fallback value in case all
|
You can also use a literal string as a fallback value in case all
|
||||||
@ -182,6 +191,14 @@ passed variables are False::
|
|||||||
|
|
||||||
{% firstof var1 var2 var3 "fallback value" %}
|
{% firstof var1 var2 var3 "fallback value" %}
|
||||||
|
|
||||||
|
Note that the variables included in the firstof tag will not be escaped. This
|
||||||
|
is because template tags do not escape their content. If you want to escape
|
||||||
|
the variables in the firstof tag, you must do so explicitly::
|
||||||
|
|
||||||
|
{% filter force_escape %}
|
||||||
|
{% firstof var1 var2 var3 "fallback value" %}
|
||||||
|
{% endfilter %}
|
||||||
|
|
||||||
.. templatetag:: for
|
.. templatetag:: for
|
||||||
|
|
||||||
for
|
for
|
||||||
|
@ -223,7 +223,19 @@ Pluralization
|
|||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Use the function ``django.utils.translation.ungettext()`` to specify pluralized
|
Use the function ``django.utils.translation.ungettext()`` to specify pluralized
|
||||||
messages. Example::
|
messages.
|
||||||
|
|
||||||
|
``ungettext`` takes three arguments: the singular translation string, the plural
|
||||||
|
translation string and the number of objects.
|
||||||
|
|
||||||
|
This function is useful when your need you Django application to be localizable
|
||||||
|
to languages where the number and complexity of `plural forms
|
||||||
|
<http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms>`_ is
|
||||||
|
greater than the two forms used in English ('object' for the singular and
|
||||||
|
'objects' for all the cases where ``count`` is different from zero, irrespective
|
||||||
|
of its value.)
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ungettext
|
||||||
def hello_world(request, count):
|
def hello_world(request, count):
|
||||||
@ -232,9 +244,61 @@ messages. Example::
|
|||||||
}
|
}
|
||||||
return HttpResponse(page)
|
return HttpResponse(page)
|
||||||
|
|
||||||
``ungettext`` takes three arguments: the singular translation string, the plural
|
In this example the number of objects is passed to the translation languages as
|
||||||
translation string and the number of objects (which is passed to the
|
the ``count`` variable.
|
||||||
translation languages as the ``count`` variable).
|
|
||||||
|
Lets see a slightly more complex usage example::
|
||||||
|
|
||||||
|
from django.utils.translation import ungettext
|
||||||
|
|
||||||
|
count = Report.objects.count()
|
||||||
|
if count == 1:
|
||||||
|
name = Report._meta.verbose_name
|
||||||
|
else:
|
||||||
|
name = Report._meta.verbose_name_plural
|
||||||
|
|
||||||
|
text = ungettext(
|
||||||
|
'There is %(count)d %(name)s available.',
|
||||||
|
'There are %(count)d %(name)s available.',
|
||||||
|
count
|
||||||
|
) % {
|
||||||
|
'count': count,
|
||||||
|
'name': name
|
||||||
|
}
|
||||||
|
|
||||||
|
Here we reuse localizable, hopefully already translated literals (contained in
|
||||||
|
the ``verbose_name`` and ``verbose_name_plural`` model ``Meta`` options) for
|
||||||
|
other parts of the sentence so all of it is consistently based on the
|
||||||
|
cardinality of the elements at play.
|
||||||
|
|
||||||
|
.. _pluralization-var-notes:
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When using this technique, make sure you use a single name for every
|
||||||
|
extrapolated variable included in the literal. In the example above note how
|
||||||
|
we used the ``name`` Python variable in both translation strings. This
|
||||||
|
example would fail::
|
||||||
|
|
||||||
|
from django.utils.translation import ungettext
|
||||||
|
from myapp.models import Report
|
||||||
|
|
||||||
|
count = Report.objects.count()
|
||||||
|
d = {
|
||||||
|
'count': count,
|
||||||
|
'name': Report._meta.verbose_name
|
||||||
|
'plural_name': Report._meta.verbose_name_plural
|
||||||
|
}
|
||||||
|
text = ungettext(
|
||||||
|
'There is %(count)d %(name)s available.',
|
||||||
|
'There are %(count)d %(plural_name)s available.',
|
||||||
|
count
|
||||||
|
) % d
|
||||||
|
|
||||||
|
You would get a ``a format specification for argument 'name', as in
|
||||||
|
'msgstr[0]', doesn't exist in 'msgid'`` error when running
|
||||||
|
``django-admin.py compilemessages`` or a ``KeyError`` Python exception at
|
||||||
|
runtime.
|
||||||
|
|
||||||
In template code
|
In template code
|
||||||
----------------
|
----------------
|
||||||
@ -257,6 +321,8 @@ content that will require translation in the future::
|
|||||||
|
|
||||||
<title>{% trans "myvar" noop %}</title>
|
<title>{% trans "myvar" noop %}</title>
|
||||||
|
|
||||||
|
Internally, inline translations use an ``ugettext`` call.
|
||||||
|
|
||||||
It's not possible to mix a template variable inside a string within ``{% trans
|
It's not possible to mix a template variable inside a string within ``{% trans
|
||||||
%}``. If your translations require strings with variables (placeholders), use
|
%}``. If your translations require strings with variables (placeholders), use
|
||||||
``{% blocktrans %}``::
|
``{% blocktrans %}``::
|
||||||
@ -288,8 +354,11 @@ To pluralize, specify both the singular and plural forms with the
|
|||||||
There are {{ counter }} {{ name }} objects.
|
There are {{ counter }} {{ name }} objects.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|
||||||
Internally, all block and inline translations use the appropriate
|
When you use the pluralization feature and bind additional values to local
|
||||||
``ugettext`` / ``ungettext`` call.
|
variables apart from the counter value that selects the translated literal to be
|
||||||
|
used, have in mind that the ``blocktrans`` construct is internally converted
|
||||||
|
to an ``ungettext`` call. This means the same :ref:`notes regarding ungettext
|
||||||
|
variables <pluralization-var-notes>` apply.
|
||||||
|
|
||||||
Each ``RequestContext`` has access to three translation-specific variables:
|
Each ``RequestContext`` has access to three translation-specific variables:
|
||||||
|
|
||||||
|
@ -479,7 +479,7 @@ arguments at time of construction:
|
|||||||
Once you have a ``Client`` instance, you can call any of the following
|
Once you have a ``Client`` instance, you can call any of the following
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
.. method:: Client.get(path, data={}, follow=False)
|
.. method:: Client.get(path, data={}, follow=False, **extra)
|
||||||
|
|
||||||
|
|
||||||
Makes a GET request on the provided ``path`` and returns a ``Response``
|
Makes a GET request on the provided ``path`` and returns a ``Response``
|
||||||
@ -495,6 +495,17 @@ arguments at time of construction:
|
|||||||
|
|
||||||
/customers/details/?name=fred&age=7
|
/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
|
The ``extra`` keyword arguments parameter can be used to specify
|
||||||
|
headers to be sent in the request. For example::
|
||||||
|
|
||||||
|
>>> c = Client()
|
||||||
|
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
|
||||||
|
... HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
|
||||||
|
...will send the HTTP header ``HTTP_X_REQUESTED_WITH`` to the
|
||||||
|
details view, which is a good way to test code paths that use the
|
||||||
|
:meth:`django.http.HttpRequest.is_ajax()` method.
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
If you already have the GET arguments in URL-encoded form, you can
|
If you already have the GET arguments in URL-encoded form, you can
|
||||||
@ -518,7 +529,7 @@ arguments at time of construction:
|
|||||||
>>> response.redirect_chain
|
>>> response.redirect_chain
|
||||||
[(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
|
[(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
|
||||||
|
|
||||||
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False)
|
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
|
||||||
|
|
||||||
Makes a POST request on the provided ``path`` and returns a
|
Makes a POST request on the provided ``path`` and returns a
|
||||||
``Response`` object, which is documented below.
|
``Response`` object, which is documented below.
|
||||||
@ -569,6 +580,8 @@ arguments at time of construction:
|
|||||||
Note that you should manually close the file after it has been provided
|
Note that you should manually close the file after it has been provided
|
||||||
to ``post()``.
|
to ``post()``.
|
||||||
|
|
||||||
|
The ``extra`` argument acts the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
.. versionchanged:: 1.1
|
||||||
|
|
||||||
If the URL you request with a POST contains encoded parameters, these
|
If the URL you request with a POST contains encoded parameters, these
|
||||||
@ -585,7 +598,7 @@ arguments at time of construction:
|
|||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
.. method:: Client.head(path, data={}, follow=False)
|
.. method:: Client.head(path, data={}, follow=False, **extra)
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
@ -597,7 +610,7 @@ arguments at time of construction:
|
|||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
.. method:: Client.options(path, data={}, follow=False)
|
.. method:: Client.options(path, data={}, follow=False, **extra)
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
@ -608,7 +621,9 @@ arguments at time of construction:
|
|||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
.. method:: Client.put(path, data={}, content_type=MULTIPART_CONTENT, follow=False)
|
The ``extra`` argument acts the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
|
.. method:: Client.put(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
@ -620,7 +635,7 @@ arguments at time of construction:
|
|||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
.. method:: Client.delete(path, follow=False)
|
.. method:: Client.delete(path, follow=False, **extra)
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
@ -631,6 +646,8 @@ arguments at time of construction:
|
|||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
|
The ``extra`` argument acts the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.login(**credentials)
|
.. method:: Client.login(**credentials)
|
||||||
|
|
||||||
.. versionadded:: 1.0
|
.. versionadded:: 1.0
|
||||||
|
@ -326,7 +326,6 @@ class GalleryAdmin(admin.ModelAdmin):
|
|||||||
class PictureAdmin(admin.ModelAdmin):
|
class PictureAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Language(models.Model):
|
class Language(models.Model):
|
||||||
iso = models.CharField(max_length=5, primary_key=True)
|
iso = models.CharField(max_length=5, primary_key=True)
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
@ -401,8 +400,25 @@ class WhatsitInline(admin.StackedInline):
|
|||||||
class FancyDoodadInline(admin.StackedInline):
|
class FancyDoodadInline(admin.StackedInline):
|
||||||
model = FancyDoodad
|
model = FancyDoodad
|
||||||
|
|
||||||
|
class Category(models.Model):
|
||||||
|
collector = models.ForeignKey(Collector)
|
||||||
|
order = models.PositiveIntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('order',)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u'%s:o%s' % (self.id, self.order)
|
||||||
|
|
||||||
|
class CategoryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('id', 'collector', 'order')
|
||||||
|
list_editable = ('order',)
|
||||||
|
|
||||||
|
class CategoryInline(admin.StackedInline):
|
||||||
|
model = Category
|
||||||
|
|
||||||
class CollectorAdmin(admin.ModelAdmin):
|
class CollectorAdmin(admin.ModelAdmin):
|
||||||
inlines = [WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, FancyDoodadInline]
|
inlines = [WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, FancyDoodadInline, CategoryInline]
|
||||||
|
|
||||||
admin.site.register(Article, ArticleAdmin)
|
admin.site.register(Article, ArticleAdmin)
|
||||||
admin.site.register(CustomArticle, CustomArticleAdmin)
|
admin.site.register(CustomArticle, CustomArticleAdmin)
|
||||||
@ -426,6 +442,7 @@ admin.site.register(Language, LanguageAdmin)
|
|||||||
admin.site.register(Recommendation, RecommendationAdmin)
|
admin.site.register(Recommendation, RecommendationAdmin)
|
||||||
admin.site.register(Recommender)
|
admin.site.register(Recommender)
|
||||||
admin.site.register(Collector, CollectorAdmin)
|
admin.site.register(Collector, CollectorAdmin)
|
||||||
|
admin.site.register(Category, CategoryAdmin)
|
||||||
|
|
||||||
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
|
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
|
||||||
# That way we cover all four cases:
|
# That way we cover all four cases:
|
||||||
|
@ -16,7 +16,8 @@ from django.utils.html import escape
|
|||||||
from models import Article, BarAccount, CustomArticle, EmptyModel, \
|
from models import Article, BarAccount, CustomArticle, EmptyModel, \
|
||||||
ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \
|
ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \
|
||||||
Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \
|
Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \
|
||||||
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit
|
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \
|
||||||
|
Category
|
||||||
|
|
||||||
try:
|
try:
|
||||||
set
|
set
|
||||||
@ -921,6 +922,45 @@ class AdminViewListEditable(TestCase):
|
|||||||
|
|
||||||
self.failUnlessEqual(Person.objects.get(name="John Mauchly").alive, False)
|
self.failUnlessEqual(Person.objects.get(name="John Mauchly").alive, False)
|
||||||
|
|
||||||
|
def test_list_editable_ordering(self):
|
||||||
|
collector = Collector.objects.create(id=1, name="Frederick Clegg")
|
||||||
|
|
||||||
|
Category.objects.create(id=1, order=1, collector=collector)
|
||||||
|
Category.objects.create(id=2, order=2, collector=collector)
|
||||||
|
Category.objects.create(id=3, order=0, collector=collector)
|
||||||
|
Category.objects.create(id=4, order=0, collector=collector)
|
||||||
|
|
||||||
|
# NB: The order values must be changed so that the items are reordered.
|
||||||
|
data = {
|
||||||
|
"form-TOTAL_FORMS": "4",
|
||||||
|
"form-INITIAL_FORMS": "4",
|
||||||
|
|
||||||
|
"form-0-order": "14",
|
||||||
|
"form-0-id": "1",
|
||||||
|
"form-0-collector": "1",
|
||||||
|
|
||||||
|
"form-1-order": "13",
|
||||||
|
"form-1-id": "2",
|
||||||
|
"form-1-collector": "1",
|
||||||
|
|
||||||
|
"form-2-order": "1",
|
||||||
|
"form-2-id": "3",
|
||||||
|
"form-2-collector": "1",
|
||||||
|
|
||||||
|
"form-3-order": "0",
|
||||||
|
"form-3-id": "4",
|
||||||
|
"form-3-collector": "1",
|
||||||
|
}
|
||||||
|
response = self.client.post('/test_admin/admin/admin_views/category/', data)
|
||||||
|
# Successful post will redirect
|
||||||
|
self.failUnlessEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
# Check that the order values have been applied to the right objects
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=1).order, 14)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=2).order, 13)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=3).order, 1)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=4).order, 0)
|
||||||
|
|
||||||
class AdminSearchTest(TestCase):
|
class AdminSearchTest(TestCase):
|
||||||
fixtures = ['admin-views-users','multiple-child-classes']
|
fixtures = ['admin-views-users','multiple-child-classes']
|
||||||
|
|
||||||
@ -1254,11 +1294,24 @@ class AdminInlineTests(TestCase):
|
|||||||
"fancydoodad_set-2-owner": "1",
|
"fancydoodad_set-2-owner": "1",
|
||||||
"fancydoodad_set-2-name": "",
|
"fancydoodad_set-2-name": "",
|
||||||
"fancydoodad_set-2-expensive": "on",
|
"fancydoodad_set-2-expensive": "on",
|
||||||
|
|
||||||
|
"category_set-TOTAL_FORMS": "3",
|
||||||
|
"category_set-INITIAL_FORMS": "0",
|
||||||
|
"category_set-0-order": "",
|
||||||
|
"category_set-0-id": "",
|
||||||
|
"category_set-0-collector": "1",
|
||||||
|
"category_set-1-order": "",
|
||||||
|
"category_set-1-id": "",
|
||||||
|
"category_set-1-collector": "1",
|
||||||
|
"category_set-2-order": "",
|
||||||
|
"category_set-2-id": "",
|
||||||
|
"category_set-2-collector": "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
result = self.client.login(username='super', password='secret')
|
result = self.client.login(username='super', password='secret')
|
||||||
self.failUnlessEqual(result, True)
|
self.failUnlessEqual(result, True)
|
||||||
Collector(pk=1,name='John Fowles').save()
|
self.collector = Collector(pk=1,name='John Fowles')
|
||||||
|
self.collector.save()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
@ -1419,3 +1472,58 @@ class AdminInlineTests(TestCase):
|
|||||||
self.failUnlessEqual(response.status_code, 302)
|
self.failUnlessEqual(response.status_code, 302)
|
||||||
self.failUnlessEqual(FancyDoodad.objects.count(), 1)
|
self.failUnlessEqual(FancyDoodad.objects.count(), 1)
|
||||||
self.failUnlessEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1 Updated")
|
self.failUnlessEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1 Updated")
|
||||||
|
|
||||||
|
def test_ordered_inline(self):
|
||||||
|
"""Check that an inline with an editable ordering fields is
|
||||||
|
updated correctly. Regression for #10922"""
|
||||||
|
# Create some objects with an initial ordering
|
||||||
|
Category.objects.create(id=1, order=1, collector=self.collector)
|
||||||
|
Category.objects.create(id=2, order=2, collector=self.collector)
|
||||||
|
Category.objects.create(id=3, order=0, collector=self.collector)
|
||||||
|
Category.objects.create(id=4, order=0, collector=self.collector)
|
||||||
|
|
||||||
|
# NB: The order values must be changed so that the items are reordered.
|
||||||
|
self.post_data.update({
|
||||||
|
"name": "Frederick Clegg",
|
||||||
|
|
||||||
|
"category_set-TOTAL_FORMS": "7",
|
||||||
|
"category_set-INITIAL_FORMS": "4",
|
||||||
|
|
||||||
|
"category_set-0-order": "14",
|
||||||
|
"category_set-0-id": "1",
|
||||||
|
"category_set-0-collector": "1",
|
||||||
|
|
||||||
|
"category_set-1-order": "13",
|
||||||
|
"category_set-1-id": "2",
|
||||||
|
"category_set-1-collector": "1",
|
||||||
|
|
||||||
|
"category_set-2-order": "1",
|
||||||
|
"category_set-2-id": "3",
|
||||||
|
"category_set-2-collector": "1",
|
||||||
|
|
||||||
|
"category_set-3-order": "0",
|
||||||
|
"category_set-3-id": "4",
|
||||||
|
"category_set-3-collector": "1",
|
||||||
|
|
||||||
|
"category_set-4-order": "",
|
||||||
|
"category_set-4-id": "",
|
||||||
|
"category_set-4-collector": "1",
|
||||||
|
|
||||||
|
"category_set-5-order": "",
|
||||||
|
"category_set-5-id": "",
|
||||||
|
"category_set-5-collector": "1",
|
||||||
|
|
||||||
|
"category_set-6-order": "",
|
||||||
|
"category_set-6-id": "",
|
||||||
|
"category_set-6-collector": "1",
|
||||||
|
})
|
||||||
|
response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
|
||||||
|
# Successful post will redirect
|
||||||
|
self.failUnlessEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
# Check that the order values have been applied to the right objects
|
||||||
|
self.failUnlessEqual(self.collector.category_set.count(), 4)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=1).order, 14)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=2).order, 13)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=3).order, 1)
|
||||||
|
self.failUnlessEqual(Category.objects.get(id=4).order, 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user