Django

Code

Changeset 4254

Show
Ignore:
Timestamp:
12/28/06 11:17:52 (2 years ago)
Author:
bouldersprinters
Message:

boulder-oracle-sprint: Merged to trunk [4253]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/boulder-oracle-sprint/AUTHORS

    r4212 r4254  
    119119    Petar Marić 
    120120    mark@junklight.com 
     121    Yasushi Masuda <whosaysni@gmail.com> 
    121122    mattycakes@gmail.com 
    122123    Jason McBrayer <http://www.carcosa.net/jason/> 
    123124    mccutchen@gmail.com 
    124125    michael.mcewan@gmail.com 
     126    mitakummaa@gmail.com 
    125127    mmarshall 
    126128    Eric Moritz <http://eric.themoritzfamily.com/> 
     
    161163    Joe Topjian <http://joe.terrarum.net/geek/code/python/django/> 
    162164    Karen Tracey <graybark@bellsouth.net> 
     165    Makoto Tsuyuki <mtsuyuki@gmail.com> 
    163166    Amit Upadhyay 
    164167    Geert Vanderkelen 
  • django/branches/boulder-oracle-sprint/django/contrib/admin/templates/admin/base.html

    r3600 r4254  
    3939        {% block pretitle %}{% endblock %} 
    4040        {% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %} 
    41         {% block content %}{{ content }}{% endblock %} 
     41        {% block content %} 
     42        {% block object-tools %}{% endblock %} 
     43        {{ content }} 
     44        {% endblock %} 
    4245        {% block sidebar %}{% endblock %} 
    4346        <br class="clear" /> 
  • django/branches/boulder-oracle-sprint/django/contrib/admin/templates/admin/change_form.html

    r3568 r4254  
    1717{% endif %}{% endblock %} 
    1818{% block content %}<div id="content-main"> 
     19{% block object-tools %} 
    1920{% if change %}{% if not is_popup %} 
    2021  <ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li> 
     
    2223  </ul> 
    2324{% endif %}{% endif %} 
     25{% endblock %} 
    2426<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %} 
    2527<div> 
  • django/branches/boulder-oracle-sprint/django/contrib/admin/templates/admin/change_list.html

    r3349 r4254  
    88{% block content %} 
    99<div id="content-main"> 
     10{% block object-tools %} 
    1011{% if has_add_permission %} 
    1112<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul> 
    1213{% endif %} 
     14{% endblock %} 
    1315<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist"> 
    1416{% block search %}{% search_form cl %}{% endblock %} 
  • django/branches/boulder-oracle-sprint/django/contrib/csrf/middleware.py

    r2900 r4254  
    1212import itertools 
    1313 
    14 _ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected.  Request aborted.</p>" 
     14_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>' 
    1515 
    1616_POST_FORM_RE = \ 
  • django/branches/boulder-oracle-sprint/django/core/handlers/base.py

    r4212 r4254  
    6161                return response 
    6262 
    63         resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF) 
     63        # Get urlconf from request object, if available.  Otherwise use default. 
     64        urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) 
     65 
     66        resolver = urlresolvers.RegexURLResolver(r'^/', urlconf) 
    6467        try: 
    6568            callback, callback_args, callback_kwargs = resolver.resolve(request.path) 
  • django/branches/boulder-oracle-sprint/django/db/backends/oracle/creation.py

    r4085 r4254  
    8282    #settings.DATABASE_USER = 'old_user' 
    8383    #settings.DATABASE_PASSWORD = 'old_password' 
     84    settings.DATABASE_USER = 'mboersma' 
     85    settings.DATABASE_PASSWORD = 'password' 
    8486 
    8587    cursor = connection.cursor() 
  • django/branches/boulder-oracle-sprint/django/db/backends/postgresql/base.py

    r4084 r4254  
    2020    # Import copy of _thread_local.py from Python 2.4 
    2121    from django.utils._threading_local import local 
     22 
     23def smart_basestring(s, charset): 
     24    if isinstance(s, unicode): 
     25        return s.encode(charset) 
     26    return s 
     27 
     28class UnicodeCursorWrapper(object): 
     29    """ 
     30    A thin wrapper around psycopg cursors that allows them to accept Unicode 
     31    strings as params. 
     32 
     33    This is necessary because psycopg doesn't apply any DB quoting to 
     34    parameters that are Unicode strings. If a param is Unicode, this will 
     35    convert it to a bytestring using DEFAULT_CHARSET before passing it to 
     36    psycopg. 
     37    """ 
     38    def __init__(self, cursor, charset): 
     39        self.cursor = cursor 
     40        self.charset = charset 
     41 
     42    def execute(self, sql, params=()): 
     43        return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params]) 
     44 
     45    def executemany(self, sql, param_list): 
     46        new_param_list = [[smart_basestring(p, self.charset) for p in params] for params in param_list] 
     47        return self.cursor.executemany(sql, new_param_list) 
     48 
     49    def __getattr__(self, attr): 
     50        if self.__dict__.has_key(attr): 
     51            return self.__dict__[attr] 
     52        else: 
     53            return getattr(self.cursor, attr) 
    2254 
    2355class DatabaseWrapper(local): 
     
    4678        cursor = self.connection.cursor() 
    4779        cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) 
     80        cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET) 
    4881        if settings.DEBUG: 
    4982            return util.CursorDebugWrapper(cursor, self) 
     
    132165    Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date)) 
    133166except AttributeError: 
    134     raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1
     167    raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'.
    135168Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time)) 
    136169Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp)) 
  • django/branches/boulder-oracle-sprint/django/db/models/fields/__init__.py

    r4212 r4254  
    338338    choices = property(_get_choices) 
    339339 
    340     def formfield(self): 
     340    def formfield(self, initial=None): 
    341341        "Returns a django.newforms.Field instance for this database Field." 
    342342        from django.newforms import CharField 
    343343        # TODO: This is just a temporary default during development. 
    344         return CharField(label=capfirst(self.verbose_name)) 
     344        return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
     345 
     346    def value_from_object(self, obj): 
     347        "Returns the value of this field in the given model instance." 
     348        return getattr(obj, self.attname) 
    345349 
    346350class AutoField(Field): 
     
    380384        cls._meta.has_auto_field = True 
    381385 
     386    def formfield(self, initial=None): 
     387        return None 
     388 
    382389class BooleanField(Field): 
    383390    def __init__(self, *args, **kwargs): 
     
    393400    def get_manipulator_field_objs(self): 
    394401        return [oldforms.CheckboxField] 
     402 
     403    def formfield(self, initial=None): 
     404        return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    395405 
    396406class CharField(Field): 
     
    407417                raise validators.ValidationError, gettext_lazy("This field cannot be null.") 
    408418        return str(value) 
     419 
     420    def formfield(self, initial=None): 
     421        return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    409422 
    410423# TODO: Maybe move this into contrib, because it's specialized. 
     
    481494        return [oldforms.DateField] 
    482495 
    483     def flatten_data(self, follow, obj = None): 
     496    def flatten_data(self, follow, obj=None): 
    484497        val = self._get_val_from_obj(obj) 
    485498        return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')} 
     499 
     500    def formfield(self, initial=None): 
     501        return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    486502 
    487503class DateTimeField(DateField): 
     
    554570                    time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 
    555571 
     572    def formfield(self, initial=None): 
     573        return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
     574 
    556575class EmailField(CharField): 
    557576    def __init__(self, *args, **kwargs): 
     
    567586    def validate(self, field_data, all_data): 
    568587        validators.isValidEmail(field_data, all_data) 
     588 
     589    def formfield(self, initial=None): 
     590        return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    569591 
    570592class FileField(Field): 
     
    700722        return [oldforms.IntegerField] 
    701723 
     724    def formfield(self, initial=None): 
     725        return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
     726 
    702727class IPAddressField(Field): 
    703728    def __init__(self, *args, **kwargs): 
     
    800825        return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} 
    801826 
     827    def formfield(self, initial=None): 
     828        return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
     829 
    802830class URLField(Field): 
    803831    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 
    804832        if verify_exists: 
    805833            kwargs.setdefault('validator_list', []).append(validators.isExistingURL) 
     834        self.verify_exists = verify_exists 
    806835        Field.__init__(self, verbose_name, name, **kwargs) 
    807836 
    808837    def get_manipulator_field_objs(self): 
    809838        return [oldforms.URLField] 
     839 
     840    def formfield(self, initial=None): 
     841        return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial) 
    810842 
    811843class USStateField(Field): 
  • django/branches/boulder-oracle-sprint/django/db/models/fields/related.py

    r4212 r4254  
    33from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class 
    44from django.db.models.related import RelatedObject 
     5from django.utils.text import capfirst 
    56from django.utils.translation import gettext_lazy, string_concat, ngettext 
    67from django.utils.functional import curry 
    78from django.core import validators 
    89from django import oldforms 
     10from django import newforms as forms 
    911from django.dispatch import dispatcher 
    1012 
     
    257259        if self.related.field.null: 
    258260            manager.clear() 
    259         for obj in value: 
    260             manager.add(obj) 
     261        manager.add(*value) 
    261262 
    262263def create_many_related_manager(superclass): 
     
    319320            from django.db import connection 
    320321 
    321             # Add the newly created or already existing objects to the join table. 
    322             # First find out which items are already added, to avoid adding them twice 
    323             new_ids = set([obj._get_pk_val() for obj in objs]) 
    324             cursor = connection.cursor() 
    325             cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ 
    326                 (target_col_name, self.join_table, source_col_name, 
    327                 target_col_name, ",".join(['%s'] * len(new_ids))), 
    328                 [self._pk_val] + list(new_ids)) 
    329             if cursor.rowcount is not None and cursor.rowcount != 0: 
    330                 existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 
    331             else: 
    332                 existing_ids = set() 
    333  
    334             # Add the ones that aren't there already 
    335             for obj_id in (new_ids - existing_ids): 
    336                 cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ 
    337                     (self.join_table, source_col_name, target_col_name), 
    338                     [self._pk_val, obj_id]) 
    339             transaction.commit_unless_managed() 
     322            # If there aren't any objects, there is nothing to do. 
     323            if objs: 
     324                # Check that all the objects are of the right type 
     325                for obj in objs: 
     326                    if not isinstance(obj, self.model): 
     327                        raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name 
     328                # Add the newly created or already existing objects to the join table. 
     329                # First find out which items are already added, to avoid adding them twice 
     330                new_ids = set([obj._get_pk_val() for obj in objs]) 
     331                cursor = connection.cursor() 
     332                cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ 
     333                    (target_col_name, self.join_table, source_col_name, 
     334                    target_col_name, ",".join(['%s'] * len(new_ids))), 
     335                    [self._pk_val] + list(new_ids)) 
     336                if cursor.rowcount is not None and cursor.rowcount != 0: 
     337                    existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 
     338                else: 
     339                    existing_ids = set() 
     340 
     341                # Add the ones that aren't there already 
     342                for obj_id in (new_ids - existing_ids): 
     343                    cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ 
     344                        (self.join_table, source_col_name, target_col_name), 
     345                        [self._pk_val, obj_id]) 
     346                transaction.commit_unless_managed() 
    340347 
    341348        def _remove_items(self, source_col_name, target_col_name, *objs): 
     
    345352            from django.db import connection 
    346353 
    347             for obj in objs: 
    348                 if not isinstance(obj, self.model): 
    349                     raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name 
    350             # Remove the specified objects from the join table 
    351             cursor = connection.cursor() 
    352             for obj in objs: 
    353                 cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \ 
    354                     (self.join_table, source_col_name, target_col_name), 
    355                     [self._pk_val, obj._get_pk_val()]) 
    356             transaction.commit_unless_managed() 
     354            # If there aren't any objects, there is nothing to do. 
     355            if objs: 
     356                # Check that all the objects are of the right type 
     357                for obj in objs: 
     358                    if not isinstance(obj, self.model): 
     359                        raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name 
     360                # Remove the specified objects from the join table 
     361                old_ids = set([obj._get_pk_val() for obj in objs]) 
     362                cursor = connection.cursor() 
     363                cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \ 
     364                    (self.join_table, source_col_name, 
     365                    target_col_name, ",".join(['%s'] * len(old_ids))), 
     366                    [self._pk_val] + list(old_ids)) 
     367                transaction.commit_unless_managed() 
    357368 
    358369        def _clear_items(self, source_col_name): 
     
    406417        manager = self.__get__(instance) 
    407418        manager.clear() 
    408         for obj in value: 
    409             manager.add(obj) 
     419        manager.add(*value) 
    410420 
    411421class ReverseManyRelatedObjectsDescriptor(object): 
     
    448458        manager = self.__get__(instance) 
    449459        manager.clear() 
    450         for obj in value: 
    451             manager.add(obj) 
     460        manager.add(*value) 
    452461 
    453462class ForeignKey(RelatedField, Field): 
     
    540549        setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) 
    541550 
     551    def formfield(self, initial=None): 
     552        return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
     553 
    542554class OneToOneField(RelatedField, IntegerField): 
    543555    def __init__(self, to, to_field=None, **kwargs): 
     
    600612        if not cls._meta.one_to_one_field: 
    601613            cls._meta.one_to_one_field = self 
     614 
     615    def formfield(self, initial=None): 
     616        return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    602617 
    603618class ManyToManyField(RelatedField, Field): 
     
    709724    def set_attributes_from_rel(self): 
    710725        pass 
     726 
     727    def value_from_object(self, obj): 
     728        "Returns the value of this field in the given model instance." 
     729        return getattr(obj, self.attname).all() 
     730 
     731    def formfield(self, initial=None): 
     732        return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) 
    711733 
    712734class ManyToOneRel(object): 
  • django/branches/boulder-oracle-sprint/django/forms/__init__.py

    r4066 r4254  
    1 from django.core import validators 
    2 from django.core.exceptions import PermissionDenied 
    3 from django.utils.html import escape 
    4 from django.conf import settings 
    5 from django.utils.translation import gettext, ngettext 
    6  
    7 FORM_FIELD_ID_PREFIX = 'id_' 
    8  
    9 class EmptyValue(Exception): 
    10     "This is raised when empty data is provided" 
    11     pass 
    12  
    13 class Manipulator(object): 
    14     # List of permission strings. User must have at least one to manipulate. 
    15     # None means everybody has permission. 
    16     required_permission = '' 
    17  
    18     def __init__(self): 
    19         # List of FormField objects 
    20         self.fields = [] 
    21  
    22     def __getitem__(self, field_name): 
    23         "Looks up field by field name; raises KeyError on failure" 
    24         for field in self.fields: 
    25             if field.field_name == field_name: 
    26                 return field 
    27         raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields)) 
    28  
    29     def __delitem__(self, field_name): 
    30         "Deletes the field with the given field name; raises KeyError on failure" 
    31         for i, field in enumerate(self.fields): 
    32             if field.field_name == field_name: 
    33                 del self.fields[i] 
    34                 return 
    35         raise KeyError, "Field %s not found" % field_name 
    36  
    37     def check_permissions(self, user): 
    38         """Confirms user has required permissions to use this manipulator; raises 
    39         PermissionDenied on failure.""" 
    40         if self.required_permission is None: 
    41             return 
    42         if user.has_perm(self.required_permission): 
    43             return 
    44         raise PermissionDenied 
    45  
    46     def prepare(self, new_data): 
    47         """ 
    48         Makes any necessary preparations to new_data, in place, before data has 
    49         been validated. 
    50         """ 
    51         for field in self.fields: 
    52             field.prepare(new_data) 
    53  
    54     def get_validation_errors(self, new_data): 
    55         "Returns dictionary mapping field_names to error-message lists" 
    56         errors = {} 
    57         self.prepare(new_data) 
    58         for field in self.fields: 
    59             errors.update(field.get_validation_errors(new_data)) 
    60             val_name = 'validate_%s' % field.field_name 
    61             if hasattr(self, val_name): 
    62                 val = getattr(self, val_name) 
    63                 try: 
    64                     field.run_validator(new_data, val) 
    65                 except (validators.ValidationError, validators.CriticalValidationError), e: 
    66                     errors.setdefault(field.field_name, []).extend(e.messages) 
    67  
    68 #            if field.is_required and not new_data.get(field.field_name, False): 
    69 #                errors.setdefault(field.field_name, []).append(gettext_lazy('This field is required.')) 
    70 #                continue 
    71 #            try: 
    72 #                validator_list = field.validator_list 
    73 #                if hasattr(self, 'validate_%s' % field.field_name): 
    74 #                    validator_list.append(getattr(self, 'validate_%s' % field.field_name)) 
    75 #                for validator in validator_list: 
    76 #                    if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'): 
    77 #                        try: 
    78 #                            if hasattr(field, 'requires_data_list'): 
    79 #                                validator(new_data.getlist(field.field_name), new_data) 
    80 #                            else: 
    81 #                                validator(new_data.get(field.field_name, ''), new_data) 
    82 #                        except validators.ValidationError, e: 
    83 #                            errors.setdefault(field.field_name, []).extend(e.messages) 
    84 #            # If a CriticalValidationError is raised, ignore any other ValidationErrors 
    85 #            # for this particular field 
    86 #            except validators.CriticalValidationError, e: 
    87 #                errors.setdefault(field.field_name, []).extend(e.messages) 
    88         return errors 
    89  
    90     def save(self, new_data): 
    91         "Saves the changes and returns the new object" 
    92         # changes is a dictionary-like object keyed by field_name 
    93         raise NotImplementedError 
    94  
    95     def do_html2python(self, new_data): 
    96         """ 
    97         Convert the data from HTML data types to Python datatypes, changing the 
    98         object in place. This happens after validation but before storage. This 
    99         must happen after validation because html2python functions aren't 
    100         expected to deal with invalid input. 
    101         """ 
    102         for field in self.fields: 
    103             field.convert_post_data(new_data) 
    104  
    105 class FormWrapper(object): 
    106     """ 
    107     A wrapper linking a Manipulator to the template system. 
    108     This allows dictionary-style lookups of formfields. It also handles feeding 
    109     prepopulated data and validation error messages to the formfield objects. 
    110     """ 
    111     def __init__(self, manipulator, data=None, error_dict=None, edit_inline=True): 
    112         self.manipulator = manipulator 
    113         if data is None: 
    114             data = {} 
    115         if error_dict is None: 
    116             error_dict = {} 
    117         self.data = data 
    118         self.error_dict = error_dict 
    119         self._inline_collections = None 
    120         self.edit_inline = edit_inline 
    121  
    122     def __repr__(self): 
    123         return repr(self.__dict__) 
    124  
    125     def __getitem__(self, key): 
    126         for field in self.manipulator.fields: 
    127             if field.field_name == key: 
    128                 data = field.extract_data(self.data) 
    129                 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 
    130         if self.edit_inline: 
    131             self.fill_inline_collections() 
    132             for inline_collection in self._inline_collections: 
    133                 if inline_collection.name == key: 
    134                     return inline_collection 
    135         raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key 
    136  
    137     def fill_inline_collections(self): 
    138         if not self._inline_collections: 
    139             ic = [] 
    140             related_objects = self.manipulator.get_related_objects() 
    141             for rel_obj in related_objects: 
    142                 data = rel_obj.extract_data(self.data) 
    143                 inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict) 
    144                 ic.append(inline_collection) 
    145             self._inline_collections = ic 
    146  
    147     def has_errors(self): 
    148         return self.error_dict != {} 
    149  
    150     def _get_fields(self): 
    151         try: 
    152             return self._fields 
    153         except AttributeError: 
    154             self._fields = [self.__getitem__(field.field_name) for field in self.manipulator.fields] 
    155             return self._fields 
    156  
    157     fields = property(_get_fields) 
    158  
    159 class FormFieldWrapper(object): 
    160     "A bridge between the template system and an individual form field. Used by FormWrapper." 
    161     def __init__(self, formfield, data, error_list): 
    162         self.formfield, self.data, self.error_list = formfield, data, error_list 
    163         self.field_name = self.formfield.field_name # for convenience in templates 
    164  
    165     def __str__(self): 
    166         "Renders the field" 
    167         return str(self.formfield.render(self.data)) 
    168  
    169     def __repr__(self): 
    170         return '<FormFieldWrapper for "%s">' % self.formfield.field_name 
    171  
    172     def field_list(self): 
    173         """ 
    174         Like __str__(), but returns a list. Use this when the field's render() 
    175         method returns a list. 
    176         """ 
    177         return self.formfield.render(self.data) 
    178  
    179     def errors(self): 
    180         return self.error_list 
    181  
    182     def html_error_list(self): 
    183         if self.errors(): 
    184             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]) 
    185         else: 
    186             return '' 
    187  
    188     def get_id(self): 
    189         return self.formfield.get_id() 
    190  
    191 class FormFieldCollection(FormFieldWrapper): 
    192     "A utility class that gives the template access to a dict of FormFieldWrappers" 
    193     def __init__(self, formfield_dict): 
    194         self.formfield_dict = formfield_dict 
    195  
    196     def __str__(self): 
    197         return str(self.formfield_dict) 
    198  
    199     def __getitem__(self, template_key): 
    200         "Look up field by template key; raise KeyError on failure" 
    201         return self.formfield_dict[template_key] 
    202  
    203     def __repr__(self): 
    204         return "<FormFieldCollection: %s>" % self.formfield_dict 
    205  
    206     def errors(self): 
    207         "Returns list of all errors in this collection's formfields" 
    208         errors = [] 
    209         for field in self.formfield_dict.values(): 
    210             if hasattr(field, 'errors'): 
    211                 errors.extend(field.errors()) 
    212         return errors 
    213  
    214     def has_errors(self): 
    215         return bool(len(self.errors())) 
    216  
    217     def html_combined_error_list(self): 
    218         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]) 
    219  
    220 class InlineObjectCollection(object): 
    221     "An object that acts like a sparse list of form field collections." 
    222     def __init__(self, parent_manipulator, rel_obj, data, errors): 
    223         self.parent_manipulator = parent_manipulator 
    224         self.rel_obj = rel_obj 
    225         self.data = data 
    226         self.errors = errors 
    227         self._collections = None 
    228         self.name = rel_obj.name 
    229  
    230     def __len__(self): 
    231         self.fill() 
    232         return self._collections.__len__() 
    233  
    234     def __getitem__(self, k): 
    235         self.fill() 
    236         return self._collections.__getitem__(k) 
    237  
    238     def __setitem__(self, k, v): 
    239         self.fill() 
    240         return self._collections.__setitem__(k,v) 
    241  
    242     def __delitem__(self, k): 
    243         self.fill() 
    244         return self._collections.__delitem__(k) 
    245  
    246     def __iter__(self): 
    247         self.fill() 
    248         return iter(self._collections.values()) 
    249  
    250     def items(self): 
    251         self.fill() 
    252         return self._collections.items() 
    253  
    254     def fill(self): 
    255         if self._collections: 
    256             return 
    257         else: 
    258             var_name = self.rel_obj.opts.object_name.lower() 
    259             collections = {} 
    260             orig = None 
    261             if hasattr(self.parent_manipulator, 'original_object'): 
    262                 orig = self.parent_manipulator.original_object 
    263             orig_list = self.rel_obj.get_list(orig) 
    264  
    265             for i, instance in enumerate(orig_list): 
    266                 collection = {'original': instance} 
    267                 for f in self.rel_obj.editable_fields(): 
    268                     for field_name in f.get_manipulator_field_names(''): 
    269                         full_field_name = '%s.%d.%s' % (var_name, i, field_name) 
    270                         field = self.parent_manipulator[full_field_name] 
    271                         data = field.extract_data(self.data) 
    272                         errors = self.errors.get(full_field_name, []) 
    273                         collection[field_name] = FormFieldWrapper(field, data, errors) 
    274                 collections[i] = FormFieldCollection(collection) 
    275             self._collections = collections 
    276  
    277  
    278 class FormField(object): 
    279     """Abstract class representing a form field. 
    280  
    281     Classes that extend FormField should define the following attributes: 
    282         field_name 
    283             The field's name for use by programs. 
    284         validator_list 
    285             A list of validation tests (callback functions) that the data for 
    286             this field must pass in order to be added or changed. 
    287         is_required 
    288             A Boolean. Is it a required field? 
    289     Subclasses should also implement a render(data) method, which is responsible 
    290     for rending the form field in XHTML. 
    291     """ 
    292     def __str__(self): 
    293         return self.render('') 
    294  
    295     def __repr__(self): 
    296         return 'FormField "%s"' % self.field_name 
    297  
    298     def prepare(self, new_data): 
    299         "Hook for doing something to new_data (in place) before validation." 
    300         pass 
    301  
    302     def html2python(data): 
    303         "Hook for converting an HTML datatype (e.g. 'on' for checkboxes) to a Python type" 
    304         return data 
    305     html2python = staticmethod(html2python) 
    306  
    307     def render(self, data): 
    308         raise NotImplementedError 
    309  
    310     def get_member_name(self): 
    311         if hasattr(self, 'member_name'): 
    312             return self.member_name 
    313         else: 
    314             return self.field_name 
    315  
    316     def extract_data(self, data_dict): 
    317         if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 
    318             data = data_dict.getlist(self.get_member_name()) 
    319         else: 
    320             data = data_dict.get(self.get_member_name(), None) 
    321         if data is None: 
    322             data = '' 
    323         return data 
    324  
    325     def convert_post_data(self, new_data): 
    326         name = self.get_member_name() 
    327         if new_data.has_key(self.field_name): 
    328             d = new_data.getlist(self.field_name) 
    329             try: 
    330                 converted_data = [self.__class__.html2python(data) for data in d] 
    331             except ValueError: 
    332                 converted_data = d 
    333             new_data.setlist(name, converted_data) 
    334         else: 
    335             try: 
    336                 #individual fields deal with None values themselves 
    337                 new_data.setlist(name, [self.__class__.html2python(None)]) 
    338             except EmptyValue: 
    339                 new_data.setlist(name, []) 
    340  
    341  
    342     def run_validator(self, new_data, validator): 
    343         if self.is_required or new_data.get(self.field_name, False) or hasattr(validator, 'always_test'): 
    344             if hasattr(self, 'requires_data_list'): 
    345                 validator(new_data.getlist(self.field_name), new_data) 
    346             else: 
    347                 validator(new_data.get(self.field_name, ''), new_data) 
    348  
    349     def get_validation_errors(self, new_data): 
    350         errors = {} 
    351         if self.is_required and not new_data.get(self.field_name, False): 
    352             errors.setdefault(self.field_name, []).append(gettext('This field is required.')) 
    353             return errors 
    354         try: 
    355             for validator in self.validator_list: 
    356                 try: 
    357                     self.run_validator(new_data, validator) 
    358                 except validators.ValidationError, e: 
    359                     errors.setdefault(self.field_name, []).extend(e.messages) 
    360         # If a CriticalValidationError is raised, ignore any other ValidationErrors 
    361         # for this particular field 
    362         except validators.CriticalValidationError, e: 
    363             errors.setdefault(self.field_name, []).extend(e.messages) 
    364         return errors 
    365  
    366     def get_id(self): 
    367         "Returns the HTML 'id' attribute for this form field." 
    368         return FORM_FIELD_ID_PREFIX + self.field_name 
    369  
    370 #################### 
    371 # GENERIC WIDGETS  # 
    372 #################### 
    373  
    374 class TextField(FormField): 
    375     input_type = "text" 
    376     def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, member_name=None): 
    377         if validator_list is None: validator_list = [] 
    378         self.field_name = field_name 
    379         self.length, self.maxlength = length, maxlength 
    380         self.is_required = is_required 
    381         self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list 
    382         if member_name != None: 
    383             self.member_name = member_name 
    384  
    385     def isValidLength(self, data, form): 
    386         if data and self.maxlength and len(data.decode(settings.DEFAULT_CHARSET)) > self.maxlength: 
    387             raise validators.ValidationError, ngettext("Ensure your text is less than %s character.", 
    388                 "Ensure your text is less than %s characters.", self.maxlength) % self.maxlength 
    389  
    390     def hasNoNewlines(self, data, form): 
    391         if data and '\n' in data: 
    392             raise validators.ValidationError, gettext("Line breaks are not allowed here.") 
    393  
    394     def render(self, data): 
    395         if data is None: 
    396             data = '' 
    397         maxlength = '' 
    398         if self.maxlength: 
    399             maxlength = 'maxlength="%s" ' % self.maxlength 
    400         if isinstance(data, unicode): 
    401             data = data.encode(settings.DEFAULT_CHARSET) 
    402         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    403             (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    404             self.field_name, self.length, escape(data), maxlength) 
    405  
    406     def html2python(data): 
    407         return data 
    408     html2python = staticmethod(html2python) 
    409  
    410 class PasswordField(TextField): 
    411     input_type = "password" 
    412  
    413 class LargeTextField(TextField): 
    414     def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, maxlength=None): 
    415         if validator_list is None: validator_list = [] 
    416         self.field_name = field_name 
    417         self.rows, self.cols, self.is_required = rows, cols, is_required 
    418         self.validator_list = validator_list[:] 
    419         if maxlength: 
    420             self.validator_list.append(self.isValidLength) 
    421             self.maxlength = maxlength 
    422  
    423     def render(self, data): 
    424         if data is None: 
    425             data = '' 
    426         if isinstance(data, unicode): 
    427             data = data.encode(settings.DEFAULT_CHARSET) 
    428         return '<textarea id=