Ticket #1714: readonly-fields.diff

File readonly-fields.diff, 20.4 KB (added by mir@…, 9 years ago)

dummdidumm, here it comes ;-)

  • django/forms/__init__.py

    a b class FormField: 
    361361        "Returns the HTML 'id' attribute for this form field."
    362362        return FORM_FIELD_ID_PREFIX + self.field_name
    363363
     364    def css_class(self):
     365        readonly_label = ''
     366        if self.readonly:
     367            readonly_label = "Readonly"
     368        return "v%s%s" % (readonly_label, self.__class__.__name__)
     369
    364370####################
    365371# GENERIC WIDGETS  #
    366372####################
    367373
    368374class TextField(FormField):
    369375    input_type = "text"
    370     def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[], member_name=None):
     376    def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[], member_name=None, readonly=False):
    371377        self.field_name = field_name
    372378        self.length, self.maxlength = length, maxlength
    373379        self.is_required = is_required
    374380        self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list
    375381        if member_name != None:
    376382            self.member_name = member_name
     383        self.readonly = readonly
    377384
    378385    def isValidLength(self, data, form):
    379386        if data and self.maxlength and len(data.decode(settings.DEFAULT_CHARSET)) > self.maxlength:
    class TextField(FormField): 
    388395        if data is None:
    389396            data = ''
    390397        maxlength = ''
     398        readonly = ''
    391399        if self.maxlength:
    392400            maxlength = 'maxlength="%s" ' % self.maxlength
     401        if self.readonly:
     402            readonly = 'readonly="readonly"'
    393403        if isinstance(data, unicode):
    394404            data = data.encode(settings.DEFAULT_CHARSET)
    395         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
    396             (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    397             self.field_name, self.length, escape(data), maxlength)
     405        return '<input type="%s" id="%s" class="%s%s" name="%s" size="%s" value="%s" %s %s/>' % \
     406            (self.input_type, self.get_id(), self.css_class(), self.is_required and ' required' or '',
     407            self.field_name, self.length, escape(data), maxlength, readonly)
    398408
    399409    def html2python(data):
    400410        return data
    class PasswordField(TextField): 
    404414    input_type = "password"
    405415
    406416class LargeTextField(TextField):
    407     def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=[], maxlength=None):
     417    def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=[], maxlength=None, readonly=False):
    408418        self.field_name = field_name
    409419        self.rows, self.cols, self.is_required = rows, cols, is_required
    410420        self.validator_list = validator_list[:]
    411421        if maxlength:
    412422            self.validator_list.append(self.isValidLength)
    413423            self.maxlength = maxlength
     424        self.readonly = readonly
    414425
    415426    def render(self, data):
    416427        if data is None:
    417428            data = ''
    418429        if isinstance(data, unicode):
    419430            data = data.encode(settings.DEFAULT_CHARSET)
    420         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
    421             (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    422             self.field_name, self.rows, self.cols, escape(data))
     431        readonly = ''
     432        if self.readonly:
     433            readonly = 'readonly="readonly"'
     434        return '<textarea id="%s" class="%s%s" name="%s" rows="%s" cols="%s" %s>%s</textarea>' % \
     435            (self.get_id(), self.css_class(), self.is_required and ' required' or '',
     436            self.field_name, self.rows, self.cols, readonly, escape(data))
    423437
    424438class HiddenField(FormField):
    425439    def __init__(self, field_name, is_required=False, validator_list=[]):
    class HiddenField(FormField): 
    431445            (self.get_id(), self.field_name, escape(data))
    432446
    433447class CheckboxField(FormField):
    434     def __init__(self, field_name, checked_by_default=False):
     448    def __init__(self, field_name, checked_by_default=False, readonly=False):
    435449        self.field_name = field_name
    436450        self.checked_by_default = checked_by_default
    437451        self.is_required, self.validator_list = False, [] # because the validator looks for these
     452        self.readonly = readonly
    438453
    439454    def render(self, data):
    440455        checked_html = ''
     456        readonly = ''
     457        if self.readonly:
     458            readonly = ' disabled="disabled"'
    441459        if data or (data is '' and self.checked_by_default):
    442460            checked_html = ' checked="checked"'
    443         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
    444             (self.get_id(), self.__class__.__name__,
    445             self.field_name, checked_html)
     461        return '<input type="checkbox" id="%s" class="%s" name="%s"%s%s/>' % \
     462            (self.get_id(), self.css_class(),
     463            self.field_name, checked_html, readonly)
    446464
    447465    def html2python(data):
    448466        "Convert value from browser ('on' or '') to a Python boolean"
    class CheckboxField(FormField): 
    452470    html2python = staticmethod(html2python)
    453471
    454472class SelectField(FormField):
    455     def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None):
     473    def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None, readonly=False):
    456474        self.field_name = field_name
    457475        # choices is a list of (value, human-readable key) tuples because order matters
    458476        self.choices, self.size, self.is_required = choices, size, is_required
    459477        self.validator_list = [self.isValidChoice] + validator_list
     478        self.readonly = readonly
    460479        if member_name != None:
    461480            self.member_name = member_name
    462481
    463482    def render(self, data):
    464         output = ['<select id="%s" class="v%s%s" name="%s" size="%s">' % \
    465             (self.get_id(), self.__class__.__name__,
    466              self.is_required and ' required' or '', self.field_name, self.size)]
     483        disabled = ''
     484        if self.readonly:
     485            disabled='disabled="disabled"'
     486        output = ['<select id="%s" class="%s%s" name="%s" size="%s" %s>' % \
     487            (self.get_id(), self.css_class(),
     488             self.is_required and ' required' or '', self.field_name, self.size,
     489             disabled)]
    467490        str_data = str(data) # normalize to string
    468491        for value, display_name in self.choices:
    469492            selected_html = ''
    class NullSelectField(SelectField): 
    488511    html2python = staticmethod(html2python)
    489512
    490513class RadioSelectField(FormField):
    491     def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None):
     514    def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None, readonly=False):
    492515        self.field_name = field_name
    493516        # choices is a list of (value, human-readable key) tuples because order matters
    494517        self.choices, self.is_required = choices, is_required
    495518        self.validator_list = [self.isValidChoice] + validator_list
    496519        self.ul_class = ul_class
     520        self.readonly = readonly
    497521        if member_name != None:
    498522            self.member_name = member_name
    499523
    class RadioSelectField(FormField): 
    532556        str_data = str(data) # normalize to string
    533557        for i, (value, display_name) in enumerate(self.choices):
    534558            selected_html = ''
     559            readonly = ''
    535560            if str(value) == str_data:
    536561                selected_html = ' checked="checked"'
     562            if self.readonly:
     563                readonly = 'readonly="readonly"'
    537564            datalist.append({
    538565                'value': value,
    539566                'name': display_name,
    540                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
    541                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html),
     567                'field': '<input type="radio" id="%s" name="%s" value="%s"%s %s/>' % \
     568                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html, readonly),
    542569                'label': '<label for="%s">%s</label>' % \
    543570                    (self.get_id() + '_' + str(i), display_name),
    544571            })
    class RadioSelectField(FormField): 
    552579
    553580class NullBooleanField(SelectField):
    554581    "This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
    555     def __init__(self, field_name, is_required=False, validator_list=[]):
     582    def __init__(self, field_name, is_required=False, validator_list=[], readonly=False):
    556583        SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')],
    557             is_required=is_required, validator_list=validator_list)
     584            is_required=is_required, validator_list=validator_list, readonly=readonly)
    558585
    559586    def render(self, data):
    560587        if data is None: data = '1'
    class CheckboxSelectMultipleField(Select 
    605632    back into the single list that validators, renderers and save() expect.
    606633    """
    607634    requires_data_list = True
    608     def __init__(self, field_name, choices=[], validator_list=[]):
    609         SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
     635    def __init__(self, field_name, choices=[], validator_list=[], readonly=False):
     636        SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list, readonly=readonly)
    610637
    611638    def prepare(self, new_data):
    612639        # new_data has "split" this field into several fields, so flatten it
    # INTEGERS/FLOATS # 
    671698####################
    672699
    673700class IntegerField(TextField):
    674     def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[], member_name=None):
     701    def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[], member_name=None, readonly=False):
    675702        validator_list = [self.isInteger] + validator_list
    676703        if member_name is not None:
    677704            self.member_name = member_name
    678         TextField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     705        TextField.__init__(self, field_name, length, maxlength, is_required, validator_list, readonly)
    679706
    680707    def isInteger(self, field_data, all_data):
    681708        try:
    class IntegerField(TextField): 
    690717    html2python = staticmethod(html2python)
    691718
    692719class SmallIntegerField(IntegerField):
    693     def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=[]):
     720    def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=[], readonly=False):
    694721        validator_list = [self.isSmallInteger] + validator_list
    695         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     722        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, readonly)
    696723
    697724    def isSmallInteger(self, field_data, all_data):
    698725        if not -32768 <= int(field_data) <= 32767:
    699726            raise validators.CriticalValidationError, gettext("Enter a whole number between -32,768 and 32,767.")
    700727
    701728class PositiveIntegerField(IntegerField):
    702     def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[]):
     729    def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[], readonly=False):
    703730        validator_list = [self.isPositive] + validator_list
    704         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     731        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, readonly)
    705732
    706733    def isPositive(self, field_data, all_data):
    707734        if int(field_data) < 0:
    708735            raise validators.CriticalValidationError, gettext("Enter a positive number.")
    709736
    710737class PositiveSmallIntegerField(IntegerField):
    711     def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=[]):
     738    def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=[], readonly=False):
    712739        validator_list = [self.isPositiveSmall] + validator_list
    713         IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
     740        IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list, readonly)
    714741
    715742    def isPositiveSmall(self, field_data, all_data):
    716743        if not 0 <= int(field_data) <= 32767:
    717744            raise validators.CriticalValidationError, gettext("Enter a whole number between 0 and 32,767.")
    718745
    719746class FloatField(TextField):
    720     def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=[]):
     747    def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=[], readonly=False):
    721748        self.max_digits, self.decimal_places = max_digits, decimal_places
    722749        validator_list = [self.isValidFloat] + validator_list
    723         TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
     750        TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list, readonly)
    724751
    725752    def isValidFloat(self, field_data, all_data):
    726753        v = validators.IsValidFloat(self.max_digits, self.decimal_places)
    #################### 
    742769class DatetimeField(TextField):
    743770    """A FormField that automatically converts its data to a datetime.datetime object.
    744771    The data should be in the format YYYY-MM-DD HH:MM:SS."""
    745     def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[]):
     772    def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[], readonly=False):
    746773        self.field_name = field_name
    747774        self.length, self.maxlength = length, maxlength
    748775        self.is_required = is_required
    749776        self.validator_list = [validators.isValidANSIDatetime] + validator_list
     777        self.readonly = readonly
    750778
    751779    def html2python(data):
    752780        "Converts the field into a datetime.datetime object"
    class DatetimeField(TextField): 
    765793class DateField(TextField):
    766794    """A FormField that automatically converts its data to a datetime.date object.
    767795    The data should be in the format YYYY-MM-DD."""
    768     def __init__(self, field_name, is_required=False, validator_list=[]):
     796    def __init__(self, field_name, is_required=False, validator_list=[], readonly=False):
    769797        validator_list = [self.isValidDate] + validator_list
    770798        TextField.__init__(self, field_name, length=10, maxlength=10,
    771             is_required=is_required, validator_list=validator_list)
     799            is_required=is_required, validator_list=validator_list, readonly=readonly)
    772800
    773801    def isValidDate(self, field_data, all_data):
    774802        try:
    class DateField(TextField): 
    789817class TimeField(TextField):
    790818    """A FormField that automatically converts its data to a datetime.time object.
    791819    The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
    792     def __init__(self, field_name, is_required=False, validator_list=[]):
     820    def __init__(self, field_name, is_required=False, validator_list=[], readonly=False):
    793821        validator_list = [self.isValidTime] + validator_list
    794822        TextField.__init__(self, field_name, length=8, maxlength=8,
    795             is_required=is_required, validator_list=validator_list)
     823            is_required=is_required, validator_list=validator_list, readonly=readonly)
    796824
    797825    def isValidTime(self, field_data, all_data):
    798826        try:
    #################### 
    823851
    824852class EmailField(TextField):
    825853    "A convenience FormField for validating e-mail addresses"
    826     def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=[]):
     854    def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=[], readonly=False):
    827855        validator_list = [self.isValidEmail] + validator_list
    828856        TextField.__init__(self, field_name, length, maxlength=maxlength,
    829             is_required=is_required, validator_list=validator_list)
     857            is_required=is_required, validator_list=validator_list, readonly=readonly)
    830858
    831859    def isValidEmail(self, field_data, all_data):
    832860        try:
    class EmailField(TextField): 
    836864
    837865class URLField(TextField):
    838866    "A convenience FormField for validating URLs"
    839     def __init__(self, field_name, length=50, is_required=False, validator_list=[]):
     867    def __init__(self, field_name, length=50, is_required=False, validator_list=[], readonly=False):
    840868        validator_list = [self.isValidURL] + validator_list
    841869        TextField.__init__(self, field_name, length=length, maxlength=200,
    842             is_required=is_required, validator_list=validator_list)
     870            is_required=is_required, validator_list=validator_list, readonly=readonly)
    843871
    844872    def isValidURL(self, field_data, all_data):
    845873        try:
    class URLField(TextField): 
    848876            raise validators.CriticalValidationError, e.messages
    849877
    850878class IPAddressField(TextField):
    851     def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=[]):
     879    def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=[], readonly=False):
    852880        validator_list = [self.isValidIPAddress] + validator_list
    853881        TextField.__init__(self, field_name, length=length, maxlength=maxlength,
    854             is_required=is_required, validator_list=validator_list)
     882            is_required=is_required, validator_list=validator_list, readonly=readonly)
    855883
    856884    def isValidIPAddress(self, field_data, all_data):
    857885        try:
    #################### 
    869897
    870898class FilePathField(SelectField):
    871899    "A SelectField whose choices are the files in a given directory."
    872     def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=[]):
     900    def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=[], readonly=False):
    873901        import os
    874902        if match is not None:
    875903            import re
    class FilePathField(SelectField): 
    888916                        choices.append((full_file, f))
    889917            except OSError:
    890918                pass
    891         SelectField.__init__(self, field_name, choices, 1, is_required, validator_list)
     919        SelectField.__init__(self, field_name, choices, 1, is_required, validator_list, readonly)
    892920
    893921class PhoneNumberField(TextField):
    894922    "A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
    895     def __init__(self, field_name, is_required=False, validator_list=[]):
     923    def __init__(self, field_name, is_required=False, validator_list=[], readonly=False):
    896924        validator_list = [self.isValidPhone] + validator_list
    897925        TextField.__init__(self, field_name, length=12, maxlength=12,
    898             is_required=is_required, validator_list=validator_list)
     926            is_required=is_required, validator_list=validator_list, readonly=readonly)
    899927
    900928    def isValidPhone(self, field_data, all_data):
    901929        try:
    class PhoneNumberField(TextField): 
    905933
    906934class USStateField(TextField):
    907935    "A convenience FormField for validating U.S. states (e.g. 'IL')"
    908     def __init__(self, field_name, is_required=False, validator_list=[]):
     936    def __init__(self, field_name, is_required=False, validator_list=[], readonly=False):
    909937        validator_list = [self.isValidUSState] + validator_list
    910938        TextField.__init__(self, field_name, length=2, maxlength=2,
    911             is_required=is_required, validator_list=validator_list)
     939            is_required=is_required, validator_list=validator_list, readonly=readonly)
    912940
    913941    def isValidUSState(self, field_data, all_data):
    914942        try:
    class USStateField(TextField): 
    925953
    926954class CommaSeparatedIntegerField(TextField):
    927955    "A convenience FormField for validating comma-separated integer fields"
    928     def __init__(self, field_name, maxlength=None, is_required=False, validator_list=[]):
     956    def __init__(self, field_name, maxlength=None, is_required=False, validator_list=[], readonly=False):
    929957        validator_list = [self.isCommaSeparatedIntegerList] + validator_list
    930958        TextField.__init__(self, field_name, length=20, maxlength=maxlength,
    931             is_required=is_required, validator_list=validator_list)
     959            is_required=is_required, validator_list=validator_list, readonly=readonly)
    932960
    933961    def isCommaSeparatedIntegerList(self, field_data, all_data):
    934962        try:
Back to Top