Django

Code

Changeset 3739

Show
Ignore:
Timestamp:
09/08/06 11:35:39 (2 years ago)
Author:
jpellerin
Message:

[multi-db] Merge trunk to [3737]. Some tests still failing.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/multiple-db-support/AUTHORS

    r3712 r3739  
    105105    Jason McBrayer <http://www.carcosa.net/jason/> 
    106106    michael.mcewan@gmail.com 
    107     mir@noris.de 
    108107    mmarshall 
    109108    Eric Moritz <http://eric.themoritzfamily.com/> 
     
    122121    Daniel Poelzleithner <http://poelzi.org/> 
    123122    J. Rademaker 
     123    Michael Radziej <mir@noris.de> 
    124124    Brian Ray <http://brianray.chipy.org/> 
    125125    rhettg@gmail.com 
     
    128128    David Schein 
    129129    Pete Shinners <pete@shinners.org> 
     130    SmileyChris <smileychris@gmail.com> 
    130131    sopel 
    131132    Thomas Steinacher <tom@eggdrop.ch> 
  • django/branches/multiple-db-support/django/contrib/admin/views/auth.py

    r3523 r3739  
     1from django.contrib.admin.views.decorators import staff_member_required 
    12from django.contrib.auth.forms import UserCreationForm 
    23from django.contrib.auth.models import User 
     
    67 
    78def user_add_stage(request): 
     9    if not request.user.has_perm('auth.change_user'): 
     10        raise PermissionDenied 
    811    manipulator = UserCreationForm() 
    912    if request.method == 'POST': 
     
    3841        'username_help_text': User._meta.get_field('username').help_text, 
    3942    }, context_instance=template.RequestContext(request)) 
     43user_add_stage = staff_member_required(user_add_stage) 
  • django/branches/multiple-db-support/django/contrib/sitemaps/__init__.py

    r3712 r3739  
    1717        try: 
    1818            # First, try to get the "index" sitemap URL. 
    19             sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.index') 
     19            sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.index') 
    2020        except urlresolvers.NoReverseMatch: 
    2121            try: 
    2222                # Next, try for the "global" sitemap URL. 
    23                 sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap') 
     23                sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap') 
    2424            except urlresolvers.NoReverseMatch: 
    2525                pass 
  • django/branches/multiple-db-support/django/contrib/sitemaps/views.py

    r3712 r3739  
    99    protocol = request.is_secure() and 'https' or 'http' 
    1010    for section in sitemaps.keys(): 
    11         sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap', kwargs={'section': section}) 
     11        sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': section}) 
    1212        sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url)) 
    1313    xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites}) 
  • django/branches/multiple-db-support/django/core/management.py

    r3712 r3739  
    756756            rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name() 
    757757            rel_query_name = f.related_query_name() 
    758             for r in rel_opts.fields: 
    759                 if r.name == rel_name: 
    760                     e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
    761                 if r.name == rel_query_name: 
    762                     e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
    763             for r in rel_opts.many_to_many: 
    764                 if r.name == rel_name: 
    765                     e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
    766                 if r.name == rel_query_name: 
    767                     e.add(opts, "Reverse query name for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
    768             for r in rel_opts.get_all_related_many_to_many_objects(): 
    769                 if r.field is not f: 
     758            # If rel_name is none, there is no reverse accessor. 
     759            # (This only occurs for symmetrical m2m relations to self).  
     760            # If this is the case, there are no clashes to check for this field, as 
     761            # there are no reverse descriptors for this field. 
     762            if rel_name is not None: 
     763                for r in rel_opts.fields: 
     764                    if r.name == rel_name: 
     765                        e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
     766                    if r.name == rel_query_name: 
     767                        e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
     768                for r in rel_opts.many_to_many: 
     769                    if r.name == rel_name: 
     770                        e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
     771                    if r.name == rel_query_name: 
     772                        e.add(opts, "Reverse query name for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) 
     773                for r in rel_opts.get_all_related_many_to_many_objects(): 
     774                    if r.field is not f: 
     775                        if r.get_accessor_name() == rel_name: 
     776                            e.add(opts, "Accessor for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
     777                        if r.get_accessor_name() == rel_query_name: 
     778                            e.add(opts, "Reverse query name for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
     779                for r in rel_opts.get_all_related_objects(): 
    770780                    if r.get_accessor_name() == rel_name: 
    771                         e.add(opts, "Accessor for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
     781                        e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
    772782                    if r.get_accessor_name() == rel_query_name: 
    773                         e.add(opts, "Reverse query name for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
    774             for r in rel_opts.get_all_related_objects(): 
    775                 if r.get_accessor_name() == rel_name: 
    776                     e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
    777                 if r.get_accessor_name() == rel_query_name: 
    778                     e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
     783                        e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 
    779784 
    780785        # Check admin attribute. 
  • django/branches/multiple-db-support/django/db/backends/postgresql_psycopg2/base.py

    r3712 r3739  
    7070    return '"%s"' % name 
    7171 
    72 def dictfetchone(cursor): 
    73     "Returns a row from the cursor as a dict" 
    74     # TODO: cursor.dictfetchone() doesn't exist in psycopg2, 
    75     # but no Django code uses this. Safe to remove? 
    76     return cursor.dictfetchone() 
    77  
    78 def dictfetchmany(cursor, number): 
    79     "Returns a certain number of rows from a cursor as a dict" 
    80     # TODO: cursor.dictfetchmany() doesn't exist in psycopg2, 
    81     # but no Django code uses this. Safe to remove? 
    82     return cursor.dictfetchmany(number) 
    83  
    84 def dictfetchall(cursor): 
    85     "Returns all rows from a cursor as a dict" 
    86     # TODO: cursor.dictfetchall() doesn't exist in psycopg2, 
    87     # but no Django code uses this. Safe to remove? 
    88     return cursor.dictfetchall() 
     72dictfetchone = util.dictfetchone 
     73dictfetchmany = util.dictfetchmany 
     74dictfetchall = util.dictfetchall 
    8975 
    9076def get_last_insert_id(cursor, table_name, pk_name): 
  • django/branches/multiple-db-support/django/db/backends/sqlite3/base.py

    r3261 r3739  
    6464 
    6565    def close(self): 
    66         if self.connection is not None: 
     66        from django.conf import settings 
     67        # If database is in memory, closing the connection destroys the database. 
     68        # To prevent accidental data loss, ignore close requests on an in-memory db. 
     69        if self.connection is not None and settings.DATABASE_NAME != ":memory:": 
    6770            self.connection.close() 
    6871            self.connection = None 
  • django/branches/multiple-db-support/django/db/models/related.py

    r3258 r3739  
    132132        # but this can be overridden with the "related_name" option. 
    133133        if self.field.rel.multiple: 
     134            # If this is a symmetrical m2m relation on self, there is no reverse accessor. 
     135            if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model: 
     136                return None 
    134137            return self.field.rel.related_name or (self.opts.object_name.lower() + '_set') 
    135138        else: 
  • django/branches/multiple-db-support/django/template/defaulttags.py

    r3712 r3739  
    8787        context.push() 
    8888        try: 
    89             values = self.sequence.resolve(context
     89            values = self.sequence.resolve(context, True
    9090        except VariableDoesNotExist: 
    9191            values = [] 
     
    213213 
    214214    def render(self, context): 
    215         obj_list = self.target.resolve(context
    216         if obj_list == '': # target_var wasn't found in context; fail silently 
     215        obj_list = self.target.resolve(context, True
     216        if obj_list == None: # target_var wasn't found in context; fail silently 
    217217            context[self.var_name] = [] 
    218218            return '' 
    219219        output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 
    220220        for obj in obj_list: 
    221             grouper = self.expression.resolve(Context({'var': obj})
     221            grouper = self.expression.resolve(Context({'var': obj}), True
    222222            # TODO: Is this a sensible way to determine equality? 
    223223            if output and repr(output[-1]['grouper']) == repr(grouper): 
  • django/branches/multiple-db-support/django/template/__init__.py

    r3712 r3739  
    138138 
    139139class Template(object): 
    140     def __init__(self, template_string, origin=None): 
     140    def __init__(self, template_string, origin=None, name='<Unknown Template>'): 
    141141        "Compilation stage" 
    142142        if settings.TEMPLATE_DEBUG and origin == None: 
     
    145145            # came from... 
    146146        self.nodelist = compile_string(template_string, origin) 
     147        self.name = name 
    147148 
    148149    def __iter__(self): 
     
    435436                i += 1 
    436437            if i >= len(subject): 
    437                 raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % subject 
     438                raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject) 
    438439            i += 1 
    439440            res = subject[p:i] 
     
    549550        except VariableDoesNotExist: 
    550551            if ignore_failures: 
    551                 return None 
     552                obj = None 
    552553            else: 
    553                 return settings.TEMPLATE_STRING_IF_INVALID 
     554                if settings.TEMPLATE_STRING_IF_INVALID: 
     555                    return settings.TEMPLATE_STRING_IF_INVALID 
     556                else: 
     557                    obj = settings.TEMPLATE_STRING_IF_INVALID 
    554558        for func, args in self.filters: 
    555559            arg_vals = [] 
  • django/branches/multiple-db-support/django/template/loader_tags.py

    r3712 r3739  
    5252            raise TemplateSyntaxError, error_msg 
    5353        if hasattr(parent, 'render'): 
    54             return parent 
     54            return parent # parent is a Template object 
    5555        try: 
    5656            source, origin = find_template_source(parent, self.template_dirs) 
  • django/branches/multiple-db-support/django/test/client.py

    r3658 r3739  
    11from cStringIO import StringIO 
    2 from django.contrib.admin.views.decorators import LOGIN_FORM_KEY, _encode_post_data 
    32from django.core.handlers.base import BaseHandler 
    43from django.core.handlers.wsgi import WSGIRequest 
    54from django.dispatch import dispatcher 
    65from django.http import urlencode, SimpleCookie 
    7 from django.template import signals 
     6from django.test import signals 
    87from django.utils.functional import curry 
    98 
     
    9796    """ 
    9897    def __init__(self, **defaults): 
    99         self.handler = TestHandler() 
     98        self.handler = ClientHandler() 
    10099        self.defaults = defaults 
    101100        self.cookie = SimpleCookie() 
     
    127126        on_template_render = curry(store_rendered_templates, data) 
    128127        dispatcher.connect(on_template_render, signal=signals.template_rendered) 
    129  
     128         
    130129        response = self.handler(environ) 
    131130         
     
    181180        """ 
    182181        A specialized sequence of GET and POST to log into a view that 
    183         is protected by @login_required or a similar access decorator. 
    184          
    185         path should be the URL of the login page, or of any page that 
    186         is login protected. 
    187          
    188         Returns True if login was successful; False if otherwise.         
    189         """ 
    190         # First, GET the login page.  
    191         # This is required to establish the session
     182        is protected by a @login_required access decorator. 
     183         
     184        path should be the URL of the page that is login protected. 
     185         
     186        Returns the response from GETting the requested URL after  
     187        login is complete. Returns False if login process failed. 
     188        """ 
     189        # First, GET the page that is login protected.  
     190        # This page will redirect to the login page
    192191        response = self.get(path) 
     192        if response.status_code != 302: 
     193            return False 
     194             
     195        login_path, data = response['Location'].split('?') 
     196        next = data.split('=')[1] 
     197 
     198        # Second, GET the login page; required to set up cookies 
     199        response = self.get(login_path, **extra) 
    193200        if response.status_code != 200: 
    194201            return False 
    195  
    196         # Set up the block of form data required by the login page
     202             
     203        # Last, POST the login data
    197204        form_data = { 
    198205            'username': username, 
    199206            'password': password, 
    200             'this_is_the_login_form': 1, 
    201             'post_data': _encode_post_data({LOGIN_FORM_KEY: 1}) 
     207            'next' : next, 
    202208        } 
    203         response = self.post(path, data=form_data, **extra) 
    204          
    205         # login page should give response 200 (if you requested the login 
    206         # page specifically), or 302 (if you requested a login 
    207         # protected page, to which the login can redirect). 
    208         return response.status_code in (200,302) 
     209        response = self.post(login_path, data=form_data, **extra) 
     210 
     211        # Login page should 302 redirect to the originally requested page 
     212        if response.status_code != 302 or response['Location'] != path: 
     213            return False 
     214 
     215        # Since we are logged in, request the actual page again 
     216        return self.get(path) 
  • django/branches/multiple-db-support/django/test/simple.py

    r3712 r3739  
    22from django.conf import settings 
    33from django.core import management 
     4from django.test.utils import setup_test_environment, teardown_test_environment 
    45from django.test.utils import create_test_db, destroy_test_db 
    56from django.test.testcases import OutputChecker, DocTestRunner 
     
    5253    will be added to the test suite. 
    5354    """ 
     55    setup_test_environment() 
    5456     
    5557    settings.DEBUG = False     
     
    6769    unittest.TextTestRunner(verbosity=verbosity).run(suite) 
    6870    destroy_test_db(old_name, verbosity) 
     71     
     72    teardown_test_environment() 
  • django/branches/multiple-db-support/django/test/utils.py

    r3712 r3739  
    11import sys, time 
    22from django.conf import settings 
     3 
    34from django.db import backend, connect, connection, connection_info, connections 
     5from django.dispatch import dispatcher 
     6from django.test import signals 
     7from django.template import Template 
    48 
    59# The prefix to put on the default database name when creating 
     
    711TEST_DATABASE_PREFIX = 'test_' 
    812 
     13def instrumented_test_render(self, context): 
     14    """An instrumented Template render method, providing a signal  
     15    that can be intercepted by the test system Client 
     16     
     17    """ 
     18    dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context) 
     19    return self.nodelist.render(context) 
     20     
     21def setup_test_environment(): 
     22    """Perform any global pre-test setup. This involves: 
     23         
     24        - Installing the instrumented test renderer 
     25         
     26    """ 
     27    Template.original_render = Template.render 
     28    Template.render = instrumented_test_render 
     29     
     30def teardown_test_environment(): 
     31    """Perform any global post-test teardown. This involves: 
     32 
     33        - Restoring the original test renderer 
     34         
     35    """ 
     36    Template.render = Template.original_render 
     37    del Template.original_render 
     38     
    939def _set_autocommit(connection): 
    1040    "Make sure a connection is in autocommit mode." 
     
    5686                print "Tests cancelled." 
    5787                sys.exit(1) 
    58         # Close the old connection 
    59         connection.close() 
     88                
     89    connection.close() 
     90    settings.DATABASE_NAME = TEST_DATABASE_NAME 
    6091 
    61         # Get a cursor (even though we don't need one yet). This has 
    62         # the side effect of initializing the test database. 
    63         cursor = connection.cursor()         
     92    # Get a cursor (even though we don't need one yet). This has 
     93    # the side effect of initializing the test database. 
     94    cursor = connection.cursor() 
    6495 
    6596    # Fill OTHER_DATABASES with the TEST_DATABASES settings, 
     
    88119    if verbosity >= 1: 
    89120        print "Destroying test database..." 
    90     if settings.DATABASE_ENGINE != "sqlite3":  
    91         connection.close() 
    92         TEST_DATABASE_NAME = settings.DATABASE_NAME 
    93         settings.DATABASE_NAME = old_database_name 
     121    connection.close() 
     122    TEST_DATABASE_NAME = settings.DATABASE_NAME 
     123    settings.DATABASE_NAME = old_database_name 
     124 
     125    if settings.DATABASE_ENGINE != "sqlite3": 
    94126        settings.OTHER_DATABASES = old_databases 
    95127        cursor = connection.cursor() 
     
    98130        cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) 
    99131        connection.close() 
    100  
    101  
  • django/branches/multiple-db-support/django/utils/functional.py

    r2809 r3739  
    1 def curry(*args, **kwargs): 
     1def curry(_curried_func, *args, **kwargs): 
    22    def _curried(*moreargs, **morekwargs): 
    3         return args[0](*(args[1:]+moreargs), **dict(kwargs.items() + morekwargs.items())) 
     3        return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) 
    44    return _curried 
    55 
  • django/branches/multiple-db-support/django/views/debug.py

    r3712 r3739  
    116116            'lineno': '?', 
    117117        }] 
    118     t = Template(TECHNICAL_500_TEMPLATE
     118    t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template'
    119119    c = Context({ 
    120120        'exception_type': exc_type.__name__, 
     
    142142            return empty_urlconf(request) 
    143143 
    144     t = Template(TECHNICAL_404_TEMPLATE
     144    t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 template'
    145145    c = Context({ 
    146146        'root_urlconf': settings.ROOT_URLCONF, 
     
    155155def empty_urlconf(request): 
    156156    "Create an empty URLconf 404 error response." 
    157     t = Template(EMPTY_URLCONF_TEMPLATE
     157    t = Template(EMPTY_URLCONF_TEMPLATE, name='Empty URLConf template'
    158158    c = Context({ 
    159159        'project_name': settings.SETTINGS_MODULE.split('.')[0] 
     
    190190  <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 
    191191  <meta name="robots" content="NONE,NOARCHIVE" /> 
    192   <title>{{ exception_type }} at {{ request.path }}</title> 
     192  <title>{{ exception_type }} at {{ request.path|escape }}</title> 
    193193  <style type="text/css"> 
    194194    html * { padding:0; margin:0; } 
     
    293293 
    294294<div id="summary"> 
    295   <h1>{{ exception_type }} at {{ request.path }}</h1> 
     295  <h1>{{ exception_type }} at {{ request.path|escape }}</h1> 
    296296  <h2>{{ exception_value|escape }}</h2> 
    297297  <table class="meta"> 
     
    302302    <tr> 
    303303      <th>Request URL:</th> 
    304       <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path }}</td> 
     304      <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}</td> 
    305305    </tr> 
    306306    <tr> 
     
    310310    <tr> 
    311311      <th>Exception Value:</th> 
    312       <td>{{ exception_value }}</td> 
     312      <td>{{ exception_value|escape }}</td> 
    313313    </tr> 
    314314    <tr> 
     
    413413  {% endif %} 
    414414{% endfor %}<br/> 
    415 &nbsp;&nbsp;{{ exception_type }} at {{ request.path }}<br/> 
     415&nbsp;&nbsp;{{ exception_type }} at {{ request.path|escape }}<br/> 
    416416&nbsp;&nbsp;{{ exception_value|escape }}</code> 
    417417          </td> 
     
    547547<head> 
    548548  <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 
    549   <title>Page not found at {{ request.path }}</title> 
     549  <title>Page not found at {{ request.path|escape }}</title> 
    550550  <meta name="robots" content="NONE,NOARCHIVE" /> 
    551551  <style type="text/css"> 
     
    577577      <tr> 
    578578        <th>Request URL:</th> 
    579       <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path }}</td> 
     579      <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}</td> 
    580580      </tr> 
    581581    </table> 
     
    592592        {% endfor %} 
    593593      </ol> 
    594       <p>The current URL, <code>{{ request.path }}</code>, didn't match any of these.</p> 
     594      <p>The current URL, <code>{{ request.path|escape }}</code>, didn't match any of these.</p> 
    595595    {% else %} 
    596596      <p>{{ reason|escape }}</p> 
  • django/branches/multiple-db-support/django/views/static.py

    r3712 r3739  
    8282        t = loader.get_template('static/directory_index') 
    8383    except TemplateDoesNotExist: 
    84         t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE
     84        t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default directory index template'
    8585    files = [] 
    8686    for f in os.listdir(fullpath): 
  • django/branches/multiple-db-support/docs/syndication_feeds.txt

    r3502 r3739  
    708708    ...     link=u"http://www.example.com/", 
    709709    ...     description=u"In which I write about what I ate today.", 
    710     ...     language=u"en"), 
     710    ...     language=u"en") 
    711711    >>> f.add_item(title=u"Hot dog today", 
    712712    ...     link=u"http://www.example.com/entries/1/", 
  • django/branches/multiple-db-support/docs/templates_python.txt

    r3712 r3739  
    199199~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    200200 
    201 If a variable doesn't exist, the template system inserts the value of the 
    202 ``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` (the empty 
    203 string) by default. 
     201Generally, if a variable doesn't exist, the template system inserts the 
     202value of the ``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` 
     203(the empty string) by default. 
     204 
     205Filters that are applied to an invalid variable will only be applied if 
     206``TEMPLATE_STRING_IF_INVALID`` is set to ``''`` (the empty string). If 
     207``TEMPLATE_STRING_IF_INVALID`` is set to any other value, variable 
     208filters will be ignored. 
     209 
     210This behavior is slightly different for the ``if``, ``for`` and ``regroup`` 
     211template tags. If an invalid variable is provided to one of these template 
     212tags, the variable will be interpreted as ``None``. Filters are always 
     213applied to invalid variables within these template tags. 
    204214 
    205215Playing with Context objects 
  • django/branches/multiple-db-support/docs/testing.txt

    r3712 r3739  
    9393Like doctests, Django's unit tests use a standard library module: unittest_. 
    9494As with doctests, Django's test runner looks for any unit test cases defined 
    95 in ``models.py``, or in a ``tests.py`` file in your application directory. 
     95in ``models.py``, or in a ``tests.py`` file stored in the application  
     96directory. 
    9697 
    9798An equivalent unittest test case for the above example would look like:: 
     
    111112 
    112113When you `run your tests`_, the test utility will find all the test cases 
    113 (that is, subclasses of ``unittest.TestCase``) in ``tests.py``, automatically 
    114 build a test suite out of those test cases, and run that suite. 
     114(that is, subclasses of ``unittest.TestCase``) in ``models.py`` and  
     115``tests.py``, automatically build a test suite out of those test cases,  
     116and run that suite. 
    115117 
    116118For more details about ``unittest``, see the `standard library unittest 
     
    160162in different circumstances. 
    161163 
    162 Testing utilities 
    163 ================= 
    164  
     164Testing Tools 
     165============= 
     166 
     167To assist in testing various features of your application, Django provides  
     168tools that can be used to establish tests and test conditions. 
     169 
     170* `Test Client`_ 
     171* Fixtures_ 
     172   
    165173Test Client 
    166174----------- 
    167175 
    168 A dummy browser; instruments the template generation process... 
     176The Test Client is a simple dummy browser. It allows you to simulate 
     177GET and POST requests on a URL, and observe the response that is received. 
     178This allows you to test that the correct view is executed for a given URL, 
     179and that the view constructs the correct response. 
     180 
     181As the response is generated, the Test Client gathers details on the  
     182Template and Context objects that were used to generate the response. These 
     183Templates and Contexts are then provided as part of the response, and can be 
     184used as test conditions. 
     185 
     186.. admonition:: Test Client vs Browser Automation? 
     187 
     188    The Test Client is not intended as a replacement for Twill_, Selenium_,  
     189    or other browser automation frameworks - it is intended to allow  
     190    testing of the contexts and templates produced by a view,  
     191    rather than the HTML rendered to the end-user. 
     192     
     193    A comprehensive test suite should use a combination of both: Test Client 
     194    tests to establish that the correct view is being called and that  
     195    the view is collecting the correct context data, and Browser Automation 
     196    tests to check that user interface behaves as expected. 
     197     
     198.. _Twill: http://twill.idyll.org/ 
     199.. _Selenium: http://www.openqa.org/selenium/ 
     200 
     201The Test Client is stateful; if a cookie is returned as part of a response, 
     202that cookie is provided as part of the next request issued to that Client 
     203instance. Expiry policies for these cookies are not followed; if you want  
     204a cookie to expire, either delete it manually from ``client.cookies``, or  
     205create a new Client instance (which will effectively delete all cookies). 
     206 
     207Making requests 
     208~~~~~~~~~~~~~~~ 
     209 
     210Creating an instance of ``Client`` (``django.test.client.Client``) requires  
     211no arguments at time of construction. Once constructed, the following methods 
     212can be invoked on the ``Client`` instance. 
     213 
     214``get(path, data={})`` 
     215    Make a GET request on the provided ``path``. The key-value pairs in the 
     216    data dictionary will be used to create a GET data payload. For example:: 
     217 
     218        c = Client() 
     219        c.get('/customers/details/', {'name':'fred', 'age':7}) 
     220 
     221    will result in the evaluation of a GET request equivalent to:: 
     222 
     223        http://yoursite.com/customers/details/?name='fred'&age=7 
     224 
     225``post(path, data={})`` 
     226    Make a POST request on the provided ``path``. The key-value pairs in the 
     227    data dictionary will be used to create the POST data payload. This payload 
     228    will be transmitted with the mimetype ``multipart/form-data``.  
     229 
     230    However submitting files is a special case. To POST a file, you need only 
     231    provide the file field name as a key, and a file handle to the file you wish to  
     232    upload as a value. The Test Client will populate the two POST fields (i.e.,  
     233    ``field`` and ``field_file``) required by FileField. For example:: 
     234 
     235        c = Client() 
     236        f = open('wishlist.doc') 
     237        c.post('/customers/wishes/', {'name':'fred', 'attachment':f}) 
     238        f.close() 
     239 
     240    will result in the evaluation of a POST request on ``/customers/wishes/``,  
     241    with a POST dictionary that contains `name`, `attachment` (containing the  
     242    file name), and `attachment_file` (containing the file data). Note that you 
     243    need to manually close the file after it has been provided to the POST. 
     244 
     245``login(path, username, password)`` 
     246    In a production site, it is likely that some views will be protected with 
     247    the @login_required URL provided by ``django.contrib.auth``. Interacting 
     248    with a URL that has been login protected is a slightly complex operation, 
     249    so the Test Client provides a simple URL to automate the login process. A 
     250    call to ``login()`` stimulates the series of GET and POST calls required 
     251    to log a user into a @login_required protected URL. 
     252 
     253    If login is possible, the final return value of ``login()`` is the response  
     254    that is generated by issuing a GET request on the protected URL. If login 
     255    is not possible, ``login()`` returns False. 
     256 
     257    Note that since the test suite will be executed using the test database,  
     258    which contains no users by default. As a result, logins for your production 
     259    site will not work. You will need to create users as part of the test suite  
     260    to be able to test logins to your application.  
     261 
     262Testing Responses 
     263~~~~~~~~~~~~~~~~~ 
     264 
     265The ``get()``, ``post()`` and ``login()`` methods all return a Response  
     266object. This Response object has the following properties that can be used  
     267for testing purposes: 
     268 
     269    ===============  ========================================================== 
     270    Property         Description 
     271    ===============  ========================================================== 
     272    ``status_code``  The HTTP status of the response. See RFC2616_ for a  
     273                     full list of HTTP status codes. 
     274 
     275    ``content``      The body of the response. The is the final page  
     276                     content as rendered by the view, or any error message  
     277                     (such as the URL for a 302 redirect). 
     278 
     279    ``template``     The Template instance that was used to render the final  
     280                     content. Testing ``template.name`` can be particularly  
     281                     useful; if the template was loaded from a file,  
     282                     ``template.name`` will be the file name that was loaded.  
     283 
     284                     If multiple templates were rendered, (e.g., if one  
     285                     template includes another template),``template`` will  
     286                     be a list of Template objects, in the order in which  
     287                     they were rendered. 
     288 
     289    ``context``      The Context that was used to render the template that  
     290                     produced the response content. 
     291 
     292                     As with ``template``, if multiple templates were rendered  
     293                     ``context`` will be a list of Context objects, stored in  
     294                     the order in which they were rendered.  
     295    ===============  ========================================================== 
     296 
     297.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 
     298 
     299The following is a simple unit test using the Test Client:: 
     300     
     301