Ticket #17627: 0002.patch

File 0002.patch, 120.5 KB (added by Luke Granger-Brown, 12 years ago)

Same as original patch, rebased against master and also including all util.py -> utils.py changes apart from utils/unittests/util.py

  • AUTHORS

    diff --git a/AUTHORS b/AUTHORS
    index 5fa21d3..faa6126 100644
    a b answer newbie questions, and generally made Django that much better:  
    218218    David Gouldin <dgouldin@gmail.com>
    219219    pradeep.gowda@gmail.com
    220220    Collin Grady <collin@collingrady.com>
     221    Luke Granger-Brown <django@lukegb.com>
    221222    Gabriel Grant <g@briel.ca>
    222223    Simon Greenhill <dev@simon.net.nz>
    223224    Owen Griffiths
    answer newbie questions, and generally made Django that much better:  
    295296    Cameron Knight (ckknight)
    296297    Nena Kojadin <nena@kiberpipa.org>
    297298    Igor Kolar <ike@email.si>
     299    Wiktor Kołodziej
    298300    Tomáš Kopeček <permonik@m6.cz>
    299301    Gasper Koren
    300302    Mikhail Korobov <kmike84@googlemail.com>
  • django/conf/locale/en/LC_MESSAGES/django.po

    diff --git a/django/conf/locale/en/LC_MESSAGES/django.po b/django/conf/locale/en/LC_MESSAGES/django.po
    index abb52ae..1f3317b 100644
    a b msgstr ""  
    655655msgid "\"%s\" is not a valid value for a primary key."
    656656msgstr ""
    657657
    658 #: forms/util.py:70
     658#: forms/utils.py:70
    659659#, python-format
    660660msgid ""
    661661"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it "
  • django/contrib/admin/actions.py

    diff --git a/django/contrib/admin/actions.py b/django/contrib/admin/actions.py
    index 5b56402..4a03a97 100644
    a b Built-in, globally-available admin actions.  
    44
    55from django.core.exceptions import PermissionDenied
    66from django.contrib.admin import helpers
    7 from django.contrib.admin.util import get_deleted_objects, model_ngettext
     7from django.contrib.admin.utils import get_deleted_objects, model_ngettext
    88from django.db import router
    99from django.template.response import TemplateResponse
    1010from django.utils.encoding import force_unicode
  • django/contrib/admin/filters.py

    diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
    index 550683b..dc4434b 100644
    a b from django.core.exceptions import ImproperlyConfigured  
    1212from django.utils.encoding import smart_unicode
    1313from django.utils.translation import ugettext_lazy as _
    1414
    15 from django.contrib.admin.util import (get_model_from_relation,
     15from django.contrib.admin.utils import (get_model_from_relation,
    1616    reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
    1717
    1818class ListFilter(object):
  • django/contrib/admin/helpers.py

    diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
    index b08d1c8..ae46be4 100644
    a b  
    11from django import forms
    2 from django.contrib.admin.util import (flatten_fieldsets, lookup_field,
     2from django.contrib.admin.utils import (flatten_fieldsets, lookup_field,
    33    display_for_field, label_for_field, help_text_for_field)
    44from django.contrib.admin.templatetags.admin_static import static
    55from django.contrib.contenttypes.models import ContentType
    66from django.core.exceptions import ObjectDoesNotExist
    77from django.db.models.fields.related import ManyToManyRel
    8 from django.forms.util import flatatt
     8from django.forms.utils import flatatt
    99from django.template.defaultfilters import capfirst
    1010from django.utils.encoding import force_unicode, smart_unicode
    1111from django.utils.html import escape, conditional_escape
    class InlineFieldset(Fieldset):  
    318318            yield Fieldline(self.form, field, self.readonly_fields,
    319319                model_admin=self.model_admin)
    320320
    321 class AdminErrorList(forms.util.ErrorList):
     321class AdminErrorList(forms.utils.ErrorList):
    322322    """
    323323    Stores all errors for the form/formsets in an add/change stage view.
    324324    """
  • django/contrib/admin/models.py

    diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py
    index 0e5b8a7..013ae44 100644
    a b  
    11from django.db import models
    22from django.contrib.contenttypes.models import ContentType
    33from django.contrib.auth.models import User
    4 from django.contrib.admin.util import quote
     4from django.contrib.admin.utils import quote
    55from django.utils.translation import ugettext_lazy as _
    66from django.utils.encoding import smart_unicode
    77from django.utils.safestring import mark_safe
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index f5f6256..0dcb4e6 100644
    a b from django.forms.models import (modelform_factory, modelformset_factory,  
    66    inlineformset_factory, BaseInlineFormSet)
    77from django.contrib.contenttypes.models import ContentType
    88from django.contrib.admin import widgets, helpers
    9 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
     9from django.contrib.admin.utils import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
    1010from django.contrib.admin.templatetags.admin_static import static
    1111from django.contrib import messages
    1212from django.views.decorators.csrf import csrf_protect
  • django/contrib/admin/templatetags/admin_list.py

    diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
    index e778429..23ba3ea 100644
    a b  
    11import datetime
    22
    3 from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
     3from django.contrib.admin.utils import lookup_field, display_for_field, label_for_field
    44from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
    55    ORDER_VAR, PAGE_VAR, SEARCH_VAR)
    66from django.contrib.admin.templatetags.admin_static import static
  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    index 61182a6..fc8b3dd 100644
    a b  
    1 from django.db import models
    2 from django.db.models.sql.constants import LOOKUP_SEP
    3 from django.db.models.deletion import Collector
    4 from django.db.models.related import RelatedObject
    5 from django.forms.forms import pretty_name
    6 from django.utils import formats
    7 from django.utils.html import escape
    8 from django.utils.safestring import mark_safe
    9 from django.utils.text import capfirst
    10 from django.utils import timezone
    11 from django.utils.encoding import force_unicode, smart_unicode, smart_str
    12 from django.utils.translation import ungettext
    13 from django.core.urlresolvers import reverse
     1import warnings
    142
    15 def lookup_needs_distinct(opts, lookup_path):
    16     """
    17     Returns True if 'distinct()' should be used to query the given lookup path.
    18     """
    19     field_name = lookup_path.split('__', 1)[0]
    20     field = opts.get_field_by_name(field_name)[0]
    21     if ((hasattr(field, 'rel') and
    22          isinstance(field.rel, models.ManyToManyRel)) or
    23         (isinstance(field, models.related.RelatedObject) and
    24          not field.field.unique)):
    25          return True
    26     return False
     3warnings.warn("The django.contrib.admin.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.contrib.admin.utils instead.", PendingDeprecationWarning)
    276
    28 def prepare_lookup_value(key, value):
    29     """
    30     Returns a lookup value prepared to be used in queryset filtering.
    31     """
    32     # if key ends with __in, split parameter into separate values
    33     if key.endswith('__in'):
    34         value = value.split(',')
    35     # if key ends with __isnull, special case '' and false
    36     if key.endswith('__isnull'):
    37         if value.lower() in ('', 'false'):
    38             value = False
    39         else:
    40             value = True
    41     return value
    42 
    43 def quote(s):
    44     """
    45     Ensure that primary key values do not confuse the admin URLs by escaping
    46     any '/', '_' and ':' characters. Similar to urllib.quote, except that the
    47     quoting is slightly different so that it doesn't get automatically
    48     unquoted by the Web browser.
    49     """
    50     if not isinstance(s, basestring):
    51         return s
    52     res = list(s)
    53     for i in range(len(res)):
    54         c = res[i]
    55         if c in """:/_#?;@&=+$,"<>%\\""":
    56             res[i] = '_%02X' % ord(c)
    57     return ''.join(res)
    58 
    59 
    60 def unquote(s):
    61     """
    62     Undo the effects of quote(). Based heavily on urllib.unquote().
    63     """
    64     mychr = chr
    65     myatoi = int
    66     list = s.split('_')
    67     res = [list[0]]
    68     myappend = res.append
    69     del list[0]
    70     for item in list:
    71         if item[1:2]:
    72             try:
    73                 myappend(mychr(myatoi(item[:2], 16)) + item[2:])
    74             except ValueError:
    75                 myappend('_' + item)
    76         else:
    77             myappend('_' + item)
    78     return "".join(res)
    79 
    80 
    81 def flatten_fieldsets(fieldsets):
    82     """Returns a list of field names from an admin fieldsets structure."""
    83     field_names = []
    84     for name, opts in fieldsets:
    85         for field in opts['fields']:
    86             # type checking feels dirty, but it seems like the best way here
    87             if type(field) == tuple:
    88                 field_names.extend(field)
    89             else:
    90                 field_names.append(field)
    91     return field_names
    92 
    93 
    94 def get_deleted_objects(objs, opts, user, admin_site, using):
    95     """
    96     Find all objects related to ``objs`` that should also be deleted. ``objs``
    97     must be a homogenous iterable of objects (e.g. a QuerySet).
    98 
    99     Returns a nested list of strings suitable for display in the
    100     template with the ``unordered_list`` filter.
    101 
    102     """
    103     collector = NestedObjects(using=using)
    104     collector.collect(objs)
    105     perms_needed = set()
    106 
    107     def format_callback(obj):
    108         has_admin = obj.__class__ in admin_site._registry
    109         opts = obj._meta
    110 
    111         if has_admin:
    112             admin_url = reverse('%s:%s_%s_change'
    113                                 % (admin_site.name,
    114                                    opts.app_label,
    115                                    opts.object_name.lower()),
    116                                 None, (quote(obj._get_pk_val()),))
    117             p = '%s.%s' % (opts.app_label,
    118                            opts.get_delete_permission())
    119             if not user.has_perm(p):
    120                 perms_needed.add(opts.verbose_name)
    121             # Display a link to the admin page.
    122             return mark_safe(u'%s: <a href="%s">%s</a>' %
    123                              (escape(capfirst(opts.verbose_name)),
    124                               admin_url,
    125                               escape(obj)))
    126         else:
    127             # Don't display link to edit, because it either has no
    128             # admin or is edited inline.
    129             return u'%s: %s' % (capfirst(opts.verbose_name),
    130                                 force_unicode(obj))
    131 
    132     to_delete = collector.nested(format_callback)
    133 
    134     protected = [format_callback(obj) for obj in collector.protected]
    135 
    136     return to_delete, perms_needed, protected
    137 
    138 
    139 class NestedObjects(Collector):
    140     def __init__(self, *args, **kwargs):
    141         super(NestedObjects, self).__init__(*args, **kwargs)
    142         self.edges = {} # {from_instance: [to_instances]}
    143         self.protected = set()
    144 
    145     def add_edge(self, source, target):
    146         self.edges.setdefault(source, []).append(target)
    147 
    148     def collect(self, objs, source_attr=None, **kwargs):
    149         for obj in objs:
    150             if source_attr:
    151                 self.add_edge(getattr(obj, source_attr), obj)
    152             else:
    153                 self.add_edge(None, obj)
    154         try:
    155             return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)
    156         except models.ProtectedError, e:
    157             self.protected.update(e.protected_objects)
    158 
    159     def related_objects(self, related, objs):
    160         qs = super(NestedObjects, self).related_objects(related, objs)
    161         return qs.select_related(related.field.name)
    162 
    163     def _nested(self, obj, seen, format_callback):
    164         if obj in seen:
    165             return []
    166         seen.add(obj)
    167         children = []
    168         for child in self.edges.get(obj, ()):
    169             children.extend(self._nested(child, seen, format_callback))
    170         if format_callback:
    171             ret = [format_callback(obj)]
    172         else:
    173             ret = [obj]
    174         if children:
    175             ret.append(children)
    176         return ret
    177 
    178     def nested(self, format_callback=None):
    179         """
    180         Return the graph as a nested list.
    181 
    182         """
    183         seen = set()
    184         roots = []
    185         for root in self.edges.get(None, ()):
    186             roots.extend(self._nested(root, seen, format_callback))
    187         return roots
    188 
    189 
    190 def model_format_dict(obj):
    191     """
    192     Return a `dict` with keys 'verbose_name' and 'verbose_name_plural',
    193     typically for use with string formatting.
    194 
    195     `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
    196 
    197     """
    198     if isinstance(obj, (models.Model, models.base.ModelBase)):
    199         opts = obj._meta
    200     elif isinstance(obj, models.query.QuerySet):
    201         opts = obj.model._meta
    202     else:
    203         opts = obj
    204     return {
    205         'verbose_name': force_unicode(opts.verbose_name),
    206         'verbose_name_plural': force_unicode(opts.verbose_name_plural)
    207     }
    208 
    209 
    210 def model_ngettext(obj, n=None):
    211     """
    212     Return the appropriate `verbose_name` or `verbose_name_plural` value for
    213     `obj` depending on the count `n`.
    214 
    215     `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
    216     If `obj` is a `QuerySet` instance, `n` is optional and the length of the
    217     `QuerySet` is used.
    218 
    219     """
    220     if isinstance(obj, models.query.QuerySet):
    221         if n is None:
    222             n = obj.count()
    223         obj = obj.model
    224     d = model_format_dict(obj)
    225     singular, plural = d["verbose_name"], d["verbose_name_plural"]
    226     return ungettext(singular, plural, n or 0)
    227 
    228 
    229 def lookup_field(name, obj, model_admin=None):
    230     opts = obj._meta
    231     try:
    232         f = opts.get_field(name)
    233     except models.FieldDoesNotExist:
    234         # For non-field values, the value is either a method, property or
    235         # returned via a callable.
    236         if callable(name):
    237             attr = name
    238             value = attr(obj)
    239         elif (model_admin is not None and hasattr(model_admin, name) and
    240           not name == '__str__' and not name == '__unicode__'):
    241             attr = getattr(model_admin, name)
    242             value = attr(obj)
    243         else:
    244             attr = getattr(obj, name)
    245             if callable(attr):
    246                 value = attr()
    247             else:
    248                 value = attr
    249         f = None
    250     else:
    251         attr = None
    252         value = getattr(obj, name)
    253     return f, attr, value
    254 
    255 
    256 def label_for_field(name, model, model_admin=None, return_attr=False):
    257     """
    258     Returns a sensible label for a field name. The name can be a callable or the
    259     name of an object attributes, as well as a genuine fields. If return_attr is
    260     True, the resolved attribute (which could be a callable) is also returned.
    261     This will be None if (and only if) the name refers to a field.
    262     """
    263     attr = None
    264     try:
    265         field = model._meta.get_field_by_name(name)[0]
    266         if isinstance(field, RelatedObject):
    267             label = field.opts.verbose_name
    268         else:
    269             label = field.verbose_name
    270     except models.FieldDoesNotExist:
    271         if name == "__unicode__":
    272             label = force_unicode(model._meta.verbose_name)
    273             attr = unicode
    274         elif name == "__str__":
    275             label = smart_str(model._meta.verbose_name)
    276             attr = str
    277         else:
    278             if callable(name):
    279                 attr = name
    280             elif model_admin is not None and hasattr(model_admin, name):
    281                 attr = getattr(model_admin, name)
    282             elif hasattr(model, name):
    283                 attr = getattr(model, name)
    284             else:
    285                 message = "Unable to lookup '%s' on %s" % (name, model._meta.object_name)
    286                 if model_admin:
    287                     message += " or %s" % (model_admin.__class__.__name__,)
    288                 raise AttributeError(message)
    289 
    290             if hasattr(attr, "short_description"):
    291                 label = attr.short_description
    292             elif callable(attr):
    293                 if attr.__name__ == "<lambda>":
    294                     label = "--"
    295                 else:
    296                     label = pretty_name(attr.__name__)
    297             else:
    298                 label = pretty_name(name)
    299     if return_attr:
    300         return (label, attr)
    301     else:
    302         return label
    303 
    304 def help_text_for_field(name, model):
    305     try:
    306         help_text = model._meta.get_field_by_name(name)[0].help_text
    307     except models.FieldDoesNotExist:
    308         help_text = ""
    309     return smart_unicode(help_text)
    310 
    311 
    312 def display_for_field(value, field):
    313     from django.contrib.admin.templatetags.admin_list import _boolean_icon
    314     from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
    315 
    316     if field.flatchoices:
    317         return dict(field.flatchoices).get(value, EMPTY_CHANGELIST_VALUE)
    318     # NullBooleanField needs special-case null-handling, so it comes
    319     # before the general null test.
    320     elif isinstance(field, models.BooleanField) or isinstance(field, models.NullBooleanField):
    321         return _boolean_icon(value)
    322     elif value is None:
    323         return EMPTY_CHANGELIST_VALUE
    324     elif isinstance(field, models.DateTimeField):
    325         return formats.localize(timezone.localtime(value))
    326     elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
    327         return formats.localize(value)
    328     elif isinstance(field, models.DecimalField):
    329         return formats.number_format(value, field.decimal_places)
    330     elif isinstance(field, models.FloatField):
    331         return formats.number_format(value)
    332     else:
    333         return smart_unicode(value)
    334 
    335 
    336 class NotRelationField(Exception):
    337     pass
    338 
    339 
    340 def get_model_from_relation(field):
    341     if isinstance(field, models.related.RelatedObject):
    342         return field.model
    343     elif getattr(field, 'rel'): # or isinstance?
    344         return field.rel.to
    345     else:
    346         raise NotRelationField
    347 
    348 
    349 def reverse_field_path(model, path):
    350     """ Create a reversed field path.
    351 
    352     E.g. Given (Order, "user__groups"),
    353     return (Group, "user__order").
    354 
    355     Final field must be a related model, not a data field.
    356 
    357     """
    358     reversed_path = []
    359     parent = model
    360     pieces = path.split(LOOKUP_SEP)
    361     for piece in pieces:
    362         field, model, direct, m2m = parent._meta.get_field_by_name(piece)
    363         # skip trailing data field if extant:
    364         if len(reversed_path) == len(pieces)-1: # final iteration
    365             try:
    366                 get_model_from_relation(field)
    367             except NotRelationField:
    368                 break
    369         if direct:
    370             related_name = field.related_query_name()
    371             parent = field.rel.to
    372         else:
    373             related_name = field.field.name
    374             parent = field.model
    375         reversed_path.insert(0, related_name)
    376     return (parent, LOOKUP_SEP.join(reversed_path))
    377 
    378 
    379 def get_fields_from_path(model, path):
    380     """ Return list of Fields given path relative to model.
    381 
    382     e.g. (ModelX, "user__groups__name") -> [
    383         <django.db.models.fields.related.ForeignKey object at 0x...>,
    384         <django.db.models.fields.related.ManyToManyField object at 0x...>,
    385         <django.db.models.fields.CharField object at 0x...>,
    386     ]
    387     """
    388     pieces = path.split(LOOKUP_SEP)
    389     fields = []
    390     for piece in pieces:
    391         if fields:
    392             parent = get_model_from_relation(fields[-1])
    393         else:
    394             parent = model
    395         fields.append(parent._meta.get_field_by_name(piece)[0])
    396     return fields
    397 
    398 
    399 def remove_trailing_data_field(fields):
    400     """ Discard trailing non-relation field if extant. """
    401     try:
    402         get_model_from_relation(fields[-1])
    403     except NotRelationField:
    404         fields = fields[:-1]
    405     return fields
    406 
    407 
    408 def get_limit_choices_to_from_path(model, path):
    409     """ Return Q object for limiting choices if applicable.
    410 
    411     If final model in path is linked via a ForeignKey or ManyToManyField which
    412     has a `limit_choices_to` attribute, return it as a Q object.
    413     """
    414 
    415     fields = get_fields_from_path(model, path)
    416     fields = remove_trailing_data_field(fields)
    417     limit_choices_to = (
    418         fields and hasattr(fields[-1], 'rel') and
    419         getattr(fields[-1].rel, 'limit_choices_to', None))
    420     if not limit_choices_to:
    421         return models.Q() # empty Q
    422     elif isinstance(limit_choices_to, models.Q):
    423         return limit_choices_to # already a Q
    424     else:
    425         return models.Q(**limit_choices_to) # convert dict to Q
     7from django.contrib.admin.utils import *
  • new file django/contrib/admin/utils.py

    diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
    new file mode 100644
    index 0000000..61182a6
    - +  
     1from django.db import models
     2from django.db.models.sql.constants import LOOKUP_SEP
     3from django.db.models.deletion import Collector
     4from django.db.models.related import RelatedObject
     5from django.forms.forms import pretty_name
     6from django.utils import formats
     7from django.utils.html import escape
     8from django.utils.safestring import mark_safe
     9from django.utils.text import capfirst
     10from django.utils import timezone
     11from django.utils.encoding import force_unicode, smart_unicode, smart_str
     12from django.utils.translation import ungettext
     13from django.core.urlresolvers import reverse
     14
     15def lookup_needs_distinct(opts, lookup_path):
     16    """
     17    Returns True if 'distinct()' should be used to query the given lookup path.
     18    """
     19    field_name = lookup_path.split('__', 1)[0]
     20    field = opts.get_field_by_name(field_name)[0]
     21    if ((hasattr(field, 'rel') and
     22         isinstance(field.rel, models.ManyToManyRel)) or
     23        (isinstance(field, models.related.RelatedObject) and
     24         not field.field.unique)):
     25         return True
     26    return False
     27
     28def prepare_lookup_value(key, value):
     29    """
     30    Returns a lookup value prepared to be used in queryset filtering.
     31    """
     32    # if key ends with __in, split parameter into separate values
     33    if key.endswith('__in'):
     34        value = value.split(',')
     35    # if key ends with __isnull, special case '' and false
     36    if key.endswith('__isnull'):
     37        if value.lower() in ('', 'false'):
     38            value = False
     39        else:
     40            value = True
     41    return value
     42
     43def quote(s):
     44    """
     45    Ensure that primary key values do not confuse the admin URLs by escaping
     46    any '/', '_' and ':' characters. Similar to urllib.quote, except that the
     47    quoting is slightly different so that it doesn't get automatically
     48    unquoted by the Web browser.
     49    """
     50    if not isinstance(s, basestring):
     51        return s
     52    res = list(s)
     53    for i in range(len(res)):
     54        c = res[i]
     55        if c in """:/_#?;@&=+$,"<>%\\""":
     56            res[i] = '_%02X' % ord(c)
     57    return ''.join(res)
     58
     59
     60def unquote(s):
     61    """
     62    Undo the effects of quote(). Based heavily on urllib.unquote().
     63    """
     64    mychr = chr
     65    myatoi = int
     66    list = s.split('_')
     67    res = [list[0]]
     68    myappend = res.append
     69    del list[0]
     70    for item in list:
     71        if item[1:2]:
     72            try:
     73                myappend(mychr(myatoi(item[:2], 16)) + item[2:])
     74            except ValueError:
     75                myappend('_' + item)
     76        else:
     77            myappend('_' + item)
     78    return "".join(res)
     79
     80
     81def flatten_fieldsets(fieldsets):
     82    """Returns a list of field names from an admin fieldsets structure."""
     83    field_names = []
     84    for name, opts in fieldsets:
     85        for field in opts['fields']:
     86            # type checking feels dirty, but it seems like the best way here
     87            if type(field) == tuple:
     88                field_names.extend(field)
     89            else:
     90                field_names.append(field)
     91    return field_names
     92
     93
     94def get_deleted_objects(objs, opts, user, admin_site, using):
     95    """
     96    Find all objects related to ``objs`` that should also be deleted. ``objs``
     97    must be a homogenous iterable of objects (e.g. a QuerySet).
     98
     99    Returns a nested list of strings suitable for display in the
     100    template with the ``unordered_list`` filter.
     101
     102    """
     103    collector = NestedObjects(using=using)
     104    collector.collect(objs)
     105    perms_needed = set()
     106
     107    def format_callback(obj):
     108        has_admin = obj.__class__ in admin_site._registry
     109        opts = obj._meta
     110
     111        if has_admin:
     112            admin_url = reverse('%s:%s_%s_change'
     113                                % (admin_site.name,
     114                                   opts.app_label,
     115                                   opts.object_name.lower()),
     116                                None, (quote(obj._get_pk_val()),))
     117            p = '%s.%s' % (opts.app_label,
     118                           opts.get_delete_permission())
     119            if not user.has_perm(p):
     120                perms_needed.add(opts.verbose_name)
     121            # Display a link to the admin page.
     122            return mark_safe(u'%s: <a href="%s">%s</a>' %
     123                             (escape(capfirst(opts.verbose_name)),
     124                              admin_url,
     125                              escape(obj)))
     126        else:
     127            # Don't display link to edit, because it either has no
     128            # admin or is edited inline.
     129            return u'%s: %s' % (capfirst(opts.verbose_name),
     130                                force_unicode(obj))
     131
     132    to_delete = collector.nested(format_callback)
     133
     134    protected = [format_callback(obj) for obj in collector.protected]
     135
     136    return to_delete, perms_needed, protected
     137
     138
     139class NestedObjects(Collector):
     140    def __init__(self, *args, **kwargs):
     141        super(NestedObjects, self).__init__(*args, **kwargs)
     142        self.edges = {} # {from_instance: [to_instances]}
     143        self.protected = set()
     144
     145    def add_edge(self, source, target):
     146        self.edges.setdefault(source, []).append(target)
     147
     148    def collect(self, objs, source_attr=None, **kwargs):
     149        for obj in objs:
     150            if source_attr:
     151                self.add_edge(getattr(obj, source_attr), obj)
     152            else:
     153                self.add_edge(None, obj)
     154        try:
     155            return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)
     156        except models.ProtectedError, e:
     157            self.protected.update(e.protected_objects)
     158
     159    def related_objects(self, related, objs):
     160        qs = super(NestedObjects, self).related_objects(related, objs)
     161        return qs.select_related(related.field.name)
     162
     163    def _nested(self, obj, seen, format_callback):
     164        if obj in seen:
     165            return []
     166        seen.add(obj)
     167        children = []
     168        for child in self.edges.get(obj, ()):
     169            children.extend(self._nested(child, seen, format_callback))
     170        if format_callback:
     171            ret = [format_callback(obj)]
     172        else:
     173            ret = [obj]
     174        if children:
     175            ret.append(children)
     176        return ret
     177
     178    def nested(self, format_callback=None):
     179        """
     180        Return the graph as a nested list.
     181
     182        """
     183        seen = set()
     184        roots = []
     185        for root in self.edges.get(None, ()):
     186            roots.extend(self._nested(root, seen, format_callback))
     187        return roots
     188
     189
     190def model_format_dict(obj):
     191    """
     192    Return a `dict` with keys 'verbose_name' and 'verbose_name_plural',
     193    typically for use with string formatting.
     194
     195    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
     196
     197    """
     198    if isinstance(obj, (models.Model, models.base.ModelBase)):
     199        opts = obj._meta
     200    elif isinstance(obj, models.query.QuerySet):
     201        opts = obj.model._meta
     202    else:
     203        opts = obj
     204    return {
     205        'verbose_name': force_unicode(opts.verbose_name),
     206        'verbose_name_plural': force_unicode(opts.verbose_name_plural)
     207    }
     208
     209
     210def model_ngettext(obj, n=None):
     211    """
     212    Return the appropriate `verbose_name` or `verbose_name_plural` value for
     213    `obj` depending on the count `n`.
     214
     215    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
     216    If `obj` is a `QuerySet` instance, `n` is optional and the length of the
     217    `QuerySet` is used.
     218
     219    """
     220    if isinstance(obj, models.query.QuerySet):
     221        if n is None:
     222            n = obj.count()
     223        obj = obj.model
     224    d = model_format_dict(obj)
     225    singular, plural = d["verbose_name"], d["verbose_name_plural"]
     226    return ungettext(singular, plural, n or 0)
     227
     228
     229def lookup_field(name, obj, model_admin=None):
     230    opts = obj._meta
     231    try:
     232        f = opts.get_field(name)
     233    except models.FieldDoesNotExist:
     234        # For non-field values, the value is either a method, property or
     235        # returned via a callable.
     236        if callable(name):
     237            attr = name
     238            value = attr(obj)
     239        elif (model_admin is not None and hasattr(model_admin, name) and
     240          not name == '__str__' and not name == '__unicode__'):
     241            attr = getattr(model_admin, name)
     242            value = attr(obj)
     243        else:
     244            attr = getattr(obj, name)
     245            if callable(attr):
     246                value = attr()
     247            else:
     248                value = attr
     249        f = None
     250    else:
     251        attr = None
     252        value = getattr(obj, name)
     253    return f, attr, value
     254
     255
     256def label_for_field(name, model, model_admin=None, return_attr=False):
     257    """
     258    Returns a sensible label for a field name. The name can be a callable or the
     259    name of an object attributes, as well as a genuine fields. If return_attr is
     260    True, the resolved attribute (which could be a callable) is also returned.
     261    This will be None if (and only if) the name refers to a field.
     262    """
     263    attr = None
     264    try:
     265        field = model._meta.get_field_by_name(name)[0]
     266        if isinstance(field, RelatedObject):
     267            label = field.opts.verbose_name
     268        else:
     269            label = field.verbose_name
     270    except models.FieldDoesNotExist:
     271        if name == "__unicode__":
     272            label = force_unicode(model._meta.verbose_name)
     273            attr = unicode
     274        elif name == "__str__":
     275            label = smart_str(model._meta.verbose_name)
     276            attr = str
     277        else:
     278            if callable(name):
     279                attr = name
     280            elif model_admin is not None and hasattr(model_admin, name):
     281                attr = getattr(model_admin, name)
     282            elif hasattr(model, name):
     283                attr = getattr(model, name)
     284            else:
     285                message = "Unable to lookup '%s' on %s" % (name, model._meta.object_name)
     286                if model_admin:
     287                    message += " or %s" % (model_admin.__class__.__name__,)
     288                raise AttributeError(message)
     289
     290            if hasattr(attr, "short_description"):
     291                label = attr.short_description
     292            elif callable(attr):
     293                if attr.__name__ == "<lambda>":
     294                    label = "--"
     295                else:
     296                    label = pretty_name(attr.__name__)
     297            else:
     298                label = pretty_name(name)
     299    if return_attr:
     300        return (label, attr)
     301    else:
     302        return label
     303
     304def help_text_for_field(name, model):
     305    try:
     306        help_text = model._meta.get_field_by_name(name)[0].help_text
     307    except models.FieldDoesNotExist:
     308        help_text = ""
     309    return smart_unicode(help_text)
     310
     311
     312def display_for_field(value, field):
     313    from django.contrib.admin.templatetags.admin_list import _boolean_icon
     314    from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
     315
     316    if field.flatchoices:
     317        return dict(field.flatchoices).get(value, EMPTY_CHANGELIST_VALUE)
     318    # NullBooleanField needs special-case null-handling, so it comes
     319    # before the general null test.
     320    elif isinstance(field, models.BooleanField) or isinstance(field, models.NullBooleanField):
     321        return _boolean_icon(value)
     322    elif value is None:
     323        return EMPTY_CHANGELIST_VALUE
     324    elif isinstance(field, models.DateTimeField):
     325        return formats.localize(timezone.localtime(value))
     326    elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
     327        return formats.localize(value)
     328    elif isinstance(field, models.DecimalField):
     329        return formats.number_format(value, field.decimal_places)
     330    elif isinstance(field, models.FloatField):
     331        return formats.number_format(value)
     332    else:
     333        return smart_unicode(value)
     334
     335
     336class NotRelationField(Exception):
     337    pass
     338
     339
     340def get_model_from_relation(field):
     341    if isinstance(field, models.related.RelatedObject):
     342        return field.model
     343    elif getattr(field, 'rel'): # or isinstance?
     344        return field.rel.to
     345    else:
     346        raise NotRelationField
     347
     348
     349def reverse_field_path(model, path):
     350    """ Create a reversed field path.
     351
     352    E.g. Given (Order, "user__groups"),
     353    return (Group, "user__order").
     354
     355    Final field must be a related model, not a data field.
     356
     357    """
     358    reversed_path = []
     359    parent = model
     360    pieces = path.split(LOOKUP_SEP)
     361    for piece in pieces:
     362        field, model, direct, m2m = parent._meta.get_field_by_name(piece)
     363        # skip trailing data field if extant:
     364        if len(reversed_path) == len(pieces)-1: # final iteration
     365            try:
     366                get_model_from_relation(field)
     367            except NotRelationField:
     368                break
     369        if direct:
     370            related_name = field.related_query_name()
     371            parent = field.rel.to
     372        else:
     373            related_name = field.field.name
     374            parent = field.model
     375        reversed_path.insert(0, related_name)
     376    return (parent, LOOKUP_SEP.join(reversed_path))
     377
     378
     379def get_fields_from_path(model, path):
     380    """ Return list of Fields given path relative to model.
     381
     382    e.g. (ModelX, "user__groups__name") -> [
     383        <django.db.models.fields.related.ForeignKey object at 0x...>,
     384        <django.db.models.fields.related.ManyToManyField object at 0x...>,
     385        <django.db.models.fields.CharField object at 0x...>,
     386    ]
     387    """
     388    pieces = path.split(LOOKUP_SEP)
     389    fields = []
     390    for piece in pieces:
     391        if fields:
     392            parent = get_model_from_relation(fields[-1])
     393        else:
     394            parent = model
     395        fields.append(parent._meta.get_field_by_name(piece)[0])
     396    return fields
     397
     398
     399def remove_trailing_data_field(fields):
     400    """ Discard trailing non-relation field if extant. """
     401    try:
     402        get_model_from_relation(fields[-1])
     403    except NotRelationField:
     404        fields = fields[:-1]
     405    return fields
     406
     407
     408def get_limit_choices_to_from_path(model, path):
     409    """ Return Q object for limiting choices if applicable.
     410
     411    If final model in path is linked via a ForeignKey or ManyToManyField which
     412    has a `limit_choices_to` attribute, return it as a Q object.
     413    """
     414
     415    fields = get_fields_from_path(model, path)
     416    fields = remove_trailing_data_field(fields)
     417    limit_choices_to = (
     418        fields and hasattr(fields[-1], 'rel') and
     419        getattr(fields[-1].rel, 'limit_choices_to', None))
     420    if not limit_choices_to:
     421        return models.Q() # empty Q
     422    elif isinstance(limit_choices_to, models.Q):
     423        return limit_choices_to # already a Q
     424    else:
     425        return models.Q(**limit_choices_to) # convert dict to Q
  • django/contrib/admin/validation.py

    diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
    index 733f89d..bd99b84 100644
    a b from django.db.models.fields import FieldDoesNotExist  
    44from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model,
    55    _get_foreign_key)
    66from django.contrib.admin import ListFilter, FieldListFilter
    7 from django.contrib.admin.util import get_fields_from_path, NotRelationField
     7from django.contrib.admin.utils import get_fields_from_path, NotRelationField
    88from django.contrib.admin.options import (flatten_fieldsets, BaseModelAdmin,
    99    HORIZONTAL, VERTICAL)
    1010
  • django/contrib/admin/views/main.py

    diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
    index 56f13f8..e12cca5 100644
    a b from django.utils.http import urlencode  
    1010
    1111from django.contrib.admin import FieldListFilter
    1212from django.contrib.admin.options import IncorrectLookupParameters
    13 from django.contrib.admin.util import (quote, get_fields_from_path,
     13from django.contrib.admin.utils import (quote, get_fields_from_path,
    1414    lookup_needs_distinct, prepare_lookup_value)
    1515
    1616# Changelist settings
  • django/contrib/admin/widgets.py

    diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
    index 29958b2..148ce1c 100644
    a b from django import forms  
    77from django.contrib.admin.templatetags.admin_static import static
    88from django.core.urlresolvers import reverse
    99from django.forms.widgets import RadioFieldRenderer
    10 from django.forms.util import flatatt
     10from django.forms.utils import flatatt
    1111from django.utils.html import escape
    1212from django.utils.text import Truncator
    1313from django.utils.translation import ugettext as _
  • django/contrib/auth/admin.py

    diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py
    index f14b3d2..973bdb8 100644
    a b class UserAdmin(admin.ModelAdmin):  
    7171        if obj is None:
    7272            defaults.update({
    7373                'form': self.add_form,
    74                 'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
     74                'fields': admin.utils.flatten_fieldsets(self.add_fieldsets),
    7575            })
    7676        defaults.update(kwargs)
    7777        return super(UserAdmin, self).get_form(request, obj, **defaults)
  • django/contrib/auth/forms.py

    diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
    index badd760..d988ce3 100644
    a b  
    11from django import forms
    2 from django.forms.util import flatatt
     2from django.forms.utils import flatatt
    33from django.template import loader
    44from django.utils.encoding import smart_str
    55from django.utils.http import int_to_base36
  • django/contrib/comments/forms.py

    diff --git a/django/contrib/comments/forms.py b/django/contrib/comments/forms.py
    index 830e24b..88c2e5e 100644
    a b  
    11import time
    22from django import forms
    3 from django.forms.util import ErrorDict
     3from django.forms.utils import ErrorDict
    44from django.conf import settings
    55from django.contrib.contenttypes.models import ContentType
    66from django.contrib.comments.models import Comment
  • django/contrib/gis/db/backends/oracle/creation.py

    diff --git a/django/contrib/gis/db/backends/oracle/creation.py b/django/contrib/gis/db/backends/oracle/creation.py
    index 043da91..14f8d4d 100644
    a b  
    11from django.db.backends.oracle.creation import DatabaseCreation
    2 from django.db.backends.util import truncate_name
     2from django.db.backends.utils import truncate_name
    33
    44class OracleCreation(DatabaseCreation):
    55
  • django/contrib/gis/db/backends/oracle/operations.py

    diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
    index a2374bb..97380a3 100644
    a b from decimal import Decimal  
    1313from django.db.backends.oracle.base import DatabaseOperations
    1414from django.contrib.gis.db.backends.base import BaseSpatialOperations
    1515from django.contrib.gis.db.backends.oracle.adapter import OracleSpatialAdapter
    16 from django.contrib.gis.db.backends.util import SpatialFunction
     16from django.contrib.gis.db.backends.utils import SpatialFunction
    1717from django.contrib.gis.geometry.backend import Geometry
    1818from django.contrib.gis.measure import Distance
    1919
  • django/contrib/gis/db/backends/postgis/operations.py

    diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py
    index 3286748..31e659c 100644
    a b from decimal import Decimal  
    33
    44from django.conf import settings
    55from django.contrib.gis.db.backends.base import BaseSpatialOperations
    6 from django.contrib.gis.db.backends.util import SpatialOperation, SpatialFunction
     6from django.contrib.gis.db.backends.utils import SpatialOperation, SpatialFunction
    77from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter
    88from django.contrib.gis.geometry.backend import Geometry
    99from django.contrib.gis.measure import Distance
  • django/contrib/gis/db/backends/spatialite/operations.py

    diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py
    index a0efb99..6dacff0 100644
    a b import re  
    22from decimal import Decimal
    33
    44from django.contrib.gis.db.backends.base import BaseSpatialOperations
    5 from django.contrib.gis.db.backends.util import SpatialOperation, SpatialFunction
     5from django.contrib.gis.db.backends.utils import SpatialOperation, SpatialFunction
    66from django.contrib.gis.db.backends.spatialite.adapter import SpatiaLiteAdapter
    77from django.contrib.gis.geometry.backend import Geometry
    88from django.contrib.gis.measure import Distance
  • django/contrib/gis/db/backends/util.py

    diff --git a/django/contrib/gis/db/backends/util.py b/django/contrib/gis/db/backends/util.py
    index b50c8e2..3b52c38 100644
    a b  
    1 """
    2 A collection of utility routines and classes used by the spatial
    3 backends.
    4 """
     1import warnings
    52
    6 def gqn(val):
    7     """
    8     The geographic quote name function; used for quoting tables and
    9     geometries (they use single rather than the double quotes of the
    10     backend quotename function).
    11     """
    12     if isinstance(val, basestring):
    13         if isinstance(val, unicode): val = val.encode('ascii')
    14         return "'%s'" % val
    15     else:
    16         return str(val)
     3warnings.warn("The django.contrib.gis.db.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.contrib.gis.db.utils instead.", PendingDeprecationWarning)
    176
    18 class SpatialOperation(object):
    19     """
    20     Base class for generating spatial SQL.
    21     """
    22     sql_template = '%(geo_col)s %(operator)s %(geometry)s'
    23 
    24     def __init__(self, function='', operator='', result='', **kwargs):
    25         self.function = function
    26         self.operator = operator
    27         self.result = result
    28         self.extra = kwargs
    29 
    30     def as_sql(self, geo_col, geometry='%s'):
    31         return self.sql_template % self.params(geo_col, geometry)
    32 
    33     def params(self, geo_col, geometry):
    34         params = {'function' : self.function,
    35                   'geo_col' : geo_col,
    36                   'geometry' : geometry,
    37                   'operator' : self.operator,
    38                   'result' : self.result,
    39                   }
    40         params.update(self.extra)
    41         return params
    42 
    43 class SpatialFunction(SpatialOperation):
    44     """
    45     Base class for generating spatial SQL related to a function.
    46     """
    47     sql_template = '%(function)s(%(geo_col)s, %(geometry)s)'
    48 
    49     def __init__(self, func, result='', operator='', **kwargs):
    50         # Getting the function prefix.
    51         default = {'function' : func,
    52                    'operator' : operator,
    53                    'result' : result
    54                    }
    55         kwargs.update(default)
    56         super(SpatialFunction, self).__init__(**kwargs)
     7from django.contrib.gis.db.utils import *
  • new file django/contrib/gis/db/backends/utils.py

    diff --git a/django/contrib/gis/db/backends/utils.py b/django/contrib/gis/db/backends/utils.py
    new file mode 100644
    index 0000000..b50c8e2
    - +  
     1"""
     2A collection of utility routines and classes used by the spatial
     3backends.
     4"""
     5
     6def gqn(val):
     7    """
     8    The geographic quote name function; used for quoting tables and
     9    geometries (they use single rather than the double quotes of the
     10    backend quotename function).
     11    """
     12    if isinstance(val, basestring):
     13        if isinstance(val, unicode): val = val.encode('ascii')
     14        return "'%s'" % val
     15    else:
     16        return str(val)
     17
     18class SpatialOperation(object):
     19    """
     20    Base class for generating spatial SQL.
     21    """
     22    sql_template = '%(geo_col)s %(operator)s %(geometry)s'
     23
     24    def __init__(self, function='', operator='', result='', **kwargs):
     25        self.function = function
     26        self.operator = operator
     27        self.result = result
     28        self.extra = kwargs
     29
     30    def as_sql(self, geo_col, geometry='%s'):
     31        return self.sql_template % self.params(geo_col, geometry)
     32
     33    def params(self, geo_col, geometry):
     34        params = {'function' : self.function,
     35                  'geo_col' : geo_col,
     36                  'geometry' : geometry,
     37                  'operator' : self.operator,
     38                  'result' : self.result,
     39                  }
     40        params.update(self.extra)
     41        return params
     42
     43class SpatialFunction(SpatialOperation):
     44    """
     45    Base class for generating spatial SQL related to a function.
     46    """
     47    sql_template = '%(function)s(%(geo_col)s, %(geometry)s)'
     48
     49    def __init__(self, func, result='', operator='', **kwargs):
     50        # Getting the function prefix.
     51        default = {'function' : func,
     52                   'operator' : operator,
     53                   'result' : result
     54                   }
     55        kwargs.update(default)
     56        super(SpatialFunction, self).__init__(**kwargs)
  • django/contrib/gis/db/models/sql/compiler.py

    diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py
    index 07eea32..a387fb6 100644
    a b  
    11from itertools import izip
    2 from django.db.backends.util import truncate_name, typecast_timestamp
     2from django.db.backends.utils import truncate_name, typecast_timestamp
    33from django.db.models.sql import compiler
    44from django.db.models.sql.constants import TABLE_NAME, MULTI
    55
  • django/contrib/localflavor/it/forms.py

    diff --git a/django/contrib/localflavor/it/forms.py b/django/contrib/localflavor/it/forms.py
    index 0060b48..a7dd5cd 100644
    a b import re  
    88
    99from django.contrib.localflavor.it.it_province import PROVINCE_CHOICES
    1010from django.contrib.localflavor.it.it_region import REGION_CHOICES
    11 from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check_digit
     11from django.contrib.localflavor.it.utils import ssn_check_digit, vat_number_check_digit
    1212from django.core.validators import EMPTY_VALUES
    1313from django.forms import ValidationError
    1414from django.forms.fields import Field, RegexField, Select
  • django/contrib/localflavor/it/util.py

    diff --git a/django/contrib/localflavor/it/util.py b/django/contrib/localflavor/it/util.py
    index c162ff7..8abd1bd 100644
    a b  
    1 from django.utils.encoding import smart_str, smart_unicode
     1import warnings
    22
    3 def ssn_check_digit(value):
    4     "Calculate Italian social security number check digit."
    5     ssn_even_chars = {
    6         '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8,
    7         '9': 9, 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7,
    8         'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15,
    9         'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23,
    10         'Y': 24, 'Z': 25
    11     }
    12     ssn_odd_chars = {
    13         '0': 1, '1': 0, '2': 5, '3': 7, '4': 9, '5': 13, '6': 15, '7': 17, '8':
    14         19, '9': 21, 'A': 1, 'B': 0, 'C': 5, 'D': 7, 'E': 9, 'F': 13, 'G': 15,
    15         'H': 17, 'I': 19, 'J': 21, 'K': 2, 'L': 4, 'M': 18, 'N': 20, 'O': 11,
    16         'P': 3, 'Q': 6, 'R': 8, 'S': 12, 'T': 14, 'U': 16, 'V': 10, 'W': 22,
    17         'X': 25, 'Y': 24, 'Z': 23
    18     }
    19     # Chars from 'A' to 'Z'
    20     ssn_check_digits = [chr(x) for x in range(65, 91)]
     3warnings.warn("The django.contrib.localflavor.it.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.contrib.localflavor.it.utils instead.", PendingDeprecationWarning)
    216
    22     ssn = value.upper()
    23     total = 0
    24     for i in range(0, 15):
    25         try:
    26             if i % 2 == 0:
    27                 total += ssn_odd_chars[ssn[i]]
    28             else:
    29                 total += ssn_even_chars[ssn[i]]
    30         except KeyError:
    31             msg = "Character '%(char)s' is not allowed." % {'char': ssn[i]}
    32             raise ValueError(msg)
    33     return ssn_check_digits[total % 26]
    34 
    35 def vat_number_check_digit(vat_number):
    36     "Calculate Italian VAT number check digit."
    37     normalized_vat_number = smart_str(vat_number).zfill(10)
    38     total = 0
    39     for i in range(0, 10, 2):
    40         total += int(normalized_vat_number[i])
    41     for i in range(1, 11, 2):
    42         quotient , remainder = divmod(int(normalized_vat_number[i]) * 2, 10)
    43         total += quotient + remainder
    44     return smart_unicode((10 - total % 10) % 10)
     7from django.contrib.localflavor.it.utils import *
  • new file django/contrib/localflavor/it/utils.py

    diff --git a/django/contrib/localflavor/it/utils.py b/django/contrib/localflavor/it/utils.py
    new file mode 100644
    index 0000000..c162ff7
    - +  
     1from django.utils.encoding import smart_str, smart_unicode
     2
     3def ssn_check_digit(value):
     4    "Calculate Italian social security number check digit."
     5    ssn_even_chars = {
     6        '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8,
     7        '9': 9, 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7,
     8        'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15,
     9        'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23,
     10        'Y': 24, 'Z': 25
     11    }
     12    ssn_odd_chars = {
     13        '0': 1, '1': 0, '2': 5, '3': 7, '4': 9, '5': 13, '6': 15, '7': 17, '8':
     14        19, '9': 21, 'A': 1, 'B': 0, 'C': 5, 'D': 7, 'E': 9, 'F': 13, 'G': 15,
     15        'H': 17, 'I': 19, 'J': 21, 'K': 2, 'L': 4, 'M': 18, 'N': 20, 'O': 11,
     16        'P': 3, 'Q': 6, 'R': 8, 'S': 12, 'T': 14, 'U': 16, 'V': 10, 'W': 22,
     17        'X': 25, 'Y': 24, 'Z': 23
     18    }
     19    # Chars from 'A' to 'Z'
     20    ssn_check_digits = [chr(x) for x in range(65, 91)]
     21
     22    ssn = value.upper()
     23    total = 0
     24    for i in range(0, 15):
     25        try:
     26            if i % 2 == 0:
     27                total += ssn_odd_chars[ssn[i]]
     28            else:
     29                total += ssn_even_chars[ssn[i]]
     30        except KeyError:
     31            msg = "Character '%(char)s' is not allowed." % {'char': ssn[i]}
     32            raise ValueError(msg)
     33    return ssn_check_digits[total % 26]
     34
     35def vat_number_check_digit(vat_number):
     36    "Calculate Italian VAT number check digit."
     37    normalized_vat_number = smart_str(vat_number).zfill(10)
     38    total = 0
     39    for i in range(0, 10, 2):
     40        total += int(normalized_vat_number[i])
     41    for i in range(1, 11, 2):
     42        quotient , remainder = divmod(int(normalized_vat_number[i]) * 2, 10)
     43        total += quotient + remainder
     44    return smart_unicode((10 - total % 10) % 10)
  • django/contrib/localflavor/uy/forms.py

    diff --git a/django/contrib/localflavor/uy/forms.py b/django/contrib/localflavor/uy/forms.py
    index 2112162..6107667 100644
    a b from django.core.validators import EMPTY_VALUES  
    99from django.forms.fields import Select, RegexField
    1010from django.forms import ValidationError
    1111from django.utils.translation import ugettext_lazy as _
    12 from django.contrib.localflavor.uy.util import get_validation_digit
     12from django.contrib.localflavor.uy.utils import get_validation_digit
    1313
    1414
    1515class UYDepartamentSelect(Select):
  • django/contrib/localflavor/uy/util.py

    diff --git a/django/contrib/localflavor/uy/util.py b/django/contrib/localflavor/uy/util.py
    index 0c1a8f8..95598ad 100644
    a b  
    1 # -*- coding: utf-8 -*-
     1import warnings
    22
    3 def get_validation_digit(number):
    4     """ Calculates the validation digit for the given number. """
    5     sum = 0
    6     dvs = [4, 3, 6, 7, 8, 9, 2]
    7     number = str(number)
     3warnings.warn("The django.contrib.localflavor.uy.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.contrib.localflavor.uy.utils instead.", PendingDeprecationWarning)
    86
    9     for i in range(0, len(number)):
    10         sum = (int(number[-1 - i]) * dvs[i] + sum) % 10
    11 
    12     return (10-sum) % 10
     7from django.contrib.localflavor.uy.utils import *
  • new file django/contrib/localflavor/uy/utils.py

    diff --git a/django/contrib/localflavor/uy/utils.py b/django/contrib/localflavor/uy/utils.py
    new file mode 100644
    index 0000000..0c1a8f8
    - +  
     1# -*- coding: utf-8 -*-
     2
     3def get_validation_digit(number):
     4    """ Calculates the validation digit for the given number. """
     5    sum = 0
     6    dvs = [4, 3, 6, 7, 8, 9, 2]
     7    number = str(number)
     8
     9    for i in range(0, len(number)):
     10        sum = (int(number[-1 - i]) * dvs[i] + sum) % 10
     11
     12    return (10-sum) % 10
  • django/db/backends/__init__.py

    diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
    index 7674f5c..37985e8 100644
    a b from contextlib import contextmanager  
    88
    99from django.conf import settings
    1010from django.db import DEFAULT_DB_ALIAS
    11 from django.db.backends import util
     11from django.db.backends import utils
    1212from django.db.transaction import TransactionManagementError
    1313from django.utils.importlib import import_module
    1414from django.utils.timezone import is_aware
    class BaseDatabaseWrapper(object):  
    305305            (self.use_debug_cursor is None and settings.DEBUG)):
    306306            cursor = self.make_debug_cursor(self._cursor())
    307307        else:
    308             cursor = util.CursorWrapper(self._cursor(), self)
     308            cursor = utils.CursorWrapper(self._cursor(), self)
    309309        return cursor
    310310
    311311    def make_debug_cursor(self, cursor):
    312         return util.CursorDebugWrapper(cursor, self)
     312        return utils.CursorDebugWrapper(cursor, self)
    313313
    314314class BaseDatabaseFeatures(object):
    315315    allows_group_by_pk = False
    class BaseDatabaseOperations(object):  
    798798        """
    799799        if value is None:
    800800            return None
    801         return util.format_number(value, max_digits, decimal_places)
     801        return utils.format_number(value, max_digits, decimal_places)
    802802
    803803    def year_lookup_bounds(self, value):
    804804        """
  • django/db/backends/creation.py

    diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py
    index 0cd0558..e845abd 100644
    a b class BaseDatabaseCreation(object):  
    129129        """
    130130        Returns any ALTER TABLE statements to add constraints after the fact.
    131131        """
    132         from django.db.backends.util import truncate_name
     132        from django.db.backends.utils import truncate_name
    133133
    134134        if not model._meta.managed or model._meta.proxy:
    135135            return []
    class BaseDatabaseCreation(object):  
    171171        """
    172172        Return the CREATE INDEX SQL statements for a single model field.
    173173        """
    174         from django.db.backends.util import truncate_name
     174        from django.db.backends.utils import truncate_name
    175175
    176176        if f.db_index and not f.unique:
    177177            qn = self.connection.ops.quote_name
    class BaseDatabaseCreation(object):  
    215215        return output
    216216
    217217    def sql_remove_table_constraints(self, model, references_to_delete, style):
    218         from django.db.backends.util import truncate_name
     218        from django.db.backends.utils import truncate_name
    219219        if not model._meta.managed or model._meta.proxy:
    220220            return []
    221221        output = []
  • django/db/backends/mysql/base.py

    diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
    index e30cb90..009cb50 100644
    a b if (version < (1,2,1) or (version[:3] == (1, 2, 1) and  
    2626from MySQLdb.converters import conversions, Thing2Literal
    2727from MySQLdb.constants import FIELD_TYPE, CLIENT
    2828
    29 from django.db import utils
    3029from django.db.backends import *
     30from django.db.backends import utils as util
     31from django.db import utils
    3132from django.db.backends.signals import connection_created
    3233from django.db.backends.mysql.client import DatabaseClient
    3334from django.db.backends.mysql.creation import DatabaseCreation
  • django/db/backends/oracle/base.py

    diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
    index 6bc6e1d..30e16f8 100644
    a b except ImportError, e:  
    4545    raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
    4646
    4747from django.conf import settings
    48 from django.db import utils
    4948from django.db.backends import *
     49from django.db.backends import utils as util
     50from django.db import utils
    5051from django.db.backends.signals import connection_created
    5152from django.db.backends.oracle.client import DatabaseClient
    5253from django.db.backends.oracle.creation import DatabaseCreation
  • django/db/backends/postgresql_psycopg2/base.py

    diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
    index 802ebf2..0bb8738 100644
    a b Requires psycopg 2: http://initd.org/projects/psycopg2  
    55"""
    66import sys
    77
    8 from django.db import utils
    98from django.db.backends import *
     9from django.db.backends import utils as util
     10from django.db import utils
    1011from django.db.backends.signals import connection_created
    1112from django.db.backends.postgresql_psycopg2.operations import DatabaseOperations
    1213from django.db.backends.postgresql_psycopg2.client import DatabaseClient
  • django/db/backends/postgresql_psycopg2/creation.py

    diff --git a/django/db/backends/postgresql_psycopg2/creation.py b/django/db/backends/postgresql_psycopg2/creation.py
    index ca389b9..75db89a 100644
    a b  
    11import psycopg2.extensions
    22
    33from django.db.backends.creation import BaseDatabaseCreation
    4 from django.db.backends.util import truncate_name
     4from django.db.backends.utils import truncate_name
    55
    66
    77class DatabaseCreation(BaseDatabaseCreation):
  • django/db/backends/sqlite3/base.py

    diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
    index 0b19442..5a05dbb 100644
    a b import warnings  
    1111import re
    1212import sys
    1313
    14 from django.db import utils
    1514from django.db.backends import *
     15from django.db.backends import utils as util
    1616from django.db.backends.signals import connection_created
    1717from django.db.backends.sqlite3.client import DatabaseClient
    1818from django.db.backends.sqlite3.creation import DatabaseCreation
    1919from django.db.backends.sqlite3.introspection import DatabaseIntrospection
     20from django.db import utils
    2021from django.utils.dateparse import parse_date, parse_datetime, parse_time
    2122from django.utils.safestring import SafeString
    2223from django.utils import timezone
  • django/db/backends/util.py

    diff --git a/django/db/backends/util.py b/django/db/backends/util.py
    index 32e0f4f..c6f3785 100644
    a b  
    1 import datetime
    2 import decimal
    3 import hashlib
    4 from time import time
     1import warnings
    52
    6 from django.conf import settings
    7 from django.utils.log import getLogger
    8 from django.utils.timezone import utc
     3warnings.warn("The django.db.backends.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.db.backends.utils instead.", PendingDeprecationWarning)
    96
    10 
    11 logger = getLogger('django.db.backends')
    12 
    13 
    14 class CursorWrapper(object):
    15     def __init__(self, cursor, db):
    16         self.cursor = cursor
    17         self.db = db
    18 
    19     def set_dirty(self):
    20         if self.db.is_managed():
    21             self.db.set_dirty()
    22 
    23     def __getattr__(self, attr):
    24         self.set_dirty()
    25         if attr in self.__dict__:
    26             return self.__dict__[attr]
    27         else:
    28             return getattr(self.cursor, attr)
    29 
    30     def __iter__(self):
    31         return iter(self.cursor)
    32 
    33 
    34 class CursorDebugWrapper(CursorWrapper):
    35 
    36     def execute(self, sql, params=()):
    37         self.set_dirty()
    38         start = time()
    39         try:
    40             return self.cursor.execute(sql, params)
    41         finally:
    42             stop = time()
    43             duration = stop - start
    44             sql = self.db.ops.last_executed_query(self.cursor, sql, params)
    45             self.db.queries.append({
    46                 'sql': sql,
    47                 'time': "%.3f" % duration,
    48             })
    49             logger.debug('(%.3f) %s; args=%s' % (duration, sql, params),
    50                 extra={'duration': duration, 'sql': sql, 'params': params}
    51             )
    52 
    53     def executemany(self, sql, param_list):
    54         self.set_dirty()
    55         start = time()
    56         try:
    57             return self.cursor.executemany(sql, param_list)
    58         finally:
    59             stop = time()
    60             duration = stop - start
    61             try:
    62                 times = len(param_list)
    63             except TypeError:           # param_list could be an iterator
    64                 times = '?'
    65             self.db.queries.append({
    66                 'sql': '%s times: %s' % (times, sql),
    67                 'time': "%.3f" % duration,
    68             })
    69             logger.debug('(%.3f) %s; args=%s' % (duration, sql, param_list),
    70                 extra={'duration': duration, 'sql': sql, 'params': param_list}
    71             )
    72 
    73 
    74 ###############################################
    75 # Converters from database (string) to Python #
    76 ###############################################
    77 
    78 def typecast_date(s):
    79     return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null
    80 
    81 def typecast_time(s): # does NOT store time zone information
    82     if not s: return None
    83     hour, minutes, seconds = s.split(':')
    84     if '.' in seconds: # check whether seconds have a fractional part
    85         seconds, microseconds = seconds.split('.')
    86     else:
    87         microseconds = '0'
    88     return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000))
    89 
    90 def typecast_timestamp(s): # does NOT store time zone information
    91     # "2005-07-29 15:48:00.590358-05"
    92     # "2005-07-29 09:56:00-05"
    93     if not s: return None
    94     if not ' ' in s: return typecast_date(s)
    95     d, t = s.split()
    96     # Extract timezone information, if it exists. Currently we just throw
    97     # it away, but in the future we may make use of it.
    98     if '-' in t:
    99         t, tz = t.split('-', 1)
    100         tz = '-' + tz
    101     elif '+' in t:
    102         t, tz = t.split('+', 1)
    103         tz = '+' + tz
    104     else:
    105         tz = ''
    106     dates = d.split('-')
    107     times = t.split(':')
    108     seconds = times[2]
    109     if '.' in seconds: # check whether seconds have a fractional part
    110         seconds, microseconds = seconds.split('.')
    111     else:
    112         microseconds = '0'
    113     tzinfo = utc if settings.USE_TZ else None
    114     return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]),
    115         int(times[0]), int(times[1]), int(seconds),
    116         int((microseconds + '000000')[:6]), tzinfo)
    117 
    118 def typecast_decimal(s):
    119     if s is None or s == '':
    120         return None
    121     return decimal.Decimal(s)
    122 
    123 ###############################################
    124 # Converters from Python to database (string) #
    125 ###############################################
    126 
    127 def rev_typecast_decimal(d):
    128     if d is None:
    129         return None
    130     return str(d)
    131 
    132 def truncate_name(name, length=None, hash_len=4):
    133     """Shortens a string to a repeatable mangled version with the given length.
    134     """
    135     if length is None or len(name) <= length:
    136         return name
    137 
    138     hsh = hashlib.md5(name).hexdigest()[:hash_len]
    139     return '%s%s' % (name[:length-hash_len], hsh)
    140 
    141 def format_number(value, max_digits, decimal_places):
    142     """
    143     Formats a number into a string with the requisite number of digits and
    144     decimal places.
    145     """
    146     if isinstance(value, decimal.Decimal):
    147         context = decimal.getcontext().copy()
    148         context.prec = max_digits
    149         return u'%s' % str(value.quantize(decimal.Decimal(".1") ** decimal_places, context=context))
    150     else:
    151         return u"%.*f" % (decimal_places, value)
     7from django.db.backends.utils import *
  • new file django/db/backends/utils.py

    diff --git a/django/db/backends/utils.py b/django/db/backends/utils.py
    new file mode 100644
    index 0000000..32e0f4f
    - +  
     1import datetime
     2import decimal
     3import hashlib
     4from time import time
     5
     6from django.conf import settings
     7from django.utils.log import getLogger
     8from django.utils.timezone import utc
     9
     10
     11logger = getLogger('django.db.backends')
     12
     13
     14class CursorWrapper(object):
     15    def __init__(self, cursor, db):
     16        self.cursor = cursor
     17        self.db = db
     18
     19    def set_dirty(self):
     20        if self.db.is_managed():
     21            self.db.set_dirty()
     22
     23    def __getattr__(self, attr):
     24        self.set_dirty()
     25        if attr in self.__dict__:
     26            return self.__dict__[attr]
     27        else:
     28            return getattr(self.cursor, attr)
     29
     30    def __iter__(self):
     31        return iter(self.cursor)
     32
     33
     34class CursorDebugWrapper(CursorWrapper):
     35
     36    def execute(self, sql, params=()):
     37        self.set_dirty()
     38        start = time()
     39        try:
     40            return self.cursor.execute(sql, params)
     41        finally:
     42            stop = time()
     43            duration = stop - start
     44            sql = self.db.ops.last_executed_query(self.cursor, sql, params)
     45            self.db.queries.append({
     46                'sql': sql,
     47                'time': "%.3f" % duration,
     48            })
     49            logger.debug('(%.3f) %s; args=%s' % (duration, sql, params),
     50                extra={'duration': duration, 'sql': sql, 'params': params}
     51            )
     52
     53    def executemany(self, sql, param_list):
     54        self.set_dirty()
     55        start = time()
     56        try:
     57            return self.cursor.executemany(sql, param_list)
     58        finally:
     59            stop = time()
     60            duration = stop - start
     61            try:
     62                times = len(param_list)
     63            except TypeError:           # param_list could be an iterator
     64                times = '?'
     65            self.db.queries.append({
     66                'sql': '%s times: %s' % (times, sql),
     67                'time': "%.3f" % duration,
     68            })
     69            logger.debug('(%.3f) %s; args=%s' % (duration, sql, param_list),
     70                extra={'duration': duration, 'sql': sql, 'params': param_list}
     71            )
     72
     73
     74###############################################
     75# Converters from database (string) to Python #
     76###############################################
     77
     78def typecast_date(s):
     79    return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null
     80
     81def typecast_time(s): # does NOT store time zone information
     82    if not s: return None
     83    hour, minutes, seconds = s.split(':')
     84    if '.' in seconds: # check whether seconds have a fractional part
     85        seconds, microseconds = seconds.split('.')
     86    else:
     87        microseconds = '0'
     88    return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000))
     89
     90def typecast_timestamp(s): # does NOT store time zone information
     91    # "2005-07-29 15:48:00.590358-05"
     92    # "2005-07-29 09:56:00-05"
     93    if not s: return None
     94    if not ' ' in s: return typecast_date(s)
     95    d, t = s.split()
     96    # Extract timezone information, if it exists. Currently we just throw
     97    # it away, but in the future we may make use of it.
     98    if '-' in t:
     99        t, tz = t.split('-', 1)
     100        tz = '-' + tz
     101    elif '+' in t:
     102        t, tz = t.split('+', 1)
     103        tz = '+' + tz
     104    else:
     105        tz = ''
     106    dates = d.split('-')
     107    times = t.split(':')
     108    seconds = times[2]
     109    if '.' in seconds: # check whether seconds have a fractional part
     110        seconds, microseconds = seconds.split('.')
     111    else:
     112        microseconds = '0'
     113    tzinfo = utc if settings.USE_TZ else None
     114    return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]),
     115        int(times[0]), int(times[1]), int(seconds),
     116        int((microseconds + '000000')[:6]), tzinfo)
     117
     118def typecast_decimal(s):
     119    if s is None or s == '':
     120        return None
     121    return decimal.Decimal(s)
     122
     123###############################################
     124# Converters from Python to database (string) #
     125###############################################
     126
     127def rev_typecast_decimal(d):
     128    if d is None:
     129        return None
     130    return str(d)
     131
     132def truncate_name(name, length=None, hash_len=4):
     133    """Shortens a string to a repeatable mangled version with the given length.
     134    """
     135    if length is None or len(name) <= length:
     136        return name
     137
     138    hsh = hashlib.md5(name).hexdigest()[:hash_len]
     139    return '%s%s' % (name[:length-hash_len], hsh)
     140
     141def format_number(value, max_digits, decimal_places):
     142    """
     143    Formats a number into a string with the requisite number of digits and
     144    decimal places.
     145    """
     146    if isinstance(value, decimal.Decimal):
     147        context = decimal.getcontext().copy()
     148        context.prec = max_digits
     149        return u'%s' % str(value.quantize(decimal.Decimal(".1") ** decimal_places, context=context))
     150    else:
     151        return u"%.*f" % (decimal_places, value)
  • django/db/models/fields/__init__.py

    diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
    index 22546c2..debdacf 100644
    a b class DecimalField(Field):  
    860860        Formats a number into a string with the requisite number of digits and
    861861        decimal places.
    862862        """
    863         # Method moved to django.db.backends.util.
     863        # Method moved to django.db.backends.utils.
    864864        #
    865865        # It is preserved because it is used by the oracle backend
    866866        # (django.db.backends.oracle.query), and also for
    867867        # backwards-compatibility with any external code which may have used
    868868        # this method.
    869         from django.db.backends import util
    870         return util.format_number(value, self.max_digits, self.decimal_places)
     869        from django.db.backends import utils
     870        return utils.format_number(value, self.max_digits, self.decimal_places)
    871871
    872872    def get_db_prep_save(self, value, connection):
    873873        return connection.ops.value_to_db_decimal(self.to_python(value),
  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index e23c7dc..ab0c74e 100644
    a b  
    11from operator import attrgetter
    22
    33from django.db import connection, router
    4 from django.db.backends import util
     4from django.db.backends import utils
    55from django.db.models import signals, get_model
    66from django.db.models.fields import (AutoField, Field, IntegerField,
    77    PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist)
    class ManyToManyField(RelatedField, Field):  
    11301130        elif self.db_table:
    11311131            return self.db_table
    11321132        else:
    1133             return util.truncate_name('%s_%s' % (opts.db_table, self.name),
     1133            return utils.truncate_name('%s_%s' % (opts.db_table, self.name),
    11341134                                      connection.ops.max_name_length())
    11351135
    11361136    def _get_m2m_attr(self, related, attr):
  • django/db/models/options.py

    diff --git a/django/db/models/options.py b/django/db/models/options.py
    index e13a496..4bf45d7 100644
    a b class Options(object):  
    6565
    6666    def contribute_to_class(self, cls, name):
    6767        from django.db import connection
    68         from django.db.backends.util import truncate_name
     68        from django.db.backends.utils import truncate_name
    6969
    7070        cls._meta = self
    7171        self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
  • django/db/models/query_utils.py

    diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
    index a56ab5c..8e9d770 100644
    a b circular import difficulties.  
    88
    99import weakref
    1010
    11 from django.db.backends import util
     11from django.db.backends import utils
    1212from django.utils import tree
    1313
    1414
    def deferred_class_factory(model, attrs):  
    155155    # name using the passed in attrs. It's OK to reuse an existing class
    156156    # object if the attrs are identical.
    157157    name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
    158     name = util.truncate_name(name, 80, 32)
     158    name = utils.truncate_name(name, 80, 32)
    159159
    160160    overrides = dict([(attr, DeferredAttribute(attr, model))
    161161            for attr in attrs])
  • django/db/models/sql/compiler.py

    diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
    index 6c516e2..95841cd 100644
    a b from itertools import izip  
    22
    33from django.core.exceptions import FieldError
    44from django.db import transaction
    5 from django.db.backends.util import truncate_name
     5from django.db.backends.utils import truncate_name
    66from django.db.models.query_utils import select_related_descend
    77from django.db.models.sql.constants import *
    88from django.db.models.sql.datastructures import EmptyResultSet
    class SQLDateCompiler(SQLCompiler):  
    10671067            from django.db.models.fields import DateTimeField
    10681068            fields = [DateTimeField()]
    10691069        else:
    1070             from django.db.backends.util import typecast_timestamp
     1070            from django.db.backends.utils import typecast_timestamp
    10711071            needs_string_cast = self.connection.features.needs_datetime_string_cast
    10721072
    10731073        offset = len(self.query.extra_select)
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index 96ecabf..61f57e0 100644
    a b except ImportError:  
    1717
    1818from django.core import validators
    1919from django.core.exceptions import ValidationError
    20 from django.forms.util import ErrorList, from_current_timezone, to_current_timezone
     20from django.forms.utils import ErrorList, from_current_timezone, to_current_timezone
    2121from django.forms.widgets import (TextInput, PasswordInput, HiddenInput,
    2222    MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
    2323    NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
  • django/forms/forms.py

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index 94eb22d..155ee90 100644
    a b import copy  
    88
    99from django.core.exceptions import ValidationError
    1010from django.forms.fields import Field, FileField
    11 from django.forms.util import flatatt, ErrorDict, ErrorList
     11from django.forms.utils import flatatt, ErrorDict, ErrorList
    1212from django.forms.widgets import Media, media_property, TextInput, Textarea
    1313from django.utils.datastructures import SortedDict
    1414from django.utils.html import conditional_escape
  • django/forms/formsets.py

    diff --git a/django/forms/formsets.py b/django/forms/formsets.py
    index dcd2f01..2736ed8 100644
    a b from __future__ import absolute_import  
    33from django.core.exceptions import ValidationError
    44from django.forms import Form
    55from django.forms.fields import IntegerField, BooleanField
    6 from django.forms.util import ErrorList
     6from django.forms.utils import ErrorList
    77from django.forms.widgets import Media, HiddenInput
    88from django.utils.encoding import StrAndUnicode
    99from django.utils.safestring import mark_safe
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index cd8f027..730f3ee 100644
    a b from django.core.validators import EMPTY_VALUES  
    1010from django.forms.fields import Field, ChoiceField
    1111from django.forms.forms import BaseForm, get_declared_fields
    1212from django.forms.formsets import BaseFormSet, formset_factory
    13 from django.forms.util import ErrorList
     13from django.forms.utils import ErrorList
    1414from django.forms.widgets import (SelectMultiple, HiddenInput,
    1515    MultipleHiddenInput, media_property)
    1616from django.utils.encoding import smart_unicode, force_unicode
  • django/forms/util.py

    diff --git a/django/forms/util.py b/django/forms/util.py
    index 886f08e..6b14b92 100644
    a b  
    1 from django.conf import settings
    2 from django.utils.html import conditional_escape
    3 from django.utils.encoding import StrAndUnicode, force_unicode
    4 from django.utils.safestring import mark_safe
    5 from django.utils import timezone
    6 from django.utils.translation import ugettext_lazy as _
     1import warnings
    72
    8 # Import ValidationError so that it can be imported from this
    9 # module to maintain backwards compatibility.
    10 from django.core.exceptions import ValidationError
     3warnings.warn("The django.forms.util module has been renamed "
     4              "(https://code.djangoproject.com/ticket/17627/). "
     5              "Use django.forms.utils instead.", PendingDeprecationWarning)
    116
    12 def flatatt(attrs):
    13     """
    14     Convert a dictionary of attributes to a single string.
    15     The returned string will contain a leading space followed by key="value",
    16     XML-style pairs.  It is assumed that the keys do not need to be XML-escaped.
    17     If the passed dictionary is empty, then return an empty string.
    18     """
    19     return u''.join([u' %s="%s"' % (k, conditional_escape(v)) for k, v in attrs.items()])
    20 
    21 class ErrorDict(dict, StrAndUnicode):
    22     """
    23     A collection of errors that knows how to display itself in various formats.
    24 
    25     The dictionary keys are the field names, and the values are the errors.
    26     """
    27     def __unicode__(self):
    28         return self.as_ul()
    29 
    30     def as_ul(self):
    31         if not self: return u''
    32         return mark_safe(u'<ul class="errorlist">%s</ul>'
    33                 % ''.join([u'<li>%s%s</li>' % (k, conditional_escape(force_unicode(v)))
    34                     for k, v in self.items()]))
    35 
    36     def as_text(self):
    37         return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u'  * %s' % force_unicode(i) for i in v])) for k, v in self.items()])
    38 
    39 class ErrorList(list, StrAndUnicode):
    40     """
    41     A collection of errors that knows how to display itself in various formats.
    42     """
    43     def __unicode__(self):
    44         return self.as_ul()
    45 
    46     def as_ul(self):
    47         if not self: return u''
    48         return mark_safe(u'<ul class="errorlist">%s</ul>'
    49                 % ''.join([u'<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self]))
    50 
    51     def as_text(self):
    52         if not self: return u''
    53         return u'\n'.join([u'* %s' % force_unicode(e) for e in self])
    54 
    55     def __repr__(self):
    56         return repr([force_unicode(e) for e in self])
    57 
    58 # Utilities for time zone support in DateTimeField et al.
    59 
    60 def from_current_timezone(value):
    61     """
    62     When time zone support is enabled, convert naive datetimes
    63     entered in the current time zone to aware datetimes.
    64     """
    65     if settings.USE_TZ and value is not None and timezone.is_naive(value):
    66         current_timezone = timezone.get_current_timezone()
    67         try:
    68             return timezone.make_aware(value, current_timezone)
    69         except Exception, e:
    70             raise ValidationError(_('%(datetime)s couldn\'t be interpreted '
    71                                     'in time zone %(current_timezone)s; it '
    72                                     'may be ambiguous or it may not exist.')
    73                                   % {'datetime': value,
    74                                      'current_timezone': current_timezone})
    75     return value
    76 
    77 def to_current_timezone(value):
    78     """
    79     When time zone support is enabled, convert aware datetimes
    80     to naive dateimes in the current time zone for display.
    81     """
    82     if settings.USE_TZ and value is not None and timezone.is_aware(value):
    83         current_timezone = timezone.get_current_timezone()
    84         return timezone.make_naive(value, current_timezone)
    85     return value
     7from django.forms.utils import *
  • new file django/forms/utils.py

    diff --git a/django/forms/utils.py b/django/forms/utils.py
    new file mode 100644
    index 0000000..886f08e
    - +  
     1from django.conf import settings
     2from django.utils.html import conditional_escape
     3from django.utils.encoding import StrAndUnicode, force_unicode
     4from django.utils.safestring import mark_safe
     5from django.utils import timezone
     6from django.utils.translation import ugettext_lazy as _
     7
     8# Import ValidationError so that it can be imported from this
     9# module to maintain backwards compatibility.
     10from django.core.exceptions import ValidationError
     11
     12def flatatt(attrs):
     13    """
     14    Convert a dictionary of attributes to a single string.
     15    The returned string will contain a leading space followed by key="value",
     16    XML-style pairs.  It is assumed that the keys do not need to be XML-escaped.
     17    If the passed dictionary is empty, then return an empty string.
     18    """
     19    return u''.join([u' %s="%s"' % (k, conditional_escape(v)) for k, v in attrs.items()])
     20
     21class ErrorDict(dict, StrAndUnicode):
     22    """
     23    A collection of errors that knows how to display itself in various formats.
     24
     25    The dictionary keys are the field names, and the values are the errors.
     26    """
     27    def __unicode__(self):
     28        return self.as_ul()
     29
     30    def as_ul(self):
     31        if not self: return u''
     32        return mark_safe(u'<ul class="errorlist">%s</ul>'
     33                % ''.join([u'<li>%s%s</li>' % (k, conditional_escape(force_unicode(v)))
     34                    for k, v in self.items()]))
     35
     36    def as_text(self):
     37        return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u'  * %s' % force_unicode(i) for i in v])) for k, v in self.items()])
     38
     39class ErrorList(list, StrAndUnicode):
     40    """
     41    A collection of errors that knows how to display itself in various formats.
     42    """
     43    def __unicode__(self):
     44        return self.as_ul()
     45
     46    def as_ul(self):
     47        if not self: return u''
     48        return mark_safe(u'<ul class="errorlist">%s</ul>'
     49                % ''.join([u'<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self]))
     50
     51    def as_text(self):
     52        if not self: return u''
     53        return u'\n'.join([u'* %s' % force_unicode(e) for e in self])
     54
     55    def __repr__(self):
     56        return repr([force_unicode(e) for e in self])
     57
     58# Utilities for time zone support in DateTimeField et al.
     59
     60def from_current_timezone(value):
     61    """
     62    When time zone support is enabled, convert naive datetimes
     63    entered in the current time zone to aware datetimes.
     64    """
     65    if settings.USE_TZ and value is not None and timezone.is_naive(value):
     66        current_timezone = timezone.get_current_timezone()
     67        try:
     68            return timezone.make_aware(value, current_timezone)
     69        except Exception, e:
     70            raise ValidationError(_('%(datetime)s couldn\'t be interpreted '
     71                                    'in time zone %(current_timezone)s; it '
     72                                    'may be ambiguous or it may not exist.')
     73                                  % {'datetime': value,
     74                                     'current_timezone': current_timezone})
     75    return value
     76
     77def to_current_timezone(value):
     78    """
     79    When time zone support is enabled, convert aware datetimes
     80    to naive dateimes in the current time zone for display.
     81    """
     82    if settings.USE_TZ and value is not None and timezone.is_aware(value):
     83        current_timezone = timezone.get_current_timezone()
     84        return timezone.make_naive(value, current_timezone)
     85    return value
  • django/forms/widgets.py

    diff --git a/django/forms/widgets.py b/django/forms/widgets.py
    index 1fbef98..74e3043 100644
    a b from itertools import chain  
    1010from urlparse import urljoin
    1111
    1212from django.conf import settings
    13 from django.forms.util import flatatt, to_current_timezone
     13from django.forms.utils import flatatt, to_current_timezone
    1414from django.utils.datastructures import MultiValueDict, MergeDict
    1515from django.utils.html import escape, conditional_escape
    1616from django.utils.translation import ugettext, ugettext_lazy
  • docs/internals/deprecation.txt

    diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
    index cb91a1c..dfc4cf4 100644
    a b these changes.  
    271271  in 1.4. The backward compatibility will be removed --
    272272  ``HttpRequest.raw_post_data`` will no longer work.
    273273
     2741.7
     275---
     276
     277* Instances of ``util.py`` in the Django codebase have been renamed to ``utils.py`` in
     278  an effort to unify all util and utils references across Django. Compatibility modules
     279  for ``django.contrib.admin.util`` (now :mod:`django.contrib.admin.utils`),
     280  ``django.contrib.gis.db.backends.util`` (now :mod:`django.contrib.gis.db.backends.utils``),
     281  ``django.contrib.localflavor.it.util`` (now :mod:`django.contrib.localflavor.it.utils``),
     282  ``django.contrib.localflavor.uy.util`` (now :mod:`django.contrib.localflavor.uy.utils``),
     283  ``django.db.backends.util`` (now :mod:`django.db.backends.utils`),
     284  and ``django.forms.util`` (now :mod:`django.forms.utils`) will be removed in the 1.8
     285  release.
     286
    2742872.0
    275288---
    276289
  • deleted file tests/regressiontests/admin_util/models.py

    diff --git a/tests/regressiontests/admin_util/__init__.py b/tests/regressiontests/admin_util/__init__.py
    deleted file mode 100644
    index e69de29..0000000
    diff --git a/tests/regressiontests/admin_util/models.py b/tests/regressiontests/admin_util/models.py
    deleted file mode 100644
    index 0e81df3..0000000
    + -  
    1 from django.db import models
    2 
    3 
    4 class Article(models.Model):
    5     """
    6     A simple Article model for testing
    7     """
    8     site = models.ForeignKey('sites.Site', related_name="admin_articles")
    9     title = models.CharField(max_length=100)
    10     title2 = models.CharField(max_length=100, verbose_name="another name")
    11     created = models.DateTimeField()
    12 
    13     def test_from_model(self):
    14         return "nothing"
    15 
    16     def test_from_model_with_override(self):
    17         return "nothing"
    18     test_from_model_with_override.short_description = "not What you Expect"
    19 
    20 class Count(models.Model):
    21     num = models.PositiveSmallIntegerField()
    22     parent = models.ForeignKey('self', null=True)
    23 
    24     def __unicode__(self):
    25         return unicode(self.num)
    26 
    27 class Event(models.Model):
    28     date = models.DateTimeField(auto_now_add=True)
    29 
    30 class Location(models.Model):
    31     event = models.OneToOneField(Event, verbose_name='awesome event')
    32 
    33 class Guest(models.Model):
    34     event = models.OneToOneField(Event)
    35     name = models.CharField(max_length=255)
    36 
    37     class Meta:
    38         verbose_name = "awesome guest"
  • deleted file tests/regressiontests/admin_util/tests.py

    diff --git a/tests/regressiontests/admin_util/tests.py b/tests/regressiontests/admin_util/tests.py
    deleted file mode 100644
    index 8113f2e..0000000
    + -  
    1 from __future__ import absolute_import
    2 
    3 from datetime import datetime
    4 
    5 from django.conf import settings
    6 from django.contrib import admin
    7 from django.contrib.admin import helpers
    8 from django.contrib.admin.util import (display_for_field, label_for_field,
    9     lookup_field, NestedObjects)
    10 from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
    11 from django.contrib.sites.models import Site
    12 from django.db import models, DEFAULT_DB_ALIAS
    13 from django import forms
    14 from django.test import TestCase
    15 from django.utils import unittest
    16 from django.utils.formats import localize
    17 from django.utils.safestring import mark_safe
    18 
    19 from .models import Article, Count, Event, Location
    20 
    21 
    22 class NestedObjectsTests(TestCase):
    23     """
    24     Tests for ``NestedObject`` utility collection.
    25 
    26     """
    27     def setUp(self):
    28         self.n = NestedObjects(using=DEFAULT_DB_ALIAS)
    29         self.objs = [Count.objects.create(num=i) for i in range(5)]
    30 
    31     def _check(self, target):
    32         self.assertEqual(self.n.nested(lambda obj: obj.num), target)
    33 
    34     def _connect(self, i, j):
    35         self.objs[i].parent = self.objs[j]
    36         self.objs[i].save()
    37 
    38     def _collect(self, *indices):
    39         self.n.collect([self.objs[i] for i in indices])
    40 
    41     def test_unrelated_roots(self):
    42         self._connect(2, 1)
    43         self._collect(0)
    44         self._collect(1)
    45         self._check([0, 1, [2]])
    46 
    47     def test_siblings(self):
    48         self._connect(1, 0)
    49         self._connect(2, 0)
    50         self._collect(0)
    51         self._check([0, [1, 2]])
    52 
    53     def test_non_added_parent(self):
    54         self._connect(0, 1)
    55         self._collect(0)
    56         self._check([0])
    57 
    58     def test_cyclic(self):
    59         self._connect(0, 2)
    60         self._connect(1, 0)
    61         self._connect(2, 1)
    62         self._collect(0)
    63         self._check([0, [1, [2]]])
    64 
    65     def test_queries(self):
    66         self._connect(1, 0)
    67         self._connect(2, 0)
    68         # 1 query to fetch all children of 0 (1 and 2)
    69         # 1 query to fetch all children of 1 and 2 (none)
    70         # Should not require additional queries to populate the nested graph.
    71         self.assertNumQueries(2, self._collect, 0)
    72 
    73 class UtilTests(unittest.TestCase):
    74     def test_values_from_lookup_field(self):
    75         """
    76         Regression test for #12654: lookup_field
    77         """
    78         SITE_NAME = 'example.com'
    79         TITLE_TEXT = 'Some title'
    80         CREATED_DATE = datetime.min
    81         ADMIN_METHOD = 'admin method'
    82         SIMPLE_FUNCTION = 'function'
    83         INSTANCE_ATTRIBUTE = 'attr'
    84 
    85         class MockModelAdmin(object):
    86             def get_admin_value(self, obj):
    87                 return ADMIN_METHOD
    88 
    89         simple_function = lambda obj: SIMPLE_FUNCTION
    90 
    91         article = Article(
    92             site=Site(domain=SITE_NAME),
    93             title=TITLE_TEXT,
    94             created=CREATED_DATE,
    95         )
    96         article.non_field = INSTANCE_ATTRIBUTE
    97 
    98         verifications = (
    99             ('site', SITE_NAME),
    100             ('created', localize(CREATED_DATE)),
    101             ('title', TITLE_TEXT),
    102             ('get_admin_value', ADMIN_METHOD),
    103             (simple_function, SIMPLE_FUNCTION),
    104             ('test_from_model', article.test_from_model()),
    105             ('non_field', INSTANCE_ATTRIBUTE)
    106         )
    107 
    108         mock_admin = MockModelAdmin()
    109         for name, value in verifications:
    110             field, attr, resolved_value = lookup_field(name, article, mock_admin)
    111 
    112             if field is not None:
    113                 resolved_value = display_for_field(resolved_value, field)
    114 
    115             self.assertEqual(value, resolved_value)
    116 
    117     def test_null_display_for_field(self):
    118         """
    119         Regression test for #12550: display_for_field should handle None
    120         value.
    121         """
    122         display_value = display_for_field(None, models.CharField())
    123         self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
    124 
    125         display_value = display_for_field(None, models.CharField(
    126             choices=(
    127                 (None, "test_none"),
    128             )
    129         ))
    130         self.assertEqual(display_value, "test_none")
    131 
    132         display_value = display_for_field(None, models.DateField())
    133         self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
    134 
    135         display_value = display_for_field(None, models.TimeField())
    136         self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
    137 
    138         # Regression test for #13071: NullBooleanField has special
    139         # handling.
    140         display_value = display_for_field(None, models.NullBooleanField())
    141         expected = u'<img src="%sadmin/img/icon-unknown.gif" alt="None" />' % settings.STATIC_URL
    142         self.assertEqual(display_value, expected)
    143 
    144         display_value = display_for_field(None, models.DecimalField())
    145         self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
    146 
    147         display_value = display_for_field(None, models.FloatField())
    148         self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
    149 
    150     def test_label_for_field(self):
    151         """
    152         Tests for label_for_field
    153         """
    154         self.assertEqual(
    155             label_for_field("title", Article),
    156             "title"
    157         )
    158         self.assertEqual(
    159             label_for_field("title2", Article),
    160             "another name"
    161         )
    162         self.assertEqual(
    163             label_for_field("title2", Article, return_attr=True),
    164             ("another name", None)
    165         )
    166 
    167         self.assertEqual(
    168             label_for_field("__unicode__", Article),
    169             "article"
    170         )
    171         self.assertEqual(
    172             label_for_field("__str__", Article),
    173             "article"
    174         )
    175 
    176         self.assertRaises(
    177             AttributeError,
    178             lambda: label_for_field("unknown", Article)
    179         )
    180 
    181         def test_callable(obj):
    182             return "nothing"
    183         self.assertEqual(
    184             label_for_field(test_callable, Article),
    185             "Test callable"
    186         )
    187         self.assertEqual(
    188             label_for_field(test_callable, Article, return_attr=True),
    189             ("Test callable", test_callable)
    190         )
    191 
    192         self.assertEqual(
    193             label_for_field("test_from_model", Article),
    194             "Test from model"
    195         )
    196         self.assertEqual(
    197             label_for_field("test_from_model", Article, return_attr=True),
    198             ("Test from model", Article.test_from_model)
    199         )
    200         self.assertEqual(
    201             label_for_field("test_from_model_with_override", Article),
    202             "not What you Expect"
    203         )
    204 
    205         self.assertEqual(
    206             label_for_field(lambda x: "nothing", Article),
    207             "--"
    208         )
    209 
    210         class MockModelAdmin(object):
    211             def test_from_model(self, obj):
    212                 return "nothing"
    213             test_from_model.short_description = "not Really the Model"
    214 
    215         self.assertEqual(
    216             label_for_field("test_from_model", Article, model_admin=MockModelAdmin),
    217             "not Really the Model"
    218         )
    219         self.assertEqual(
    220             label_for_field("test_from_model", Article,
    221                 model_admin = MockModelAdmin,
    222                 return_attr = True
    223             ),
    224             ("not Really the Model", MockModelAdmin.test_from_model)
    225         )
    226 
    227     def test_related_name(self):
    228         """
    229         Regression test for #13963
    230         """
    231         self.assertEqual(
    232             label_for_field('location', Event, return_attr=True),
    233             ('location', None),
    234         )
    235         self.assertEqual(
    236             label_for_field('event', Location, return_attr=True),
    237             ('awesome event', None),
    238         )
    239         self.assertEqual(
    240             label_for_field('guest', Event, return_attr=True),
    241             ('awesome guest', None),
    242         )
    243 
    244     def test_logentry_unicode(self):
    245         """
    246         Regression test for #15661
    247         """
    248         log_entry = admin.models.LogEntry()
    249 
    250         log_entry.action_flag = admin.models.ADDITION
    251         self.assertTrue(
    252             unicode(log_entry).startswith('Added ')
    253         )
    254 
    255         log_entry.action_flag = admin.models.CHANGE
    256         self.assertTrue(
    257             unicode(log_entry).startswith('Changed ')
    258         )
    259 
    260         log_entry.action_flag = admin.models.DELETION
    261         self.assertTrue(
    262             unicode(log_entry).startswith('Deleted ')
    263         )
    264 
    265     def test_safestring_in_field_label(self):
    266         # safestring should not be escaped
    267         class MyForm(forms.Form):
    268             text = forms.CharField(label=mark_safe('<i>text</i>'))
    269             cb   = forms.BooleanField(label=mark_safe('<i>cb</i>'))
    270 
    271         form = MyForm()
    272         self.assertEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
    273                          '<label for="id_text" class="required inline"><i>text</i>:</label>')
    274         self.assertEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
    275                          '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>')
    276 
    277         # normal strings needs to be escaped
    278         class MyForm(forms.Form):
    279             text = forms.CharField(label='&text')
    280             cb   = forms.BooleanField(label='&cb')
    281 
    282         form = MyForm()
    283         self.assertEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
    284                          '<label for="id_text" class="required inline">&amp;text:</label>')
    285         self.assertEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
    286                          '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb</label>')
  • new file tests/regressiontests/admin_utils/models.py

    diff --git a/tests/regressiontests/admin_utils/__init__.py b/tests/regressiontests/admin_utils/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/tests/regressiontests/admin_utils/models.py b/tests/regressiontests/admin_utils/models.py
    new file mode 100644
    index 0000000..0e81df3
    - +  
     1from django.db import models
     2
     3
     4class Article(models.Model):
     5    """
     6    A simple Article model for testing
     7    """
     8    site = models.ForeignKey('sites.Site', related_name="admin_articles")
     9    title = models.CharField(max_length=100)
     10    title2 = models.CharField(max_length=100, verbose_name="another name")
     11    created = models.DateTimeField()
     12
     13    def test_from_model(self):
     14        return "nothing"
     15
     16    def test_from_model_with_override(self):
     17        return "nothing"
     18    test_from_model_with_override.short_description = "not What you Expect"
     19
     20class Count(models.Model):
     21    num = models.PositiveSmallIntegerField()
     22    parent = models.ForeignKey('self', null=True)
     23
     24    def __unicode__(self):
     25        return unicode(self.num)
     26
     27class Event(models.Model):
     28    date = models.DateTimeField(auto_now_add=True)
     29
     30class Location(models.Model):
     31    event = models.OneToOneField(Event, verbose_name='awesome event')
     32
     33class Guest(models.Model):
     34    event = models.OneToOneField(Event)
     35    name = models.CharField(max_length=255)
     36
     37    class Meta:
     38        verbose_name = "awesome guest"
  • new file tests/regressiontests/admin_utils/tests.py

    diff --git a/tests/regressiontests/admin_utils/tests.py b/tests/regressiontests/admin_utils/tests.py
    new file mode 100644
    index 0000000..d7eaabe
    - +  
     1from __future__ import absolute_import
     2
     3from datetime import datetime
     4
     5from django.conf import settings
     6from django.contrib import admin
     7from django.contrib.admin import helpers
     8from django.contrib.admin.utils import (display_for_field, label_for_field,
     9    lookup_field, NestedObjects)
     10from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
     11from django.contrib.sites.models import Site
     12from django.db import models, DEFAULT_DB_ALIAS
     13from django import forms
     14from django.test import TestCase
     15from django.utils import unittest
     16from django.utils.formats import localize
     17from django.utils.safestring import mark_safe
     18
     19from .models import Article, Count, Event, Location
     20
     21
     22class NestedObjectsTests(TestCase):
     23    """
     24    Tests for ``NestedObject`` utility collection.
     25
     26    """
     27    def setUp(self):
     28        self.n = NestedObjects(using=DEFAULT_DB_ALIAS)
     29        self.objs = [Count.objects.create(num=i) for i in range(5)]
     30
     31    def _check(self, target):
     32        self.assertEqual(self.n.nested(lambda obj: obj.num), target)
     33
     34    def _connect(self, i, j):
     35        self.objs[i].parent = self.objs[j]
     36        self.objs[i].save()
     37
     38    def _collect(self, *indices):
     39        self.n.collect([self.objs[i] for i in indices])
     40
     41    def test_unrelated_roots(self):
     42        self._connect(2, 1)
     43        self._collect(0)
     44        self._collect(1)
     45        self._check([0, 1, [2]])
     46
     47    def test_siblings(self):
     48        self._connect(1, 0)
     49        self._connect(2, 0)
     50        self._collect(0)
     51        self._check([0, [1, 2]])
     52
     53    def test_non_added_parent(self):
     54        self._connect(0, 1)
     55        self._collect(0)
     56        self._check([0])
     57
     58    def test_cyclic(self):
     59        self._connect(0, 2)
     60        self._connect(1, 0)
     61        self._connect(2, 1)
     62        self._collect(0)
     63        self._check([0, [1, [2]]])
     64
     65    def test_queries(self):
     66        self._connect(1, 0)
     67        self._connect(2, 0)
     68        # 1 query to fetch all children of 0 (1 and 2)
     69        # 1 query to fetch all children of 1 and 2 (none)
     70        # Should not require additional queries to populate the nested graph.
     71        self.assertNumQueries(2, self._collect, 0)
     72
     73class UtilTests(unittest.TestCase):
     74    def test_values_from_lookup_field(self):
     75        """
     76        Regression test for #12654: lookup_field
     77        """
     78        SITE_NAME = 'example.com'
     79        TITLE_TEXT = 'Some title'
     80        CREATED_DATE = datetime.min
     81        ADMIN_METHOD = 'admin method'
     82        SIMPLE_FUNCTION = 'function'
     83        INSTANCE_ATTRIBUTE = 'attr'
     84
     85        class MockModelAdmin(object):
     86            def get_admin_value(self, obj):
     87                return ADMIN_METHOD
     88
     89        simple_function = lambda obj: SIMPLE_FUNCTION
     90
     91        article = Article(
     92            site=Site(domain=SITE_NAME),
     93            title=TITLE_TEXT,
     94            created=CREATED_DATE,
     95        )
     96        article.non_field = INSTANCE_ATTRIBUTE
     97
     98        verifications = (
     99            ('site', SITE_NAME),
     100            ('created', localize(CREATED_DATE)),
     101            ('title', TITLE_TEXT),
     102            ('get_admin_value', ADMIN_METHOD),
     103            (simple_function, SIMPLE_FUNCTION),
     104            ('test_from_model', article.test_from_model()),
     105            ('non_field', INSTANCE_ATTRIBUTE)
     106        )
     107
     108        mock_admin = MockModelAdmin()
     109        for name, value in verifications:
     110            field, attr, resolved_value = lookup_field(name, article, mock_admin)
     111
     112            if field is not None:
     113                resolved_value = display_for_field(resolved_value, field)
     114
     115            self.assertEqual(value, resolved_value)
     116
     117    def test_null_display_for_field(self):
     118        """
     119        Regression test for #12550: display_for_field should handle None
     120        value.
     121        """
     122        display_value = display_for_field(None, models.CharField())
     123        self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
     124
     125        display_value = display_for_field(None, models.CharField(
     126            choices=(
     127                (None, "test_none"),
     128            )
     129        ))
     130        self.assertEqual(display_value, "test_none")
     131
     132        display_value = display_for_field(None, models.DateField())
     133        self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
     134
     135        display_value = display_for_field(None, models.TimeField())
     136        self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
     137
     138        # Regression test for #13071: NullBooleanField has special
     139        # handling.
     140        display_value = display_for_field(None, models.NullBooleanField())
     141        expected = u'<img src="%sadmin/img/icon-unknown.gif" alt="None" />' % settings.STATIC_URL
     142        self.assertEqual(display_value, expected)
     143
     144        display_value = display_for_field(None, models.DecimalField())
     145        self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
     146
     147        display_value = display_for_field(None, models.FloatField())
     148        self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE)
     149
     150    def test_label_for_field(self):
     151        """
     152        Tests for label_for_field
     153        """
     154        self.assertEqual(
     155            label_for_field("title", Article),
     156            "title"
     157        )
     158        self.assertEqual(
     159            label_for_field("title2", Article),
     160            "another name"
     161        )
     162        self.assertEqual(
     163            label_for_field("title2", Article, return_attr=True),
     164            ("another name", None)
     165        )
     166
     167        self.assertEqual(
     168            label_for_field("__unicode__", Article),
     169            "article"
     170        )
     171        self.assertEqual(
     172            label_for_field("__str__", Article),
     173            "article"
     174        )
     175
     176        self.assertRaises(
     177            AttributeError,
     178            lambda: label_for_field("unknown", Article)
     179        )
     180
     181        def test_callable(obj):
     182            return "nothing"
     183        self.assertEqual(
     184            label_for_field(test_callable, Article),
     185            "Test callable"
     186        )
     187        self.assertEqual(
     188            label_for_field(test_callable, Article, return_attr=True),
     189            ("Test callable", test_callable)
     190        )
     191
     192        self.assertEqual(
     193            label_for_field("test_from_model", Article),
     194            "Test from model"
     195        )
     196        self.assertEqual(
     197            label_for_field("test_from_model", Article, return_attr=True),
     198            ("Test from model", Article.test_from_model)
     199        )
     200        self.assertEqual(
     201            label_for_field("test_from_model_with_override", Article),
     202            "not What you Expect"
     203        )
     204
     205        self.assertEqual(
     206            label_for_field(lambda x: "nothing", Article),
     207            "--"
     208        )
     209
     210        class MockModelAdmin(object):
     211            def test_from_model(self, obj):
     212                return "nothing"
     213            test_from_model.short_description = "not Really the Model"
     214
     215        self.assertEqual(
     216            label_for_field("test_from_model", Article, model_admin=MockModelAdmin),
     217            "not Really the Model"
     218        )
     219        self.assertEqual(
     220            label_for_field("test_from_model", Article,
     221                model_admin = MockModelAdmin,
     222                return_attr = True
     223            ),
     224            ("not Really the Model", MockModelAdmin.test_from_model)
     225        )
     226
     227    def test_related_name(self):
     228        """
     229        Regression test for #13963
     230        """
     231        self.assertEqual(
     232            label_for_field('location', Event, return_attr=True),
     233            ('location', None),
     234        )
     235        self.assertEqual(
     236            label_for_field('event', Location, return_attr=True),
     237            ('awesome event', None),
     238        )
     239        self.assertEqual(
     240            label_for_field('guest', Event, return_attr=True),
     241            ('awesome guest', None),
     242        )
     243
     244    def test_logentry_unicode(self):
     245        """
     246        Regression test for #15661
     247        """
     248        log_entry = admin.models.LogEntry()
     249
     250        log_entry.action_flag = admin.models.ADDITION
     251        self.assertTrue(
     252            unicode(log_entry).startswith('Added ')
     253        )
     254
     255        log_entry.action_flag = admin.models.CHANGE
     256        self.assertTrue(
     257            unicode(log_entry).startswith('Changed ')
     258        )
     259
     260        log_entry.action_flag = admin.models.DELETION
     261        self.assertTrue(
     262            unicode(log_entry).startswith('Deleted ')
     263        )
     264
     265    def test_safestring_in_field_label(self):
     266        # safestring should not be escaped
     267        class MyForm(forms.Form):
     268            text = forms.CharField(label=mark_safe('<i>text</i>'))
     269            cb   = forms.BooleanField(label=mark_safe('<i>cb</i>'))
     270
     271        form = MyForm()
     272        self.assertEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
     273                         '<label for="id_text" class="required inline"><i>text</i>:</label>')
     274        self.assertEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
     275                         '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>')
     276
     277        # normal strings needs to be escaped
     278        class MyForm(forms.Form):
     279            text = forms.CharField(label='&text')
     280            cb   = forms.BooleanField(label='&cb')
     281
     282        form = MyForm()
     283        self.assertEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
     284                         '<label for="id_text" class="required inline">&amp;text:</label>')
     285        self.assertEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
     286                         '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb</label>')
  • tests/regressiontests/admin_views/admin.py

    diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
    index d960749..86cd039 100644
    a b site.register(UnorderedObject, UnorderedObjectAdmin)  
    625625#     related OneToOne object not registered in admin
    626626# when deleting Book so as exercise all four troublesome (w.r.t escaping
    627627# and calling force_unicode to avoid problems on Python 2.3) paths through
    628 # contrib.admin.util's get_deleted_objects function.
     628# contrib.admin.utils's get_deleted_objects function.
    629629site.register(Book, inlines=[ChapterInline])
    630630site.register(Promo)
    631631site.register(ChapterXtra1, ChapterXtra1Admin)
  • tests/regressiontests/admin_views/tests.py

    diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
    index 5241d26..0652e81 100755
    a b from django.contrib import admin  
    1616from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
    1717from django.contrib.admin.models import LogEntry, DELETION
    1818from django.contrib.admin.sites import LOGIN_FORM_KEY
    19 from django.contrib.admin.util import quote
     19from django.contrib.admin.utils import quote
    2020from django.contrib.admin.views.main import IS_POPUP_VAR
    2121from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
    2222from django.contrib.auth import REDIRECT_FIELD_NAME
    2323from django.contrib.auth.models import Group, User, Permission, UNUSABLE_PASSWORD
    2424from django.contrib.contenttypes.models import ContentType
    25 from django.forms.util import ErrorList
     25from django.forms.utils import ErrorList
    2626from django.template import context as context_module
    2727from django.template.response import TemplateResponse
    2828from django.test import TestCase
  • tests/regressiontests/db_typecasts/tests.py

    diff --git a/tests/regressiontests/db_typecasts/tests.py b/tests/regressiontests/db_typecasts/tests.py
    index 83bd1e6..c905a6d 100644
    a b  
    22
    33import datetime
    44
    5 from django.db.backends import util as typecasts
     5from django.db.backends import utils as typecasts
    66from django.utils import unittest
    77
    88
  • tests/regressiontests/forms/tests/__init__.py

    diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py
    index 8e2150c..a94b2a0 100644
    a b from .media import FormsMediaTestCase, StaticFormsMediaTestCase  
    1515from .models import (TestTicket12510, ModelFormCallableModelDefault,
    1616    FormsModelTestCase, RelatedModelFormTests)
    1717from .regressions import FormsRegressionsTestCase
    18 from .util import FormsUtilTestCase
     18from .utils import FormsUtilTestCase
    1919from .validators import TestFieldWithValidators
    2020from .widgets import (FormsWidgetTestCase, FormsI18NWidgetsTestCase,
    2121    WidgetTests, ClearableFileInputTests)
  • tests/regressiontests/forms/tests/error_messages.py

    diff --git a/tests/regressiontests/forms/tests/error_messages.py b/tests/regressiontests/forms/tests/error_messages.py
    index b7b4e98..e8c8999 100644
    a b class FormsErrorMessagesTestCase(TestCase, AssertFormErrorsMixin):  
    219219            def clean(self):
    220220                raise ValidationError("I like to be awkward.")
    221221
    222         class CustomErrorList(util.ErrorList):
     222        class CustomErrorList(utils.ErrorList):
    223223            def __unicode__(self):
    224224                return self.as_divs()
    225225
  • tests/regressiontests/forms/tests/extra.py

    diff --git a/tests/regressiontests/forms/tests/extra.py b/tests/regressiontests/forms/tests/extra.py
    index c873af7..87d2c0d 100644
    a b import datetime  
    77from django.conf import settings
    88from django.forms import *
    99from django.forms.extras import SelectDateWidget
    10 from django.forms.util import ErrorList
     10from django.forms.utils import ErrorList
    1111from django.test import TestCase
    1212from django.utils import translation
    1313from django.utils.encoding import force_unicode, smart_unicode
  • tests/regressiontests/forms/tests/fields.py

    diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py
    index 03e0ff6..a1aa199 100644
    a b class FieldsTests(SimpleTestCase):  
    999999                ('/django/forms/formsets.py', 'formsets.py'),
    10001000                ('/django/forms/models.py', 'models.py'),
    10011001                ('/django/forms/util.py', 'util.py'),
     1002                ('/django/forms/utils.py', 'utils.py'),
    10021003                ('/django/forms/widgets.py', 'widgets.py')
    10031004            ]
    10041005        for exp, got in zip(expected, fix_os_paths(f.choices)):
    class FieldsTests(SimpleTestCase):  
    10191020                ('/django/forms/formsets.py', 'formsets.py'),
    10201021                ('/django/forms/models.py', 'models.py'),
    10211022                ('/django/forms/util.py', 'util.py'),
     1023                ('/django/forms/utils.py', 'utils.py'),
    10221024                ('/django/forms/widgets.py', 'widgets.py')
    10231025            ]
    10241026        for exp, got in zip(expected, fix_os_paths(f.choices)):
    class FieldsTests(SimpleTestCase):  
    10391041                ('/django/forms/formsets.py', 'formsets.py'),
    10401042                ('/django/forms/models.py', 'models.py'),
    10411043                ('/django/forms/util.py', 'util.py'),
     1044                ('/django/forms/utils.py', 'utils.py'),
    10421045                ('/django/forms/widgets.py', 'widgets.py')
    10431046            ]
    10441047        for exp, got in zip(expected, fix_os_paths(f.choices)):
  • deleted file tests/regressiontests/forms/tests/util.py

    diff --git a/tests/regressiontests/forms/tests/util.py b/tests/regressiontests/forms/tests/util.py
    deleted file mode 100644
    index dddb8c7..0000000
    + -  
    1 # -*- coding: utf-8 -*-
    2 from django.core.exceptions import ValidationError
    3 from django.forms.util import flatatt, ErrorDict, ErrorList
    4 from django.test import TestCase
    5 from django.utils.safestring import mark_safe
    6 from django.utils.translation import ugettext_lazy
    7 
    8 
    9 class FormsUtilTestCase(TestCase):
    10         # Tests for forms/util.py module.
    11 
    12     def test_flatatt(self):
    13         ###########
    14         # flatatt #
    15         ###########
    16 
    17         self.assertEqual(flatatt({'id': "header"}), u' id="header"')
    18         self.assertEqual(flatatt({'class': "news", 'title': "Read this"}), u' class="news" title="Read this"')
    19         self.assertEqual(flatatt({}), u'')
    20 
    21     def test_validation_error(self):
    22         ###################
    23         # ValidationError #
    24         ###################
    25 
    26         # Can take a string.
    27         self.assertHTMLEqual(str(ErrorList(ValidationError("There was an error.").messages)),
    28                          '<ul class="errorlist"><li>There was an error.</li></ul>')
    29 
    30         # Can take a unicode string.
    31         self.assertHTMLEqual(unicode(ErrorList(ValidationError(u"Not \u03C0.").messages)),
    32                          u'<ul class="errorlist"><li>Not π.</li></ul>')
    33 
    34         # Can take a lazy string.
    35         self.assertHTMLEqual(str(ErrorList(ValidationError(ugettext_lazy("Error.")).messages)),
    36                          '<ul class="errorlist"><li>Error.</li></ul>')
    37 
    38         # Can take a list.
    39         self.assertHTMLEqual(str(ErrorList(ValidationError(["Error one.", "Error two."]).messages)),
    40                          '<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>')
    41 
    42         # Can take a mixture in a list.
    43         self.assertHTMLEqual(str(ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages)),
    44                          '<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>')
    45 
    46         class VeryBadError:
    47             def __unicode__(self): return u"A very bad error."
    48 
    49         # Can take a non-string.
    50         self.assertHTMLEqual(str(ErrorList(ValidationError(VeryBadError()).messages)),
    51                          '<ul class="errorlist"><li>A very bad error.</li></ul>')
    52 
    53         # Escapes non-safe input but not input marked safe.
    54         example = 'Example of link: <a href="http://www.example.com/">example</a>'
    55         self.assertHTMLEqual(str(ErrorList([example])),
    56                          '<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>')
    57         self.assertHTMLEqual(str(ErrorList([mark_safe(example)])),
    58                          '<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>')
    59         self.assertHTMLEqual(str(ErrorDict({'name': example})),
    60                          '<ul class="errorlist"><li>nameExample of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>')
    61         self.assertHTMLEqual(str(ErrorDict({'name': mark_safe(example)})),
    62                          '<ul class="errorlist"><li>nameExample of link: <a href="http://www.example.com/">example</a></li></ul>')
  • new file tests/regressiontests/forms/tests/utils.py

    diff --git a/tests/regressiontests/forms/tests/utils.py b/tests/regressiontests/forms/tests/utils.py
    new file mode 100644
    index 0000000..ab827e6
    - +  
     1# -*- coding: utf-8 -*-
     2from django.core.exceptions import ValidationError
     3from django.forms.utils import flatatt, ErrorDict, ErrorList
     4from django.test import TestCase
     5from django.utils.safestring import mark_safe
     6from django.utils.translation import ugettext_lazy
     7
     8
     9class FormsUtilTestCase(TestCase):
     10        # Tests for forms/util.py module.
     11
     12    def test_flatatt(self):
     13        ###########
     14        # flatatt #
     15        ###########
     16
     17        self.assertEqual(flatatt({'id': "header"}), u' id="header"')
     18        self.assertEqual(flatatt({'class': "news", 'title': "Read this"}), u' class="news" title="Read this"')
     19        self.assertEqual(flatatt({}), u'')
     20
     21    def test_validation_error(self):
     22        ###################
     23        # ValidationError #
     24        ###################
     25
     26        # Can take a string.
     27        self.assertHTMLEqual(str(ErrorList(ValidationError("There was an error.").messages)),
     28                         '<ul class="errorlist"><li>There was an error.</li></ul>')
     29
     30        # Can take a unicode string.
     31        self.assertHTMLEqual(unicode(ErrorList(ValidationError(u"Not \u03C0.").messages)),
     32                         u'<ul class="errorlist"><li>Not π.</li></ul>')
     33
     34        # Can take a lazy string.
     35        self.assertHTMLEqual(str(ErrorList(ValidationError(ugettext_lazy("Error.")).messages)),
     36                         '<ul class="errorlist"><li>Error.</li></ul>')
     37
     38        # Can take a list.
     39        self.assertHTMLEqual(str(ErrorList(ValidationError(["Error one.", "Error two."]).messages)),
     40                         '<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>')
     41
     42        # Can take a mixture in a list.
     43        self.assertHTMLEqual(str(ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages)),
     44                         '<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>')
     45
     46        class VeryBadError:
     47            def __unicode__(self): return u"A very bad error."
     48
     49        # Can take a non-string.
     50        self.assertHTMLEqual(str(ErrorList(ValidationError(VeryBadError()).messages)),
     51                         '<ul class="errorlist"><li>A very bad error.</li></ul>')
     52
     53        # Escapes non-safe input but not input marked safe.
     54        example = 'Example of link: <a href="http://www.example.com/">example</a>'
     55        self.assertHTMLEqual(str(ErrorList([example])),
     56                         '<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>')
     57        self.assertHTMLEqual(str(ErrorList([mark_safe(example)])),
     58                         '<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>')
     59        self.assertHTMLEqual(str(ErrorDict({'name': example})),
     60                         '<ul class="errorlist"><li>nameExample of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>')
     61        self.assertHTMLEqual(str(ErrorDict({'name': mark_safe(example)})),
     62                         '<ul class="errorlist"><li>nameExample of link: <a href="http://www.example.com/">example</a></li></ul>')
  • tests/regressiontests/localflavor/uy/tests.py

    diff --git a/tests/regressiontests/localflavor/uy/tests.py b/tests/regressiontests/localflavor/uy/tests.py
    index eda0068..b1fdc19 100644
    a b  
    11from django.contrib.localflavor.uy.forms import UYDepartamentSelect, UYCIField
    2 from django.contrib.localflavor.uy.util import get_validation_digit
     2from django.contrib.localflavor.uy.utils import get_validation_digit
    33
    44from django.test import SimpleTestCase
    55
  • tests/regressiontests/model_formsets_regress/tests.py

    diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py
    index d058f1d..ecdf72e 100644
    a b from __future__ import absolute_import, with_statement  
    22
    33from django import forms
    44from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME
    5 from django.forms.util import ErrorDict, ErrorList
     5from django.forms.utils import ErrorDict, ErrorList
    66from django.forms.models import modelform_factory, inlineformset_factory, modelformset_factory, BaseModelFormSet
    77from django.test import TestCase
    88
Back to Top