Django

Code

Changeset 5788

Show
Ignore:
Timestamp:
08/02/07 16:51:32 (1 year ago)
Author:
danderson
Message:

schema-evolution: updated from trunk/HEAD (r5787)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/schema-evolution/AUTHORS

    r5734 r5788  
    118118    martin.glueck@gmail.com 
    119119    GomoX <gomo@datafull.com> 
    120     Mario Gonzalez <gonzalemario @t gmail.com> 
     120    Mario Gonzalez <gonzalemario@gmail.com> 
    121121    Simon Greenhill <dev@simon.net.nz> 
    122122    Owen Griffiths 
     
    215215    Daniel Poelzleithner <http://poelzi.org/> 
    216216    polpak@yahoo.com 
     217    Johann Queuniet <johann.queuniet@adh.naellia.eu> 
    217218    J. Rademaker 
    218219    Michael Radziej <mir@noris.de> 
     
    243244    nowell strite 
    244245    Sundance 
     246    SuperJared 
    245247    Radek Švarz <http://www.svarz.cz/translate/> 
    246248    Swaroop C H <http://www.swaroopch.info> 
  • django/branches/schema-evolution/django/contrib/auth/models.py

    r5734 r5788  
    77import datetime 
    88import urllib 
     9 
     10UNUSABLE_PASSWORD = '!' # This will never be a valid hash 
    911 
    1012try: 
     
    8486 
    8587class UserManager(models.Manager): 
    86     def create_user(self, username, email, password): 
     88    def create_user(self, username, email, password=None): 
    8789        "Creates and saves a User with the given username, e-mail and password." 
    8890        now = datetime.datetime.now() 
    8991        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now) 
    90         user.set_password(password) 
     92        if password: 
     93            user.set_password(password) 
     94        else: 
     95            user.set_unusable_password() 
    9196        user.save() 
    9297        return user 
     
    179184            return is_correct 
    180185        return check_password(raw_password, self.password) 
     186 
     187    def set_unusable_password(self): 
     188        # Sets a value that will never be a valid hash 
     189        self.password = UNUSABLE_PASSWORD 
     190 
     191    def has_usable_password(self): 
     192        return self.password != UNUSABLE_PASSWORD 
    181193 
    182194    def get_group_permissions(self): 
     
    269281 
    270282class Message(models.Model): 
    271     """The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message. 
     283    """ 
     284    The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message. 
    272285    """ 
    273286    user = models.ForeignKey(User) 
  • django/branches/schema-evolution/django/contrib/databrowse/plugins/fieldchoices.py

    r5734 r5788  
    3838    def urls(self, plugin_name, easy_instance_field): 
    3939        if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values(): 
     40            field_value = smart_str(easy_instance_field.raw_value) 
    4041            return [u'%s%s/%s/%s/' % (easy_instance_field.model.url(), 
    4142                plugin_name, easy_instance_field.field.name, 
    42                 urllib.quote(smart_str(easy_instance_field.raw_value)))] 
     43                urllib.quote(field_value, safe=''))] 
    4344 
    4445    def model_view(self, request, model_databrowse, url): 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/calendar_day.html

    r5734 r5788  
    1111<ul class="objectlist"> 
    1212{% for object in object_list %} 
    13 <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li> 
     13<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li> 
    1414{% endfor %} 
    1515</ul> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/calendar_month.html

    r5734 r5788  
    1111<ul class="objectlist"> 
    1212{% for object in object_list %} 
    13 <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li> 
     13<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li> 
    1414{% endfor %} 
    1515</ul> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/choice_detail.html

    r5734 r5788  
    1111<ul class="objectlist"> 
    1212{% for object in object_list %} 
    13 <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li> 
     13<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li> 
    1414{% endfor %} 
    1515</ul> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/choice_list.html

    r5734 r5788  
    1111<ul class="objectlist"> 
    1212{% for choice in field.choices %} 
    13 <li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label }}</a></li> 
     13<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label|escape }}</a></li> 
    1414{% endfor %} 
    1515</ul> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/homepage.html

    r5734 r5788  
    1212                <p> 
    1313                {% for object in model.sample_objects %} 
    14                         <a href="{{ object.url }}">{{ object }}</a>,  
     14                        <a href="{{ object.url }}">{{ object|escape }}</a>,  
    1515                {% endfor %} 
    1616                        <a class="more" href="{{ model.url }}">More &rarr;</a> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/model_detail.html

    r5734 r5788  
    1313<ul class="objectlist"> 
    1414{% for object in model.objects %} 
    15     <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li> 
     15    <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li> 
    1616{% endfor %} 
    1717</ul> 
  • django/branches/schema-evolution/django/contrib/databrowse/templates/databrowse/object_detail.html

    r5734 r5788  
    55{% block content %} 
    66 
    7 <div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object }}</div> 
     7<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object|escape }}</div> 
    88 
    9 <h1>{{ object.model.verbose_name|capfirst }}: {{ object }}</h1> 
     9<h1>{{ object.model.verbose_name|capfirst }}: {{ object|escape }}</h1> 
    1010 
    1111<table class="objectinfo"> 
     
    1515<td> 
    1616{% if field.urls %} 
    17 {% for urlvalue in field.urls %} 
    18 {% if urlvalue.1 %}<a href="{{ urlvalue.1 }}">{% endif %}{{ urlvalue.0 }}{% if urlvalue.1 %}</a>{% endif %}{% if not forloop.last %}, {% endif %} 
     17{% for value, url in field.urls %} 
     18{% if url %}<a href="{{ url }}">{% endif %}{{ value|escape }}{% if url %}</a>{% endif %}{% if not forloop.last %}, {% endif %} 
    1919{% endfor %} 
    2020{% else %}None{% endif %} 
     
    3030  <ul class="objectlist"> 
    3131    {% for object in related_object.object_list %} 
    32     <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li> 
     32    <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li> 
    3333    {% endfor %} 
    3434  </ul> 
  • django/branches/schema-evolution/django/core/management.py

    r5787 r5788  
    14551455                error_text = str(e) 
    14561456            sys.stderr.write(style.ERROR("Error: %s" % error_text) + '\n') 
    1457             sys.exit(1) 
     1457            # Need to use an OS exit because sys.exit doesn't work in a thread 
     1458            os._exit(1) 
    14581459        except KeyboardInterrupt: 
    14591460            sys.exit(0) 
     
    15491550runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]' 
    15501551 
    1551 def test(app_labels, verbosity=1): 
     1552def test(test_labels, verbosity=1, interactive=True): 
    15521553    "Runs the test suite for the specified applications" 
    15531554    from django.conf import settings 
    15541555    from django.db.models import get_app, get_apps 
    1555  
    1556     if len(app_labels) == 0: 
    1557         app_list = get_apps() 
    1558     else: 
    1559         app_list = [get_app(app_label) for app_label in app_labels] 
    1560  
     1556     
    15611557    test_path = settings.TEST_RUNNER.split('.') 
    15621558    # Allow for Python 2.5 relative paths 
     
    15681564    test_runner = getattr(test_module, test_path[-1]) 
    15691565 
    1570     failures = test_runner(app_list, verbosity
     1566    failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive
    15711567    if failures: 
    15721568        sys.exit(failures) 
    15731569 
    15741570test.help_doc = 'Runs the test suite for the specified applications, or the entire site if no apps are specified' 
    1575 test.args = '[--verbosity] ' + APP_ARGS 
     1571test.args = '[--verbosity] [--noinput]' + APP_ARGS 
    15761572 
    15771573def load_data(fixture_labels, verbosity=1): 
     
    18501846        except IndexError: 
    18511847            parser.print_usage_and_exit() 
    1852     elif action in ('test', 'loaddata'): 
     1848    elif action == 'test': 
     1849        try: 
     1850            action_mapping[action](args[1:], int(options.verbosity), options.interactive) 
     1851        except IndexError: 
     1852            parser.print_usage_and_exit() 
     1853    elif action == 'loaddata': 
    18531854        try: 
    18541855            action_mapping[action](args[1:], int(options.verbosity)) 
  • django/branches/schema-evolution/django/db/models/fields/__init__.py

    r5735 r5788  
    622622            return decimal.Decimal(value) 
    623623        except decimal.InvalidOperation: 
    624             raise validators.ValidationError, ugettext("This value must be a decimal number.") 
     624            raise validators.ValidationError( 
     625                _("This value must be a decimal number.")) 
    625626 
    626627    def _format(self, value): 
  • django/branches/schema-evolution/django/db/models/manager.py

    r5734 r5788  
    33from django.db.models import signals 
    44from django.db.models.fields import FieldDoesNotExist 
    5  
    6 # Size of each "chunk" for get_iterator calls. 
    7 # Larger values are slightly faster at the expense of more storage space. 
    8 GET_ITERATOR_CHUNK_SIZE = 100 
    95 
    106def ensure_default_manager(sender): 
  • django/branches/schema-evolution/django/db/models/query.py

    r5734 r5788  
    580580            raise StopIteration 
    581581 
    582         # self._fields is a list of field names to fetch. 
     582        # self._select is a dictionary, and dictionaries' key order is 
     583        # undefined, so we convert it to a list of tuples. 
     584        extra_select = self._select.items() 
     585 
     586        # Construct two objects -- fields and field_names. 
     587        # fields is a list of Field objects to fetch. 
     588        # field_names is a list of field names, which will be the keys in the 
     589        # resulting dictionaries. 
    583590        if self._fields: 
    584             if not self._select: 
     591            if not extra_select: 
    585592                fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
     593                field_names = self._fields 
    586594            else: 
    587595                fields = [] 
     596                field_names = [] 
    588597                for f in self._fields: 
    589598                    if f in [field.name for field in self.model._meta.fields]: 
    590599                        fields.append(self.model._meta.get_field(f, many_to_many=False)) 
    591                     elif not self._select.has_key( f ): 
    592                         raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) 
    593  
    594             field_names = self._fields 
     600                        field_names.append(f) 
     601                    elif not self._select.has_key(f): 
     602                        raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f)) 
    595603        else: # Default to all fields. 
    596604            fields = self.model._meta.fields 
     
    599607        columns = [f.column for f in fields] 
    600608        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    601         # Add any additional SELECTs. 
    602         if self._select: 
    603             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 
     609        if extra_select: 
     610            select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in extra_select]) 
     611            field_names.extend([f[0] for f in extra_select]) 
    604612 
    605613        cursor = connection.cursor() 
  • django/branches/schema-evolution/django/newforms/forms.py

    r5734 r5788  
    233233 
    234234    def __unicode__(self): 
    235         "Renders this field as an HTML widget." 
    236         # Use the 'widget' attribute on the field to determine which type 
    237         # of HTML widget to use. 
    238         value = self.as_widget(self.field.widget) 
    239         if not isinstance(value, basestring): 
    240             # Some Widget render() methods -- notably RadioSelect -- return a 
    241             # "special" object rather than a string. Call __unicode__() on that 
    242             # object to get its rendered value. 
    243             value = unicode(value) 
    244         return value 
     235        """Renders this field as an HTML widget.""" 
     236        return self.as_widget() 
    245237 
    246238    def _errors(self): 
     
    252244    errors = property(_errors) 
    253245 
    254     def as_widget(self, widget, attrs=None): 
     246    def as_widget(self, widget=None, attrs=None): 
     247        """ 
     248        Renders the field by rendering the passed widget, adding any HTML 
     249        attributes passed as attrs.  If no widget is specified, then the 
     250        field's default widget will be used. 
     251        """ 
     252        if not widget: 
     253            widget = self.field.widget 
    255254        attrs = attrs or {} 
    256255        auto_id = self.auto_id 
  • django/branches/schema-evolution/django/newforms/widgets.py

    r5734 r5788  
    217217 
    218218class RadioInput(StrAndUnicode): 
    219     "An object used by RadioFieldRenderer that represents a single <input type='radio'>." 
     219    """ 
     220    An object used by RadioFieldRenderer that represents a single 
     221    <input type='radio'>. 
     222    """ 
     223 
    220224    def __init__(self, name, value, attrs, choice, index): 
    221225        self.name, self.value = name, value 
     
    240244 
    241245class RadioFieldRenderer(StrAndUnicode): 
    242     "An object used by RadioSelect to enable customization of radio widgets." 
     246    """ 
     247    An object used by RadioSelect to enable customization of radio widgets. 
     248    """ 
     249 
    243250    def __init__(self, name, value, attrs, choices): 
    244251        self.name, self.value, self.attrs = name, value, attrs 
     
    254261 
    255262    def __unicode__(self): 
    256         "Outputs a <ul> for this set of radio fields." 
     263        return self.render() 
     264 
     265    def render(self): 
     266        """Outputs a <ul> for this set of radio fields.""" 
    257267        return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]) 
    258268 
    259269class RadioSelect(Select): 
    260     def render(self, name, value, attrs=None, choices=()): 
    261         "Returns a RadioFieldRenderer instance rather than a Unicode string." 
     270 
     271    def __init__(self, *args, **kwargs): 
     272        self.renderer = kwargs.pop('renderer', None) 
     273        if not self.renderer: 
     274            self.renderer = RadioFieldRenderer 
     275        super(RadioSelect, self).__init__(*args, **kwargs) 
     276 
     277    def get_renderer(self, name, value, attrs=None, choices=()): 
     278        """Returns an instance of the renderer.""" 
    262279        if value is None: value = '' 
    263280        str_value = force_unicode(value) # Normalize to string. 
    264281        final_attrs = self.build_attrs(attrs) 
    265         return RadioFieldRenderer(name, str_value, final_attrs, list(chain(self.choices, choices))) 
     282        choices = list(chain(self.choices, choices)) 
     283        return self.renderer(name, str_value, final_attrs, choices) 
     284 
     285    def render(self, name, value, attrs=None, choices=()): 
     286        return self.get_renderer(name, value, attrs, choices).render() 
    266287 
    267288    def id_for_label(self, id_): 
  • django/branches/schema-evolution/django/shortcuts/__init__.py

    r5734 r5788  
    1 # This module collects helper functions and classes that "span" multiple levels 
    2 # of MVC. In other words, these functions/classes introduce controlled coupling 
    3 # for convenience's sake. 
     1""" 
     2This module collects helper functions and classes that "span" multiple levels 
     3of MVC. In other words, these functions/classes introduce controlled coupling 
     4for convenience's sake. 
     5""" 
    46 
    57from django.template import loader 
    68from django.http import HttpResponse, Http404 
    79from django.db.models.manager import Manager 
     10from django.db.models.query import QuerySet 
    811 
    912def render_to_response(*args, **kwargs): 
     13    """ 
     14    Returns a HttpResponse whose content is filled with the result of calling 
     15    django.template.loader.render_to_string() with the passed arguments. 
     16    """ 
    1017    return HttpResponse(loader.render_to_string(*args, **kwargs)) 
    1118load_and_render = render_to_response # For backwards compatibility. 
    1219 
    13 def get_object_or_404(klass, *args, **kwargs): 
    14     if isinstance(klass, Manager): 
    15         manager = klass 
    16         klass = manager.model 
    17     else: 
    18         manager = klass._default_manager 
    19     try: 
    20         return manager.get(*args, **kwargs) 
    21     except klass.DoesNotExist: 
    22         raise Http404('No %s matches the given query.' % klass._meta.object_name) 
    23  
    24 def get_list_or_404(klass, *args, **kwargs): 
    25     if isinstance(klass, Manager): 
     20def _get_queryset(klass): 
     21    """ 
     22    Returns a QuerySet from a Model, Manager, or QuerySet. Created to make 
     23    get_object_or_404 and get_list_or_404 more DRY. 
     24    """ 
     25    if isinstance(klass, QuerySet): 
     26        return klass 
     27    elif isinstance(klass, Manager): 
    2628        manager = klass 
    2729    else: 
    2830        manager = klass._default_manager 
    29     obj_list = list(manager.filter(*args, **kwargs)) 
     31    return manager.all() 
     32 
     33def get_object_or_404(klass, *args, **kwargs): 
     34    """ 
     35    Uses get() to return an object, or raises a Http404 exception if the object 
     36    does not exist. 
     37 
     38    klass may be a Model, Manager, or QuerySet object. All other passed 
     39    arguments and keyword arguments are used in the get() query. 
     40 
     41    Note: Like with get(), an AssertionError will be raised if more than one 
     42    object is found. 
     43    """ 
     44    queryset = _get_queryset(klass) 
     45    try: 
     46        return queryset.get(*args, **kwargs) 
     47    except queryset.model.DoesNotExist: 
     48        raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) 
     49 
     50def get_list_or_404(klass, *args, **kwargs): 
     51    """ 
     52    Uses filter() to return a list of objects, or raise a Http404 exception if 
     53    the list is empty. 
     54 
     55    klass may be a Model, Manager, or QuerySet object. All other passed 
     56    arguments and keyword arguments are used in the filter() query. 
     57    """ 
     58    queryset = _get_queryset(klass) 
     59    obj_list = list(queryset.filter(*args, **kwargs)) 
    3060    if not obj_list: 
    31         raise Http404('No %s matches the given query.' % manager.model._meta.object_name) 
     61        raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) 
    3262    return obj_list 
  • django/branches/schema-evolution/django/template/loaders/app_directories.py

    r5734 r5788  
    1 # Wrapper for loading templates from "template" directories in installed app packages. 
     1""" 
     2Wrapper for loading templates from "template" directories in INSTALLED_APPS 
     3packages. 
     4""" 
     5 
     6import os 
    27 
    38from django.conf import settings 
    49from django.core.exceptions import ImproperlyConfigured 
    510from django.template import TemplateDoesNotExist 
    6 import os 
     11from django.utils._os import safe_join 
    712 
    813# At compile time, cache the directories to search. 
     
    2934 
    3035def get_template_sources(template_name, template_dirs=None): 
    31     for template_dir in app_template_dirs: 
    32         yield os.path.join(template_dir, template_name) 
     36    if not template_dirs: 
     37        template_dirs = app_template_dirs 
     38    for template_dir in template_dirs: 
     39        try: 
     40            yield safe_join(template_dir, template_name) 
     41        except ValueError: 
     42            # The joined path was located outside of template_dir. 
     43            pass 
    3344 
    3445def load_template_source(template_name, template_dirs=None): 
  • django/branches/schema-evolution/django/template/loaders/filesystem.py

    r5734 r5788  
    1 # Wrapper for loading templates from the filesystem. 
     1""" 
     2Wrapper for loading templates from the filesystem. 
     3""" 
    24 
    35from django.conf import settings 
    46from django.template import TemplateDoesNotExist 
    5 import os 
     7from django.utils._os import safe_join 
    68 
    79def get_template_sources(template_name, template_dirs=None): 
     
    911        template_dirs = settings.TEMPLATE_DIRS 
    1012    for template_dir in template_dirs: 
    11         yield os.path.join(template_dir, template_name) 
     13        try: 
     14            yield safe_join(template_dir, template_name) 
     15        except ValueError: 
     16            # The joined path was located outside of template_dir. 
     17            pass 
    1218 
    1319def load_template_source(template_name, template_dirs=None): 
  • django/branches/schema-evolution/django/test/client.py

    r5734 r5788  
    196196            'CONTENT_TYPE':    'text/html; charset=utf-8', 
    197197            'PATH_INFO':       path, 
    198             'QUERY_STRING':    urlencode(data), 
     198            'QUERY_STRING':    urlencode(data, doseq=True), 
    199199            'REQUEST_METHOD': 'GET', 
    200200        } 
  • django/branches/schema-evolution/django/test/simple.py

    r5785 r5788  
    11import unittest 
    22from django.conf import settings 
     3from django.db.models import get_app, get_apps 
    34from django.test import _doctest as doctest 
    45from django.test.utils import setup_test_environment, teardown_test_environment 
     
    1112doctestOutputChecker = OutputChecker() 
    1213 
     14def get_tests(app_module): 
     15    try: 
     16        app_path = app_module.__name__.split('.')[:-1] 
     17        test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE) 
     18    except ImportError, e: 
     19        # Couldn't import tests.py. Was it due to a missing file, or 
     20        # due to an import error in a tests.py that actually exists? 
     21        import os.path 
     22        from imp import find_module 
     23        try: 
     24            mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)]) 
     25        except ImportError: 
     26            # 'tests' module doesn't exist. Move on. 
     27            test_module = None 
     28        else: 
     29            # The module exists, so there must be an import error in the  
     30            # test module itself. We don't need the module; so if the 
     31            # module was a single file module (i.e., tests.py), close the file 
     32            # handle returned by find_module. Otherwise, the test module 
     33            # is a directory, and there is nothing to close. 
     34            if mod[0]: 
     35                mod[0].close() 
     36            raise 
     37    return test_module 
     38     
    1339def build_suite(app_module): 
    1440    "Create a complete Django test suite for the provided application module" 
     
    3157    # Check to see if a separate 'tests' module exists parallel to the  
    3258    # models module 
    33     try: 
    34         app_path = app_module.__name__.split('.')[:-1] 
    35         test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE) 
    36          
     59    test_module = get_tests(app_module) 
     60    if test_module: 
    3761        # Load unit and doctests in the tests.py module. If module has 
    3862        # a suite() method, use it. Otherwise build the test suite ourselves. 
     
    4872                # No doc tests in tests.py 
    4973                pass 
    50     except ImportError, e: 
    51         # Couldn't import tests.py. Was it due to a missing file, or 
    52         # due to an import error in a tests.py that actually exists? 
    53         import os.path 
    54         from imp import find_module 
    55         try: 
    56             mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)]) 
    57         except ImportError: 
    58             # 'tests' module doesn't exist. Move on. 
    59             pass 
    60         else: 
    61             # The module exists, so there must be an import error in the  
    62             # test module itself. We don't need the module; so if the 
    63             # module was a single file module (i.e., tests.py), close the file 
    64             # handle returned by find_module. Otherwise, the test module 
    65             # is a directory, and there is nothing to close. 
    66             if mod[0]: 
    67                 mod[0].close() 
    68             raise 
    69              
    7074    return suite 
    7175 
    72 def run_tests(module_list, verbosity=1, extra_tests=[]): 
     76def build_test(label): 
     77    """Construct a test case a test with the specified label. Label should  
     78    be of the form model.TestClass or model.TestClass.test_method. Returns 
     79    an instantiated test or test suite corresponding to the label provided. 
     80         
    7381    """ 
    74     Run the unit tests for all the modules in the provided list. 
    75     This testrunner will search each of the modules in the provided list, 
    76     looking for doctests and unittests in models.py or tests.py within 
    77     the module. A list of 'extra' tests may also be provided; these tests 
     82    parts = label.split('.') 
     83    if len(parts) < 2 or len(parts) > 3: 
     84        raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label) 
     85     
     86    app_module = get_app(parts[0]) 
     87    TestClass = getattr(app_module, parts[1], None) 
     88 
     89    # Couldn't find the test class in models.py; look in tests.py 
     90    if TestClass is None: 
     91        test_module = get_tests(app_module) 
     92        if test_module: 
     93            TestClass = getattr(test_module, parts[1], None) 
     94 
     95    if len(parts) == 2: # label is app.TestClass 
     96        try: 
     97            return unittest.TestLoader().loadTestsFromTestCase(TestClass) 
     98        except TypeError: 
     99            raise ValueError("Test label '%s' does not refer to a test class" % label)             
     100    else: # label is app.TestClass.test_method 
     101        return TestClass(parts[2]) 
     102 
     103def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]): 
     104    """ 
     105    Run the unit tests for all the test labels in the provided list. 
     106    Labels must be of the form: 
     107     - app.TestClass.test_method 
     108        Run a single specific test method 
     109     - app.TestClass 
     110        Run all the test methods in a given class 
     111     - app 
     112        Search for doctests and unittests in the named application. 
     113 
     114    When looking for tests, the test runner will look in the models and 
     115    tests modules for the application. 
     116     
     117    A list of 'extra' tests may also be provided; these tests 
    78118    will be added to the test suite. 
    79119     
     
    84124    settings.DEBUG = False     
    85125    suite = unittest.TestSuite() 
    86       
    87     for module in module_list: 
    88         suite.addTest(build_suite(module)) 
     126     
     127    if test_labels: 
     128        for label in test_labels: 
     129            if '.' in label: 
     130                suite.addTest(build_test(label)) 
     131            else: 
     132                app = get_app(label) 
     133                suite.addTest(build_suite(app)) 
     134    else: 
     135        for app in get_apps(): 
     136            suite.addTest(build_suite(app)) 
    89137     
    90138    for test in extra_tests: 
     
    92140 
    93141    old_name = settings.DATABASE_NAME 
    94     create_test_db(verbosity
     142    create_test_db(verbosity, autoclobber=not interactive
    95143    result = unittest.TextTestRunner(verbosity=verbosity).run(suite) 
    96144    destroy_test_db(old_name, verbosity) 
  • django/branches/schema-evolution/django/test/testcases.py

    r5734 r5788  
    8585                (response.status_code, status_code)) 
    8686        real_count = response.content.count(text) 
    87         if count
     87        if count is not None
    8888            self.assertEqual(real_count, count, 
    8989                "Found %d instances of '%s' in response (expected %d)" % (real_count, text, count)) 
  • django/branches/schema-evolution/django/test/utils.py

    r5734 r5788  
    145145    management.syncdb(verbosity, interactive=False) 
    146146 
     147    if settings.CACHE_BACKEND.startswith('db://'): 
     148        cache_name = settings.CACHE_BACKEND[len('db://'):] 
     149        management.createcachetable(cache_name) 
     150 
    147151    # Get a cursor (even though we don't need one yet). This has 
    148152    # the side effect of initializing the test database. 
  • django/branches/schema-evolution/django/utils/http.py

    r5734 r5788  
    3131    if hasattr(query, 'items'): 
    3232        query = query.items() 
    33     return urllib.urlencode([(smart_str(k), smart_str(v)) for k, 
    34         v in query], doseq) 
     33    return urllib.urlencode( 
     34        [(smart_str(k), 
     35         isinstance(v, (list,tuple)) and [smart_str(i) for i in v] or smart_str(v)) 
     36            for k, v in query],