Ticket #2926: django_forms_attributes.2.diff

File django_forms_attributes.2.diff, 24.6 KB (added by Winston Lee <lee.winston@…>, 8 years ago)

Patch for 0.95 to include custom attributes. Warning, not properly tested so you'll probably need to submit further patches.

  • __init__.py

     
    280280            this field must pass in order to be added or changed.
    281281        is_required
    282282            A Boolean. Is it a required field?
     283        attribute_dict
     284            A dictionary of property/value attributes for the form field.
    283285    Subclasses should also implement a render(data) method, which is responsible
    284286    for rending the form field in XHTML.
    285287    """
     
    360362    def get_id(self):
    361363        "Returns the HTML 'id' attribute for this form field."
    362364        return FORM_FIELD_ID_PREFIX + self.field_name
     365       
     366    def get_attribute_string(self):
     367        attributes = ''
     368        if self.attribute_dict:
     369            attributes = " ".join(["%s=\"%s\"" % (p, v) for p, v in self.attribute_dict.items()])
     370        return attributes
    363371
    364372####################
    365373# GENERIC WIDGETS  #
     
    367375
    368376class TextField(FormField):
    369377    input_type = "text"
    370     def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, member_name=None):
     378    def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, member_name=None, attribute_dict=None):
    371379        if validator_list is None: validator_list = []
     380        if attribute_dict is None: attribute_dict = {}
    372381        self.field_name = field_name
    373382        self.length, self.maxlength = length, maxlength
    374383        self.is_required = is_required
    375384        self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list
     385        self.attribute_dict = attribute_dict
    376386        if member_name != None:
    377387            self.member_name = member_name
    378388
     
    393403            maxlength = 'maxlength="%s" ' % self.maxlength
    394404        if isinstance(data, unicode):
    395405            data = data.encode(settings.DEFAULT_CHARSET)
    396         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
     406        return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s %s />' % \
    397407            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    398             self.field_name, self.length, escape(data), maxlength)
     408            self.field_name, self.length, escape(data), maxlength, self.get_attribute_string())
    399409
    400410    def html2python(data):
    401411        return data
     
    405415    input_type = "password"
    406416
    407417class LargeTextField(TextField):
    408     def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, maxlength=None):
     418    def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, maxlength=None, attribute_dict=None):
    409419        if validator_list is None: validator_list = []
     420        if attribute_dict is None: attribute_dict = {}
    410421        self.field_name = field_name
    411422        self.rows, self.cols, self.is_required = rows, cols, is_required
    412423        self.validator_list = validator_list[:]
     424        self.attribute_dict = attribute_dict
    413425        if maxlength:
    414426            self.validator_list.append(self.isValidLength)
    415427            self.maxlength = maxlength
     
    419431            data = ''
    420432        if isinstance(data, unicode):
    421433            data = data.encode(settings.DEFAULT_CHARSET)
    422         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
     434        return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s" %s>%s</textarea>' % \
    423435            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    424             self.field_name, self.rows, self.cols, escape(data))
     436            self.field_name, self.rows, self.cols, self.get_attribute_string(), escape(data))
    425437
    426438class HiddenField(FormField):
    427     def __init__(self, field_name, is_required=False, validator_list=None):
     439    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    428440        if validator_list is None: validator_list = []
     441        if attribute_dict is None: attribute_dict = {}
    429442        self.field_name, self.is_required = field_name, is_required
    430443        self.validator_list = validator_list[:]
     444        self.attribute_dict = attribute_dict
    431445
    432446    def render(self, data):
    433         return '<input type="hidden" id="%s" name="%s" value="%s" />' % \
    434             (self.get_id(), self.field_name, escape(data))
     447        return '<input type="hidden" id="%s" name="%s" value="%s" %s />' % \
     448            (self.get_id(), self.field_name, escape(data), self.get_attribute_string())
    435449
    436450class CheckboxField(FormField):
    437     def __init__(self, field_name, checked_by_default=False, validator_list=None):
     451    def __init__(self, field_name, checked_by_default=False, validator_list=None, attribute_dict=None):
    438452        if validator_list is None: validator_list = []
     453        if attribute_dict is None: attribute_dict = {}
    439454        self.field_name = field_name
    440455        self.checked_by_default = checked_by_default
    441456        self.is_required = False # because the validator looks for these
    442457        self.validator_list = validator_list[:]
     458        self.attribute_dict = attribute_dict
    443459
    444460    def render(self, data):
    445461        checked_html = ''
    446462        if data or (data is '' and self.checked_by_default):
    447463            checked_html = ' checked="checked"'
    448         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
     464        return '<input type="checkbox" id="%s" class="v%s" name="%s"%s %s />' % \
    449465            (self.get_id(), self.__class__.__name__,
    450             self.field_name, checked_html)
     466            self.field_name, checked_html, self.get_attribute_string())
    451467
    452468    def html2python(data):
    453469        "Convert value from browser ('on' or '') to a Python boolean"
     
    457473    html2python = staticmethod(html2python)
    458474
    459475class SelectField(FormField):
    460     def __init__(self, field_name, choices=None, size=1, is_required=False, validator_list=None, member_name=None):
     476    def __init__(self, field_name, choices=None, size=1, is_required=False, validator_list=None, member_name=None, attribute_dict=None):
    461477        if validator_list is None: validator_list = []
     478        if attribute_dict is None: attribute_dict = {}
    462479        if choices is None: choices = []
    463480        self.field_name = field_name
    464481        # choices is a list of (value, human-readable key) tuples because order matters
    465482        self.choices, self.size, self.is_required = choices, size, is_required
    466483        self.validator_list = [self.isValidChoice] + validator_list
     484        self.attribute_dict = attribute_dict
    467485        if member_name != None:
    468486            self.member_name = member_name
    469487
    470488    def render(self, data):
    471         output = ['<select id="%s" class="v%s%s" name="%s" size="%s">' % \
     489        output = ['<select id="%s" class="v%s%s" name="%s" size="%s" %s>' % \
    472490            (self.get_id(), self.__class__.__name__,
    473              self.is_required and ' required' or '', self.field_name, self.size)]
     491             self.is_required and ' required' or '', self.field_name, self.size, self.get_attribute_string())]
    474492        str_data = str(data) # normalize to string
    475493        for value, display_name in self.choices:
    476494            selected_html = ''
     
    495513    html2python = staticmethod(html2python)
    496514
    497515class RadioSelectField(FormField):
    498     def __init__(self, field_name, choices=None, ul_class='', is_required=False, validator_list=None, member_name=None):
     516    def __init__(self, field_name, choices=None, ul_class='', is_required=False, validator_list=None, member_name=None, attribute_dict=None):
    499517        if validator_list is None: validator_list = []
     518        if attribute_dict is None: attribute_dict = {}
    500519        if choices is None: choices = []
    501520        self.field_name = field_name
    502521        # choices is a list of (value, human-readable key) tuples because order matters
    503522        self.choices, self.is_required = choices, is_required
    504523        self.validator_list = [self.isValidChoice] + validator_list
    505524        self.ul_class = ul_class
     525        self.attribute_dict = attribute_dict
    506526        if member_name != None:
    507527            self.member_name = member_name
    508528
     
    546566            datalist.append({
    547567                'value': value,
    548568                'name': display_name,
    549                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
    550                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html),
     569                'field': '<input type="radio" id="%s" name="%s" value="%s"%s %s/>' % \
     570                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html, self.get_attribute_string()),
    551571                'label': '<label for="%s">%s</label>' % \
    552572                    (self.get_id() + '_' + str(i), display_name),
    553573            })
     
    561581
    562582class NullBooleanField(SelectField):
    563583    "This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
    564     def __init__(self, field_name, is_required=False, validator_list=None):
     584    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    565585        if validator_list is None: validator_list = []
    566586        SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')],
    567             is_required=is_required, validator_list=validator_list)
     587            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    568588
    569589    def render(self, data):
    570590        if data is None: data = '1'
     
    579599class SelectMultipleField(SelectField):
    580600    requires_data_list = True
    581601    def render(self, data):
    582         output = ['<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \
     602        output = ['<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple" %s>' % \
    583603            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    584             self.field_name, self.size)]
     604            self.field_name, self.size, self.get_attribute_string())]
    585605        str_data_list = map(str, data) # normalize to strings
    586606        for value, choice in self.choices:
    587607            selected_html = ''
     
    615635    back into the single list that validators, renderers and save() expect.
    616636    """
    617637    requires_data_list = True
    618     def __init__(self, field_name, choices=None, ul_class='', validator_list=None):
     638    def __init__(self, field_name, choices=None, ul_class='', validator_list=None, attribute_dict=None):
    619639        if validator_list is None: validator_list = []
    620640        if choices is None: choices = []
    621641        self.ul_class = ul_class
    622         SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
     642        SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list, attribute_dict=attribute_dict)
    623643
    624644    def prepare(self, new_data):
    625645        # new_data has "split" this field into several fields, so flatten it
     
    638658            if str(value) in str_data_list:
    639659                checked_html = ' checked="checked"'
    640660            field_name = '%s%s' % (self.field_name, value)
    641             output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
    642                 (self.get_id() + value , self.__class__.__name__, field_name, checked_html,
    643                 self.get_id() + value, choice))
     661            output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s %s /> <label for="%s">%s</label></li>' % \
     662                (self.get_id() + value , self.__class__.__name__, field_name, checked_html, 
     663                self.get_attribute_string(), self.get_id() + value, choice))
    644664        output.append('</ul>')
    645665        return '\n'.join(output)
    646666
     
    649669####################
    650670
    651671class FileUploadField(FormField):
    652     def __init__(self, field_name, is_required=False, validator_list=None):
     672    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    653673        if validator_list is None: validator_list = []
     674        if attribute_dict is None: attribute_dict = {}
    654675        self.field_name, self.is_required = field_name, is_required
     676        self.attribute_dict = attribute_dict
    655677        self.validator_list = [self.isNonEmptyFile] + validator_list
    656678
    657679    def isNonEmptyFile(self, field_data, all_data):
     
    663685            raise validators.CriticalValidationError, gettext("The submitted file is empty.")
    664686
    665687    def render(self, data):
    666         return '<input type="file" id="%s" class="v%s" name="%s" />' % \
    667             (self.get_id(), self.__class__.__name__, self.field_name)
     688        return '<input type="file" id="%s" class="v%s" name="%s" %s />' % \
     689            (self.get_id(), self.__class__.__name__, self.field_name, self.get_attribute_string())
    668690
    669691    def html2python(data):
    670692        if data is None:
     
    689711####################
    690712
    691713class IntegerField(TextField):
    692     def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None, member_name=None):
     714    def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None, member_name=None, attribute_dict=None):
    693715        if validator_list is None: validator_list = []
    694716        validator_list = [self.isInteger] + validator_list
    695717        if member_name is not None:
    696718            self.member_name = member_name
    697         TextField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     719        TextField.__init__(self, field_name, length, maxlength, is_required, validator_list, attribute_dict=attribute_dict)
    698720
    699721    def isInteger(self, field_data, all_data):
    700722        try:
     
    709731    html2python = staticmethod(html2python)
    710732
    711733class SmallIntegerField(IntegerField):
    712     def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=None):
     734    def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=None, attribute_dict=None):
    713735        if validator_list is None: validator_list = []
    714736        validator_list = [self.isSmallInteger] + validator_list
    715         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     737        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, attribute_dict=attribute_dict)
    716738
    717739    def isSmallInteger(self, field_data, all_data):
    718740        if not -32768 <= int(field_data) <= 32767:
    719741            raise validators.CriticalValidationError, gettext("Enter a whole number between -32,768 and 32,767.")
    720742
    721743class PositiveIntegerField(IntegerField):
    722     def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None):
     744    def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None, attribute_dict=None):
    723745        if validator_list is None: validator_list = []
    724746        validator_list = [self.isPositive] + validator_list
    725         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     747        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, attribute_dict=attribute_dict)
    726748
    727749    def isPositive(self, field_data, all_data):
    728750        if int(field_data) < 0:
    729751            raise validators.CriticalValidationError, gettext("Enter a positive number.")
    730752
    731753class PositiveSmallIntegerField(IntegerField):
    732     def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=None):
     754    def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=None, attribute_dict=None):
    733755        if validator_list is None: validator_list = []
    734756        validator_list = [self.isPositiveSmall] + validator_list
    735         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     757        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, attribute_dict=attribute_dict)
    736758
    737759    def isPositiveSmall(self, field_data, all_data):
    738760        if not 0 <= int(field_data) <= 32767:
    739761            raise validators.CriticalValidationError, gettext("Enter a whole number between 0 and 32,767.")
    740762
    741763class FloatField(TextField):
    742     def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None):
     764    def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None, attribute_dict=None):
    743765        if validator_list is None: validator_list = []
    744766        self.max_digits, self.decimal_places = max_digits, decimal_places
    745767        validator_list = [self.isValidFloat] + validator_list
    746         TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
     768        TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list, attribute_dict=attribute_dict)
    747769
    748770    def isValidFloat(self, field_data, all_data):
    749771        v = validators.IsValidFloat(self.max_digits, self.decimal_places)
     
    765787class DatetimeField(TextField):
    766788    """A FormField that automatically converts its data to a datetime.datetime object.
    767789    The data should be in the format YYYY-MM-DD HH:MM:SS."""
    768     def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None):
     790    def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, attribute_dict=None):
    769791        if validator_list is None: validator_list = []
     792        if attribute_dict is None: attribute_dict = {}
    770793        self.field_name = field_name
    771794        self.length, self.maxlength = length, maxlength
    772795        self.is_required = is_required
    773796        self.validator_list = [validators.isValidANSIDatetime] + validator_list
     797        self.attribute_dict = attribute_dict
    774798
    775799    def html2python(data):
    776800        "Converts the field into a datetime.datetime object"
     
    792816class DateField(TextField):
    793817    """A FormField that automatically converts its data to a datetime.date object.
    794818    The data should be in the format YYYY-MM-DD."""
    795     def __init__(self, field_name, is_required=False, validator_list=None):
     819    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    796820        if validator_list is None: validator_list = []
    797821        validator_list = [self.isValidDate] + validator_list
    798822        TextField.__init__(self, field_name, length=10, maxlength=10,
    799             is_required=is_required, validator_list=validator_list)
     823            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    800824
    801825    def isValidDate(self, field_data, all_data):
    802826        try:
     
    817841class TimeField(TextField):
    818842    """A FormField that automatically converts its data to a datetime.time object.
    819843    The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
    820     def __init__(self, field_name, is_required=False, validator_list=None):
     844    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    821845        if validator_list is None: validator_list = []
    822846        validator_list = [self.isValidTime] + validator_list
    823847        TextField.__init__(self, field_name, length=8, maxlength=8,
    824             is_required=is_required, validator_list=validator_list)
     848            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    825849
    826850    def isValidTime(self, field_data, all_data):
    827851        try:
     
    852876
    853877class EmailField(TextField):
    854878    "A convenience FormField for validating e-mail addresses"
    855     def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=None):
     879    def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=None, attribute_dict=None):
    856880        if validator_list is None: validator_list = []
    857881        validator_list = [self.isValidEmail] + validator_list
    858882        TextField.__init__(self, field_name, length, maxlength=maxlength,
    859             is_required=is_required, validator_list=validator_list)
     883            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    860884
    861885    def isValidEmail(self, field_data, all_data):
    862886        try:
     
    866890
    867891class URLField(TextField):
    868892    "A convenience FormField for validating URLs"
    869     def __init__(self, field_name, length=50, maxlength=200, is_required=False, validator_list=None):
     893    def __init__(self, field_name, length=50, maxlength=200, is_required=False, validator_list=None, attribute_dict=None):
    870894        if validator_list is None: validator_list = []
    871895        validator_list = [self.isValidURL] + validator_list
    872896        TextField.__init__(self, field_name, length=length, maxlength=maxlength,
    873             is_required=is_required, validator_list=validator_list)
     897            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    874898
    875899    def isValidURL(self, field_data, all_data):
    876900        try:
     
    879903            raise validators.CriticalValidationError, e.messages
    880904
    881905class IPAddressField(TextField):
    882     def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=None):
     906    def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=None, attribute_dict=None):
    883907        if validator_list is None: validator_list = []
    884908        validator_list = [self.isValidIPAddress] + validator_list
    885909        TextField.__init__(self, field_name, length=length, maxlength=maxlength,
    886             is_required=is_required, validator_list=validator_list)
     910            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    887911
    888912    def isValidIPAddress(self, field_data, all_data):
    889913        try:
     
    901925
    902926class FilePathField(SelectField):
    903927    "A SelectField whose choices are the files in a given directory."
    904     def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None):
     928    def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None, attribute_dict=None):
    905929        import os
    906930        from django.db.models import BLANK_CHOICE_DASH
    907931        if match is not None:
     
    921945                        choices.append((full_file, f))
    922946            except OSError:
    923947                pass
    924         SelectField.__init__(self, field_name, choices, 1, is_required, validator_list)
     948        SelectField.__init__(self, field_name, choices, 1, is_required, validator_list, attribute_dict=attribute_dict)
    925949
    926950class PhoneNumberField(TextField):
    927951    "A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
    928     def __init__(self, field_name, is_required=False, validator_list=None):
     952    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    929953        if validator_list is None: validator_list = []
    930954        validator_list = [self.isValidPhone] + validator_list
    931955        TextField.__init__(self, field_name, length=12, maxlength=12,
    932             is_required=is_required, validator_list=validator_list)
     956            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    933957
    934958    def isValidPhone(self, field_data, all_data):
    935959        try:
     
    939963
    940964class USStateField(TextField):
    941965    "A convenience FormField for validating U.S. states (e.g. 'IL')"
    942     def __init__(self, field_name, is_required=False, validator_list=None):
     966    def __init__(self, field_name, is_required=False, validator_list=None, attribute_dict=None):
    943967        if validator_list is None: validator_list = []
    944968        validator_list = [self.isValidUSState] + validator_list
    945969        TextField.__init__(self, field_name, length=2, maxlength=2,
    946             is_required=is_required, validator_list=validator_list)
     970            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    947971
    948972    def isValidUSState(self, field_data, all_data):
    949973        try:
     
    960984
    961985class CommaSeparatedIntegerField(TextField):
    962986    "A convenience FormField for validating comma-separated integer fields"
    963     def __init__(self, field_name, maxlength=None, is_required=False, validator_list=None):
     987    def __init__(self, field_name, maxlength=None, is_required=False, validator_list=None, attribute_dict=None):
    964988        if validator_list is None: validator_list = []
    965989        validator_list = [self.isCommaSeparatedIntegerList] + validator_list
    966990        TextField.__init__(self, field_name, length=20, maxlength=maxlength,
    967             is_required=is_required, validator_list=validator_list)
     991            is_required=is_required, validator_list=validator_list, attribute_dict=attribute_dict)
    968992
    969993    def isCommaSeparatedIntegerList(self, field_data, all_data):
    970994        try:
Back to Top