Django

Code

Changeset 6021

Show
Ignore:
Timestamp:
08/26/07 12:47:34 (1 year ago)
Author:
jbronn
Message:

gis: Merged revisions 6000-6020 via svnmerge from trunk.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis

    • Property svnmerge-integrated changed from /django/trunk:1-6000 to /django/trunk:1-6020
  • django/branches/gis/AUTHORS

    r6018 r6021  
    9595    deric@monowerks.com 
    9696    Max Derkachev <mderk@yandex.ru> 
     97    Sander Dijkhuis <sander.dijkhuis@gmail.com> 
    9798    Jordan Dimov <s3x3y1@gmail.com> 
    9899    dne@mayonnaise.net 
  • django/branches/gis/django/contrib/admin/media/js/SelectBox.js

    r450 r6021  
    77        var cache = SelectBox.cache[id]; 
    88        for (var i = 0; (node = box.options[i]); i++) { 
    9             cache.push({ value: node.value, text: node.text, displayed: 1 }); 
     9            cache.push({value: node.value, text: node.text, displayed: 1}); 
    1010        } 
    1111    }, 
     
    5151    }, 
    5252    add_to_cache: function(id, option) { 
    53         SelectBox.cache[id].push({ value: option.value, text: option.text, displayed: 1 }); 
     53        SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); 
    5454    }, 
    5555    cache_contains: function(id, value) { 
     
    6969        for (var i = 0; (option = from_box.options[i]); i++) { 
    7070            if (option.selected && SelectBox.cache_contains(from, option.value)) { 
    71                 SelectBox.add_to_cache(to, { value: option.value, text: option.text, displayed: 1 }); 
     71                SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 
    7272                SelectBox.delete_from_cache(from, option.value); 
    7373            } 
     
    8181        var option; 
    8282        for (var i = 0; (option = from_box.options[i]); i++) { 
    83             SelectBox.add_to_cache(to, { value: option.value, text: option.text, displayed: 1 }); 
    84             SelectBox.delete_from_cache(from, option.value); 
     83            if (SelectBox.cache_contains(from, option.value)) { 
     84                SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 
     85                SelectBox.delete_from_cache(from, option.value); 
     86            } 
    8587        } 
    8688        SelectBox.redisplay(from); 
  • django/branches/gis/django/contrib/auth/views.py

    r6018 r6021  
    1818        if not errors: 
    1919            # Light security check -- make sure redirect_to isn't garbage. 
    20             if not redirect_to or '://' in redirect_to or ' ' in redirect_to: 
     20            if not redirect_to or '//' in redirect_to or ' ' in redirect_to: 
    2121                from django.conf import settings 
    2222                redirect_to = settings.LOGIN_REDIRECT_URL 
  • django/branches/gis/django/contrib/gis/tests/test_spatialrefsys.py

    r6018 r6021  
    5252    def test03_ellipsoid(self): 
    5353        "Testing the ellipsoid property." 
    54         return 
    5554        for sd in test_srs: 
    5655            # Getting the ellipsoid and precision parameters. 
  • django/branches/gis/django/core/management/commands/flush.py

    r6018 r6021  
    2525                pass 
    2626 
    27         sql_list = sql_flush(self.style
     27        sql_list = sql_flush(self.style, only_django=True
    2828 
    2929        if interactive: 
  • django/branches/gis/django/core/management/commands/runserver.py

    r6018 r6021  
    3131 
    3232        use_reloader = options.get('use_reloader', True) 
    33         admin_media_dir = options.get('admin_media_dir', '') 
     33        admin_media_path = options.get('admin_media_path', '') 
    3434        shutdown_message = options.get('shutdown_message', '') 
    3535        quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' 
     
    4343            print "Quit the server with %s." % quit_command 
    4444            try: 
    45                 path = admin_media_dir or django.__path__[0] + '/contrib/admin/media' 
     45                path = admin_media_path or django.__path__[0] + '/contrib/admin/media' 
    4646                handler = AdminMediaHandler(WSGIHandler(), path) 
    4747                run(addr, int(port), handler) 
  • django/branches/gis/django/core/management/commands/sqlflush.py

    r6018 r6021  
    88    def handle_noargs(self, **options): 
    99        from django.core.management.sql import sql_flush 
    10         return '\n'.join(sql_flush(self.style)) 
     10        return '\n'.join(sql_flush(self.style, only_django=True)) 
  • django/branches/gis/django/core/management/sql.py

    r6018 r6021  
    1313    cursor = connection.cursor() 
    1414    return get_introspection_module().get_table_list(cursor) 
     15 
     16def django_table_list(only_existing=False): 
     17    """ 
     18    Returns a list of all table names that have associated Django models and 
     19    are in INSTALLED_APPS. 
     20 
     21    If only_existing is True, the resulting list will only include the tables 
     22    that actually exist in the database. 
     23    """ 
     24    from django.db import models 
     25    tables = [] 
     26    for app in models.get_apps(): 
     27        for model in models.get_models(app): 
     28            tables.append(model._meta.db_table) 
     29            tables.extend([f.m2m_db_table() for f in model._meta.many_to_many]) 
     30    if only_existing: 
     31        existing = table_list() 
     32        tables = [t for t in tables if t in existing] 
     33    return tables 
    1534 
    1635def installed_models(table_list): 
     
    182201    return sql_delete(app, style) + sql_all(app, style) 
    183202 
    184 def sql_flush(style): 
    185     "Returns a list of the SQL statements used to flush the database." 
     203def sql_flush(style, only_django=False): 
     204    """ 
     205    Returns a list of the SQL statements used to flush the database. 
     206     
     207    If only_django is True, then only table names that have associated Django 
     208    models and are in INSTALLED_APPS will be included. 
     209    """ 
    186210    from django.db import connection 
    187     statements = connection.ops.sql_flush(style, table_list(), sequence_list()) 
     211    if only_django: 
     212        tables = django_table_list() 
     213    else: 
     214        tables = table_list() 
     215    statements = connection.ops.sql_flush(style, tables, sequence_list()) 
    188216    return statements 
    189217 
  • django/branches/gis/django/db/backends/dummy/base.py

    r6018 r6021  
    99 
    1010from django.core.exceptions import ImproperlyConfigured 
     11from django.db.backends import BaseDatabaseFeatures, BaseDatabaseOperations 
    1112 
    1213def complain(*args, **kwargs): 
     
    2223    pass 
    2324 
    24 class ComplainOnGetattr(object): 
    25     def __getattr__(self, *args, **kwargs): 
    26         complain() 
     25class DatabaseOperations(BaseDatabaseOperations): 
     26    quote_name = complain 
    2727 
    2828class DatabaseWrapper(object): 
    29     features = ComplainOnGetattr() 
    30     ops = ComplainOnGetattr() 
     29    features = BaseDatabaseFeatures() 
     30    ops = DatabaseOperations() 
    3131    operators = {} 
    3232    cursor = complain 
  • django/branches/gis/django/db/backends/postgresql/base.py

    r6018 r6021  
    103103        cursor.execute("SET client_encoding to 'UNICODE'") 
    104104        cursor = UnicodeCursorWrapper(cursor, 'utf-8') 
    105         if self.ops.postgres_version is None: 
    106             cursor.execute("SELECT version()") 
    107             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    108105        return cursor 
    109106 
  • django/branches/gis/django/db/backends/postgresql/operations.py

    r6018 r6021  
    55 
    66class DatabaseOperations(BaseDatabaseOperations): 
    7     def __init__(self, postgres_version=None): 
    8         self.postgres_version = postgres_version 
     7    def __init__(self): 
     8        self._postgres_version = None 
     9 
     10    def _get_postgres_version(self): 
     11        if self._postgres_version is None: 
     12            from django.db import connection 
     13            cursor = connection.cursor() 
     14            cursor.execute("SELECT version()") 
     15            self._postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
     16        return self._postgres_version 
     17    postgres_version = property(_get_postgres_version) 
    918 
    1019    def date_extract_sql(self, lookup_type, field_name): 
     
    5362                table_name = sequence_info['table'] 
    5463                column_name = sequence_info['column'] 
    55                 if column_name and len(column_name)>0: 
    56                     # sequence name in this case will be <table>_<column>_seq 
    57                     sql.append("%s %s %s %s %s %s;" % \ 
    58                         (style.SQL_KEYWORD('ALTER'), 
    59                         style.SQL_KEYWORD('SEQUENCE'), 
    60                         style.SQL_FIELD(self.quote_name('%s_%s_seq' % (table_name, column_name))), 
    61                         style.SQL_KEYWORD('RESTART'), 
    62                         style.SQL_KEYWORD('WITH'), 
    63                         style.SQL_FIELD('1') 
    64                         ) 
    65                     ) 
     64                if column_name and len(column_name) > 0: 
     65                    sequence_name = '%s_%s_seq' % (table_name, column_name) 
    6666                else: 
    67                     # sequence name in this case will be <table>_id_seq 
    68                     sql.append("%s %s %s %s %s %s;" % \ 
    69                         (style.SQL_KEYWORD('ALTER'), 
    70                          style.SQL_KEYWORD('SEQUENCE'), 
    71                          style.SQL_FIELD(self.quote_name('%s_id_seq' % table_name)), 
    72                          style.SQL_KEYWORD('RESTART'), 
    73                          style.SQL_KEYWORD('WITH'), 
    74                          style.SQL_FIELD('1') 
    75                          ) 
    76                     ) 
     67                    sequence_name = '%s_id_seq' % table_name 
     68                sql.append("%s setval('%s', 1, false);" % \ 
     69                    (style.SQL_KEYWORD('SELECT'), 
     70                    style.SQL_FIELD(self.quote_name(sequence_name))) 
     71                ) 
    7772            return sql 
    7873        else: 
  • django/branches/gis/django/db/backends/postgresql_psycopg2/base.py

    r6018 r6021  
    6565        if set_tz: 
    6666            cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) 
    67         if self.ops.postgres_version is None: 
    68             cursor.execute("SELECT version()") 
    69             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    7067        return cursor 
  • django/branches/gis/django/template/defaultfilters.py

    r6018 r6021  
    356356    WITHOUT opening and closing <ul> tags. 
    357357 
    358     The list is assumed to be in the proper format. For example, if ``var`` contains 
    359     ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``, 
     358    The list is assumed to be in the proper format. For example, if ``var`` 
     359    contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, 
    360360    then ``{{ var|unordered_list }}`` would return:: 
    361361 
     
    372372        </li> 
    373373    """ 
    374     def _helper(value, tabs): 
     374    def convert_old_style_list(list_): 
     375        """ 
     376        Converts old style lists to the new easier to understand format. 
     377 
     378        The old list format looked like: 
     379            ['Item 1', [['Item 1.1', []], ['Item 1.2', []]] 
     380 
     381        And it is converted to: 
     382            ['Item 1', ['Item 1.1', 'Item 1.2]] 
     383        """ 
     384        if not isinstance(list_, (tuple, list)) or len(list_) != 2: 
     385            return list_, False 
     386        first_item, second_item = list_ 
     387        if second_item == []: 
     388            return [first_item], True 
     389        old_style_list = True 
     390        new_second_item = [] 
     391        for sublist in second_item: 
     392            item, old_style_list = convert_old_style_list(sublist) 
     393            if not old_style_list: 
     394                break 
     395            new_second_item.extend(item) 
     396        if old_style_list: 
     397            second_item = new_second_item 
     398        return [first_item, second_item], old_style_list 
     399    def _helper(list_, tabs=1): 
    375400        indent = u'\t' * tabs 
    376         if value[1]: 
    377             return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, force_unicode(value[0]), indent, 
    378                 u'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 
    379         else: 
    380             return u'%s<li>%s</li>' % (indent, force_unicode(value[0])) 
    381     return _helper(value, 1) 
     401        output = [] 
     402 
     403        list_length = len(list_) 
     404        i = 0 
     405        while i < list_length: 
     406            title = list_[i] 
     407            sublist = '' 
     408            sublist_item = None 
     409            if isinstance(title, (list, tuple)): 
     410                sublist_item = title  
     411                title = '' 
     412            elif i < list_length - 1: 
     413                next_item = list_[i+1] 
     414                if next_item and isinstance(next_item, (list, tuple)): 
     415                    # The next item is a sub-list. 
     416                    sublist_item = next_item 
     417                    # We've processed the next item now too. 
     418                    i += 1  
     419            if sublist_item: 
     420                sublist = _helper(sublist_item, tabs+1) 
     421                sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist, 
     422                                                         indent, indent) 
     423            output.append('%s<li>%s%s</li>' % (indent, force_unicode(title), 
     424                                               sublist)) 
     425            i += 1 
     426        return '\n'.join(output) 
     427    value, converted = convert_old_style_list(value)  
     428    return _helper(value) 
    382429 
    383430################### 
  • django/branches/gis/docs/db-api.txt

    r6018 r6021  
    208208The ``save()`` method has no return value. 
    209209 
    210 Updating ``ForeignKey`` fields works exactly the same way; simply assign an 
    211 object of the right type to the field in question:: 
     210Saving ForeignKey and ManyToManyField fields 
     211-------------------------------------------- 
     212 
     213Updating ``ForeignKey`` fields works exactly the same way as saving a normal 
     214field; simply assign an object of the right type to the field in question::  
     215 
     216    cheese_blog = Blog.objects.get(name="Cheddar Talk")  
     217    entry.blog = cheese_blog  
     218    entry.save()  
     219 
     220Updating a ``ManyToManyField`` works a little differently; use the ``add()`` 
     221method on the field to add a record to the relation:: 
    212222 
    213223    joe = Author.objects.create(name="Joe") 
    214     entry.author = joe 
    215     entry.save() 
    216  
    217 Django will complain if you try to assign an object of the wrong type. 
     224    entry.authors.add(joe) 
     225 
     226Django will complain if you try to assign or add an object of the wrong type. 
    218227 
    219228How Django knows to UPDATE vs. INSERT 
  • django/branches/gis/docs/django-admin.txt

    r6018 r6021  
    124124post-synchronization handlers will be re-executed, and the ``initial_data`` 
    125125fixture will be re-installed. 
     126 
     127The behavior of this command has changed in the Django development version. 
     128Previously, this command cleared *every* table in the database, including any 
     129table that Django didn't know about (i.e., tables that didn't have associated 
     130models and/or weren't in ``INSTALLED_APPS``). Now, the command only clears 
     131tables that are represented by Django models and are activated in 
     132``INSTALLED_APPS``. 
    126133 
    127134inspectdb 
     
    241248runfcgi [options] 
    242249----------------- 
     250 
    243251Starts a set of FastCGI processes suitable for use with any web server 
    244252which supports the FastCGI protocol. See the `FastCGI deployment 
     
    338346 
    339347sqlclear [appname appname ...] 
    340 -------------------------------------- 
     348------------------------------ 
    341349 
    342350Prints the DROP TABLE SQL statements for the given appnames. 
     
    361369Note that the order in which the SQL files are processed is undefined. 
    362370 
     371sqlflush 
     372-------- 
     373 
     374Prints the SQL statements that would be executed for the `flush`_ command. 
     375 
    363376sqlindexes [appname appname ...] 
    364 ---------------------------------------- 
     377-------------------------------- 
    365378 
    366379Prints the CREATE INDEX SQL statements for the given appnames. 
    367380 
    368381sqlreset [appname appname ...] 
     382------------------------------ 
     383 
     384Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given appnames. 
     385 
     386sqlsequencereset [appname appname ...] 
    369387-------------------------------------- 
    370  
    371 Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given appnames. 
    372  
    373 sqlsequencereset [appname appname ...] 
    374 ---------------------------------------------- 
    375388 
    376389Prints the SQL statements for resetting sequences for the given 
     
    467480not yet accept a ``host`` or ``port`` parameter. 
    468481 
     482Also note that it does *not* automatically detect changes to your Python source 
     483code (as ``runserver`` does). It does, however, detect changes to templates. 
     484 
    469485.. _unit tests: ../testing/ 
    470486 
  • django/branches/gis/docs/faq.txt

    r6018 r6021  
    205205spend a lot of energy creating screencasts yet, because Django APIs will shift. 
    206206 
    207 In the meantime, though, check out this `unofficial Django screencast`_. 
    208  
    209 .. _unofficial Django screencast: http://www.throwingbeans.org/django_screencasts.html 
    210  
    211207Is Django a content-management-system (CMS)? 
    212208-------------------------------------------- 
  • django/branches/gis/docs/model-api.txt

    r6018 r6021  
    345345~~~~~~~~~~~~~~ 
    346346 
    347 Like ``FileField``, but validates that the uploaded object is a valid 
     347Like `FileField`_, but validates that the uploaded object is a valid 
    348348image. Has two extra optional arguments, ``height_field`` and 
    349349``width_field``, which, if set, will be auto-populated with the height and 
  • django/branches/gis/docs/newforms.txt

    r6018 r6021  
    15551555        def __init__(self, *args, **kwargs): 
    15561556            kwargs.setdefault('attrs',{}).update({'size': '40'}) 
    1557             super(forms.TextInput, self).__init__(*args, **kwargs) 
     1557            super(CommentWidget, self).__init__(*args, **kwargs) 
    15581558 
    15591559Then you can use this widget in your forms:: 
  • django/branches/gis/docs/settings.txt

    r6018 r6021  
    11021102---------- 
    11031103 
    1104 When ``DEBUG`` is ``False`` and your ``MIDDLEWARE_CLASSES`` setting includes 
    1105 ``CommonMiddleware``, Django will e-mail the users listed in the ``MANAGERS`` 
    1106 setting whenever your code raises a 404 and the request has a referer. 
    1107 (It doesn't bother to e-mail for 404s that don't have a referer.) 
     1104When ``DEBUG`` is ``False``, ``SEND_BROKEN_LINK_EMAILS`` is ``True`` and your 
     1105``MIDDLEWARE_CLASSES`` setting includes ``CommonMiddleware``, Django will 
     1106e-mail the users listed in the ``MANAGERS`` setting whenever your code raises 
     1107a 404 and the request has a referer. (It doesn't bother to e-mail for 404s 
     1108that don't have a referer.) 
    11081109 
    11091110You can tell Django to stop reporting particular 404s by tweaking the 
  • django/branches/gis/docs/templates.txt

    r6018 r6021  
    13021302WITHOUT opening and closing <ul> tags. 
    13031303 
     1304**Changed in Django development version** 
     1305 
     1306The format accepted by ``unordered_list`` has changed to an easier to 
     1307understand format. 
     1308 
    13041309The list is assumed to be in the proper format. For example, if ``var`` contains 
    1305 ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``, 
    1306 then ``{{ var|unordered_list }}`` would return:: 
     1310``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then 
     1311``{{ var|unordered_list }}`` would return:: 
    13071312 
    13081313    <li>States 
     
    13181323    </li> 
    13191324 
     1325Note: the previous more restrictive and verbose format is still supported: 
     1326``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``, 
     1327 
    13201328upper 
    13211329~~~~~ 
  • django/branches/gis/docs/tutorial01.txt

    r6018 r6021  
    259259        choice = models.CharField(max_length=200) 
    260260        votes = models.IntegerField() 
     261 
     262.. admonition:: Errors about ``max_length`` 
     263 
     264   If Django gives you an error message saying that ``max_length`` is 
     265   not a valid argument, you're most likely using an old version of 
     266   Django. (This version of the tutorial is written for the latest 
     267   development version of Django.) If you're using a Subversion checkout 
     268   of Django's development version (see `the installation docs`_ for 
     269   more information), you shouldn't have any problems. 
     270 
     271   If you want to stick with an older version of Django, you'll want to 
     272   switch to `the Django 0.96 tutorial`_, because this tutorial covers 
     273   several features that only exist in the Django development version. 
     274 
     275.. _the installation docs: ../install/ 
     276.. _the Django 0.96 tutorial: ../0.96/tutorial01/ 
    261277 
    262278The code is straightforward. Each model is represented by a class that 
     
    488504            return self.choice 
    489505 
     506.. admonition:: If ``__unicode__()`` doesn't seem to work 
     507 
     508   If you add the ``__unicode__()`` method to your models and don't 
     509   see any change in how they're represented, you're most likely using 
     510   an old version of Django. (This version of the tutorial is written 
     511   for the latest development version of Django.) If you're using a 
     512   Subversion checkout of of Django's development version (see `the 
     513   installation docs`_ for more information), you shouldn't have any 
     514   problems. 
     515 
     516   If you want to stick with an older version of Django, you'll want to 
     517   switch to `the Django 0.96 tutorial`_, because this tutorial covers 
     518   several features that only exist in the Django development version. 
     519 
     520.. _the installation docs: ../install/ 
     521.. _the Django 0.96 tutorial: ../0.96/tutorial01/ 
     522 
    490523It's important to add ``__unicode__()`` methods to your models, not only for 
    491524your own sanity when dealing with the interactive prompt, but also because 
  • django/branches/gis/tests/regressiontests/defaultfilters/tests.py

    r6018 r6021  
    267267u'aceg' 
    268268 
     269>>> unordered_list([u'item 1', u'item 2']) 
     270u'\t<li>item 1</li>\n\t<li>item 2</li>' 
     271 
     272>>> unordered_list([u'item 1', [u'item 1.1']]) 
     273u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>' 
     274 
     275>>> unordered_list([u'item 1', [u'item 1.1', u'item1.2'], u'item 2']) 
     276u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item1.2</li>\n\t</ul>\n\t</li>\n\t<li>item 2</li>' 
     277 
     278>>> unordered_list([u'item 1', [u'item 1.1', [u'item 1.1.1', [u'item 1.1.1.1']]]]) 
     279u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1\n\t\t<ul>\n\t\t\t<li>item 1.1.1\n\t\t\t<ul>\n\t\t\t\t<li>item 1.1.1.1</li>\n\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t\t</li>\n\t</ul>\n\t</li>' 
     280 
     281>>> unordered_list(['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]) 
     282u'\t<li>States\n\t<ul>\n\t\t<li>Kansas\n\t\t<ul>\n\t\t\t<li>Lawrence</li>\n\t\t\t<li>Topeka</li>\n\t\t</ul>\n\t\t</li>\n\t\t<li>Illinois</li>\n\t</ul>\n\t</li>' 
     283 
     284# Old format for unordered lists should still work 
    269285>>> unordered_list([u'item 1', []]) 
    270286u'\t<li>item 1</li>' 
     
    275291>>> unordered_list([u'item 1', [[u'item 1.1', []], [u'item 1.2', []]]]) 
    276292u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>' 
     293 
     294>>> unordered_list(['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]) 
     295u'\t<li>States\n\t<ul>\n\t\t<li>Kansas\n\t\t<ul>\n\t\t\t<li>Lawrence</li>\n\t\t\t<li>Topeka</li>\n\t\t</ul>\n\t\t</li>\n\t\t<li>Illinois</li>\n\t</ul>\n\t</li>' 
    277296 
    278297>>> add(u'1', u'2')