[documentos]classField(object):widget=TextInput# Default widget to use when rendering this type of Field.hidden_widget=HiddenInput# Default widget to use when rendering this as "hidden".default_validators=[]# Default set of validators# Add an 'invalid' entry to default_error_message if you want a specific# field error message not raised by the field validators.default_error_messages={'required':_('This field is required.'),}empty_values=list(validators.EMPTY_VALUES)# Tracks each time a Field instance is created. Used to retain order.creation_counter=0def__init__(self,required=True,widget=None,label=None,initial=None,help_text='',error_messages=None,show_hidden_initial=False,validators=[],localize=False,disabled=False,label_suffix=None):# required -- Boolean that specifies whether the field is required.# True by default.# widget -- A Widget class, or instance of a Widget class, that should# be used for this Field when displaying it. Each Field has a# default Widget that it'll use if you don't specify this. In# most cases, the default widget is TextInput.# label -- A verbose name for this field, for use in displaying this# field in a form. By default, Django will use a "pretty"# version of the form field name, if the Field is part of a# Form.# initial -- A value to use in this Field's initial display. This value# is *not* used as a fallback if data isn't given.# help_text -- An optional string to use as "help text" for this Field.# error_messages -- An optional dictionary to override the default# messages that the field will raise.# show_hidden_initial -- Boolean that specifies if it is needed to render a# hidden widget with initial value after widget.# validators -- List of additional validators to use# localize -- Boolean that specifies if the field should be localized.# disabled -- Boolean that specifies whether the field is disabled, that# is its widget is shown in the form but not editable.# label_suffix -- Suffix to be added to the label. Overrides# form's label_suffix.self.required,self.label,self.initial=required,label,initialself.show_hidden_initial=show_hidden_initialself.help_text=help_textself.disabled=disabledself.label_suffix=label_suffixwidget=widgetorself.widgetifisinstance(widget,type):widget=widget()# Trigger the localization machinery if needed.self.localize=localizeifself.localize:widget.is_localized=True# Let the widget know whether it should display as required.widget.is_required=self.required# Hook into self.widget_attrs() for any Field-specific HTML attributes.extra_attrs=self.widget_attrs(widget)ifextra_attrs:widget.attrs.update(extra_attrs)self.widget=widget# Increase the creation counter, and save our local copy.self.creation_counter=Field.creation_counterField.creation_counter+=1messages={}forcinreversed(self.__class__.__mro__):messages.update(getattr(c,'default_error_messages',{}))messages.update(error_messagesor{})self.error_messages=messagesself.validators=list(itertools.chain(self.default_validators,validators))super(Field,self).__init__()defprepare_value(self,value):returnvaluedefto_python(self,value):returnvaluedefvalidate(self,value):ifvalueinself.empty_valuesandself.required:raiseValidationError(self.error_messages['required'],code='required')defrun_validators(self,value):ifvalueinself.empty_values:returnerrors=[]forvinself.validators:try:v(value)exceptValidationErrorase:ifhasattr(e,'code')ande.codeinself.error_messages:e.message=self.error_messages[e.code]errors.extend(e.error_list)iferrors:raiseValidationError(errors)

[documentos]defclean(self,value):""" Validates the given value and returns its "cleaned" value as an appropriate Python object. Raises ValidationError for any errors. """value=self.to_python(value)self.validate(value)self.run_validators(value)returnvalue

defbound_data(self,data,initial):""" Return the value that should be shown for this field on render of a bound form, given the submitted POST data for the field and the initial data, if any. For most fields, this will simply be data; FileFields need to handle it a bit differently. """ifself.disabled:returninitialreturndatadefwidget_attrs(self,widget):""" Given a Widget instance (*not* a Widget class), returns a dictionary of any HTML attributes that should be added to the Widget, based on this Field. """return{}

[documentos]defhas_changed(self,initial,data):""" Return True if data differs from initial. """try:data=self.to_python(data)ifhasattr(self,'_coerce'):returnself._coerce(data)!=self._coerce(initial)exceptValidationError:returnTrue# For purposes of seeing whether something has changed, None is# the same as an empty string, if the data or initial value we get# is None, replace it with ''.initial_value=initialifinitialisnotNoneelse''data_value=dataifdataisnotNoneelse''returninitial_value!=data_value

[documentos]defget_bound_field(self,form,field_name):""" Return a BoundField instance that will be used when accessing the form field in a template. """returnBoundField(form,self,field_name)

[documentos]classCharField(Field):def__init__(self,max_length=None,min_length=None,strip=True,*args,**kwargs):self.max_length=max_lengthself.min_length=min_lengthself.strip=stripsuper(CharField,self).__init__(*args,**kwargs)ifmin_lengthisnotNone:self.validators.append(validators.MinLengthValidator(int(min_length)))ifmax_lengthisnotNone:self.validators.append(validators.MaxLengthValidator(int(max_length)))defto_python(self,value):"Returns a Unicode object."ifvalueinself.empty_values:return''value=force_text(value)ifself.strip:value=value.strip()returnvaluedefwidget_attrs(self,widget):attrs=super(CharField,self).widget_attrs(widget)ifself.max_lengthisnotNone:# The HTML attribute is maxlength, not max_length.attrs['maxlength']=str(self.max_length)ifself.min_lengthisnotNone:# The HTML attribute is minlength, not min_length.attrs['minlength']=str(self.min_length)returnattrs

[documentos]classIntegerField(Field):widget=NumberInputdefault_error_messages={'invalid':_('Enter a whole number.'),}re_decimal=re.compile(r'\.0*\s*$')def__init__(self,max_value=None,min_value=None,*args,**kwargs):self.max_value,self.min_value=max_value,min_valueifkwargs.get('localize')andself.widget==NumberInput:# Localized number input is not well supported on most browserskwargs.setdefault('widget',super(IntegerField,self).widget)super(IntegerField,self).__init__(*args,**kwargs)ifmax_valueisnotNone:self.validators.append(validators.MaxValueValidator(max_value))ifmin_valueisnotNone:self.validators.append(validators.MinValueValidator(min_value))defto_python(self,value):""" Validates that int() can be called on the input. Returns the result of int(). Returns None for empty values. """value=super(IntegerField,self).to_python(value)ifvalueinself.empty_values:returnNoneifself.localize:value=formats.sanitize_separators(value)# Strip trailing decimal and zeros.try:value=int(self.re_decimal.sub('',force_text(value)))except(ValueError,TypeError):raiseValidationError(self.error_messages['invalid'],code='invalid')returnvaluedefwidget_attrs(self,widget):attrs=super(IntegerField,self).widget_attrs(widget)ifisinstance(widget,NumberInput):ifself.min_valueisnotNone:attrs['min']=self.min_valueifself.max_valueisnotNone:attrs['max']=self.max_valuereturnattrs

[documentos]classFloatField(IntegerField):default_error_messages={'invalid':_('Enter a number.'),}defto_python(self,value):""" Validates that float() can be called on the input. Returns the result of float(). Returns None for empty values. """value=super(IntegerField,self).to_python(value)ifvalueinself.empty_values:returnNoneifself.localize:value=formats.sanitize_separators(value)try:value=float(value)except(ValueError,TypeError):raiseValidationError(self.error_messages['invalid'],code='invalid')returnvaluedefvalidate(self,value):super(FloatField,self).validate(value)# Check for NaN (which is the only thing not equal to itself) and +/- infinityifvalue!=valueorvaluein(Decimal('Inf'),Decimal('-Inf')):raiseValidationError(self.error_messages['invalid'],code='invalid')returnvaluedefwidget_attrs(self,widget):attrs=super(FloatField,self).widget_attrs(widget)ifisinstance(widget,NumberInput)and'step'notinwidget.attrs:attrs.setdefault('step','any')returnattrs

[documentos]classDecimalField(IntegerField):default_error_messages={'invalid':_('Enter a number.'),}def__init__(self,max_value=None,min_value=None,max_digits=None,decimal_places=None,*args,**kwargs):self.max_digits,self.decimal_places=max_digits,decimal_placessuper(DecimalField,self).__init__(max_value,min_value,*args,**kwargs)self.validators.append(validators.DecimalValidator(max_digits,decimal_places))defto_python(self,value):""" Validates that the input is a decimal number. Returns a Decimal instance. Returns None for empty values. Ensures that there are no more than max_digits in the number, and no more than decimal_places digits after the decimal point. """ifvalueinself.empty_values:returnNoneifself.localize:value=formats.sanitize_separators(value)value=smart_text(value).strip()try:value=Decimal(value)exceptDecimalException:raiseValidationError(self.error_messages['invalid'],code='invalid')returnvaluedefvalidate(self,value):super(DecimalField,self).validate(value)ifvalueinself.empty_values:return# Check for NaN, Inf and -Inf values. We can't compare directly for NaN,# since it is never equal to itself. However, NaN is the only value that# isn't equal to itself, so we can use this to identify NaNifvalue!=valueorvalue==Decimal("Inf")orvalue==Decimal("-Inf"):raiseValidationError(self.error_messages['invalid'],code='invalid')defwidget_attrs(self,widget):attrs=super(DecimalField,self).widget_attrs(widget)ifisinstance(widget,NumberInput)and'step'notinwidget.attrs:ifself.decimal_placesisnotNone:# Use exponential notation for small values since they might# be parsed as 0 otherwise. ref #20765step=str(Decimal('1')/10**self.decimal_places).lower()else:step='any'attrs.setdefault('step',step)returnattrs

classBaseTemporalField(Field):def__init__(self,input_formats=None,*args,**kwargs):super(BaseTemporalField,self).__init__(*args,**kwargs)ifinput_formatsisnotNone:self.input_formats=input_formatsdefto_python(self,value):# Try to coerce the value to unicode.unicode_value=force_text(value,strings_only=True)ifisinstance(unicode_value,six.text_type):value=unicode_value.strip()# If unicode, try to strptime against each input format.ifisinstance(value,six.text_type):forformatinself.input_formats:try:returnself.strptime(value,format)except(ValueError,TypeError):continueraiseValidationError(self.error_messages['invalid'],code='invalid')defstrptime(self,value,format):raiseNotImplementedError('Subclasses must define this method.')

[documentos]classDateField(BaseTemporalField):widget=DateInputinput_formats=formats.get_format_lazy('DATE_INPUT_FORMATS')default_error_messages={'invalid':_('Enter a valid date.'),}defto_python(self,value):""" Validates that the input can be converted to a date. Returns a Python datetime.date object. """ifvalueinself.empty_values:returnNoneifisinstance(value,datetime.datetime):returnvalue.date()ifisinstance(value,datetime.date):returnvaluereturnsuper(DateField,self).to_python(value)defstrptime(self,value,format):returndatetime.datetime.strptime(force_str(value),format).date()

[documentos]classTimeField(BaseTemporalField):widget=TimeInputinput_formats=formats.get_format_lazy('TIME_INPUT_FORMATS')default_error_messages={'invalid':_('Enter a valid time.')}defto_python(self,value):""" Validates that the input can be converted to a time. Returns a Python datetime.time object. """ifvalueinself.empty_values:returnNoneifisinstance(value,datetime.time):returnvaluereturnsuper(TimeField,self).to_python(value)defstrptime(self,value,format):returndatetime.datetime.strptime(force_str(value),format).time()

[documentos]classDateTimeField(BaseTemporalField):widget=DateTimeInputinput_formats=formats.get_format_lazy('DATETIME_INPUT_FORMATS')default_error_messages={'invalid':_('Enter a valid date/time.'),}defprepare_value(self,value):ifisinstance(value,datetime.datetime):value=to_current_timezone(value)returnvaluedefto_python(self,value):""" Validates that the input can be converted to a datetime. Returns a Python datetime.datetime object. """ifvalueinself.empty_values:returnNoneifisinstance(value,datetime.datetime):returnfrom_current_timezone(value)ifisinstance(value,datetime.date):result=datetime.datetime(value.year,value.month,value.day)returnfrom_current_timezone(result)result=super(DateTimeField,self).to_python(value)returnfrom_current_timezone(result)defstrptime(self,value,format):returndatetime.datetime.strptime(force_str(value),format)

[documentos]classRegexField(CharField):def__init__(self,regex,max_length=None,min_length=None,error_message=None,*args,**kwargs):""" regex can be either a string or a compiled regular expression object. error_message is an optional error message to use, if 'Enter a valid value' is too generic for you. """kwargs.setdefault('strip',False)super(RegexField,self).__init__(max_length,min_length,*args,**kwargs)self._set_regex(regex)def_get_regex(self):returnself._regexdef_set_regex(self,regex):ifisinstance(regex,six.string_types):regex=re.compile(regex,re.UNICODE)self._regex=regexifhasattr(self,'_regex_validator')andself._regex_validatorinself.validators:self.validators.remove(self._regex_validator)self._regex_validator=validators.RegexValidator(regex=regex)self.validators.append(self._regex_validator)regex=property(_get_regex,_set_regex)

[documentos]classFileField(Field):widget=ClearableFileInputdefault_error_messages={'invalid':_("No file was submitted. Check the encoding type on the form."),'missing':_("No file was submitted."),'empty':_("The submitted file is empty."),'max_length':ungettext_lazy('Ensure this filename has at most %(max)d character (it has %(length)d).','Ensure this filename has at most %(max)d characters (it has %(length)d).','max'),'contradiction':_('Please either submit a file or check the clear checkbox, not both.')}def__init__(self,*args,**kwargs):self.max_length=kwargs.pop('max_length',None)self.allow_empty_file=kwargs.pop('allow_empty_file',False)super(FileField,self).__init__(*args,**kwargs)defto_python(self,data):ifdatainself.empty_values:returnNone# UploadedFile objects should have name and size attributes.try:file_name=data.namefile_size=data.sizeexceptAttributeError:raiseValidationError(self.error_messages['invalid'],code='invalid')ifself.max_lengthisnotNoneandlen(file_name)>self.max_length:params={'max':self.max_length,'length':len(file_name)}raiseValidationError(self.error_messages['max_length'],code='max_length',params=params)ifnotfile_name:raiseValidationError(self.error_messages['invalid'],code='invalid')ifnotself.allow_empty_fileandnotfile_size:raiseValidationError(self.error_messages['empty'],code='empty')returndatadefclean(self,data,initial=None):# If the widget got contradictory inputs, we raise a validation errorifdataisFILE_INPUT_CONTRADICTION:raiseValidationError(self.error_messages['contradiction'],code='contradiction')# False means the field value should be cleared; further validation is# not needed.ifdataisFalse:ifnotself.required:returnFalse# If the field is required, clearing is not possible (the widget# shouldn't return False data in that case anyway). False is not# in self.empty_value; if a False value makes it this far# it should be validated from here on out as None (so it will be# caught by the required check).data=Noneifnotdataandinitial:returninitialreturnsuper(FileField,self).clean(data)defbound_data(self,data,initial):ifdatain(None,FILE_INPUT_CONTRADICTION):returninitialreturndatadefhas_changed(self,initial,data):ifdataisNone:returnFalsereturnTrue

[documentos]classImageField(FileField):default_error_messages={'invalid_image':_("Upload a valid image. The file you uploaded was either not an ""image or a corrupted image."),}defto_python(self,data):""" Checks that the file-upload field data contains a valid image (GIF, JPG, PNG, possibly others -- whatever the Python Imaging Library supports). """f=super(ImageField,self).to_python(data)iffisNone:returnNonefromPILimportImage# We need to get a file object for Pillow. We might have a path or we might# have to read the data into memory.ifhasattr(data,'temporary_file_path'):file=data.temporary_file_path()else:ifhasattr(data,'read'):file=BytesIO(data.read())else:file=BytesIO(data['content'])try:# load() could spot a truncated JPEG, but it loads the entire# image in memory, which is a DoS vector. See #3848 and #18520.image=Image.open(file)# verify() must be called immediately after the constructor.image.verify()# Annotating so subclasses can reuse it for their own validationf.image=image# Pillow doesn't detect the MIME type of all formats. In those# cases, content_type will be None.f.content_type=Image.MIME.get(image.format)exceptException:# Pillow doesn't recognize it as an image.six.reraise(ValidationError,ValidationError(self.error_messages['invalid_image'],code='invalid_image',),sys.exc_info()[2])ifhasattr(f,'seek')andcallable(f.seek):f.seek(0)returnf

[documentos]classURLField(CharField):widget=URLInputdefault_error_messages={'invalid':_('Enter a valid URL.'),}default_validators=[validators.URLValidator()]defto_python(self,value):defsplit_url(url):""" Returns a list of url parts via ``urlparse.urlsplit`` (or raises a ``ValidationError`` exception for certain). """try:returnlist(urlsplit(url))exceptValueError:# urlparse.urlsplit can raise a ValueError with some# misformatted URLs.raiseValidationError(self.error_messages['invalid'],code='invalid')value=super(URLField,self).to_python(value)ifvalue:url_fields=split_url(value)ifnoturl_fields[0]:# If no URL scheme given, assume http://url_fields[0]='http'ifnoturl_fields[1]:# Assume that if no domain is provided, that the path segment# contains the domain.url_fields[1]=url_fields[2]url_fields[2]=''# Rebuild the url_fields list, since the domain segment may now# contain the path too.url_fields=split_url(urlunsplit(url_fields))value=urlunsplit(url_fields)returnvaluedefclean(self,value):value=self.to_python(value).strip()returnsuper(URLField,self).clean(value)

[documentos]classBooleanField(Field):widget=CheckboxInputdefto_python(self,value):"""Returns a Python boolean object."""# Explicitly check for the string 'False', which is what a hidden field# will submit for False. Also check for '0', since this is what# RadioSelect will provide. Because bool("True") == bool('1') == True,# we don't need to handle that explicitly.ifisinstance(value,six.string_types)andvalue.lower()in('false','0'):value=Falseelse:value=bool(value)returnsuper(BooleanField,self).to_python(value)defvalidate(self,value):ifnotvalueandself.required:raiseValidationError(self.error_messages['required'],code='required')defhas_changed(self,initial,data):# Sometimes data or initial may be a string equivalent of a boolean# so we should run it through to_python first to get a boolean valuereturnself.to_python(initial)!=self.to_python(data)

[documentos]classNullBooleanField(BooleanField):""" A field whose valid values are None, True and False. Invalid values are cleaned to None. """widget=NullBooleanSelectdefto_python(self,value):""" Explicitly checks for the string 'True' and 'False', which is what a hidden field will submit for True and False, for 'true' and 'false', which are likely to be returned by JavaScript serializations of forms, and for '1' and '0', which is what a RadioField will submit. Unlike the Booleanfield we need to explicitly check for True, because we are not using the bool() function """ifvaluein(True,'True','true','1'):returnTrueelifvaluein(False,'False','false','0'):returnFalseelse:returnNonedefvalidate(self,value):pass

[documentos]classChoiceField(Field):widget=Selectdefault_error_messages={'invalid_choice':_('Select a valid choice. %(value)s is not one of the available choices.'),}def__init__(self,choices=(),required=True,widget=None,label=None,initial=None,help_text='',*args,**kwargs):super(ChoiceField,self).__init__(required=required,widget=widget,label=label,initial=initial,help_text=help_text,*args,**kwargs)self.choices=choicesdef__deepcopy__(self,memo):result=super(ChoiceField,self).__deepcopy__(memo)result._choices=copy.deepcopy(self._choices,memo)returnresultdef_get_choices(self):returnself._choicesdef_set_choices(self,value):# Setting choices also sets the choices on the widget.# choices can be any iterable, but we call list() on it because# it will be consumed more than once.ifcallable(value):value=CallableChoiceIterator(value)else:value=list(value)self._choices=self.widget.choices=valuechoices=property(_get_choices,_set_choices)defto_python(self,value):"Returns a Unicode object."ifvalueinself.empty_values:return''returnsmart_text(value)defvalidate(self,value):""" Validates that the input is in self.choices. """super(ChoiceField,self).validate(value)ifvalueandnotself.valid_value(value):raiseValidationError(self.error_messages['invalid_choice'],code='invalid_choice',params={'value':value},)defvalid_value(self,value):"Check to see if the provided value is a valid choice"text_value=force_text(value)fork,vinself.choices:ifisinstance(v,(list,tuple)):# This is an optgroup, so look inside the group for optionsfork2,v2inv:ifvalue==k2ortext_value==force_text(k2):returnTrueelse:ifvalue==kortext_value==force_text(k):returnTruereturnFalse

[documentos]classTypedChoiceField(ChoiceField):def__init__(self,*args,**kwargs):self.coerce=kwargs.pop('coerce',lambdaval:val)self.empty_value=kwargs.pop('empty_value','')super(TypedChoiceField,self).__init__(*args,**kwargs)def_coerce(self,value):""" Validate that the value can be coerced to the right type (if not empty). """ifvalue==self.empty_valueorvalueinself.empty_values:returnself.empty_valuetry:value=self.coerce(value)except(ValueError,TypeError,ValidationError):raiseValidationError(self.error_messages['invalid_choice'],code='invalid_choice',params={'value':value},)returnvaluedefclean(self,value):value=super(TypedChoiceField,self).clean(value)returnself._coerce(value)

[documentos]classMultipleChoiceField(ChoiceField):hidden_widget=MultipleHiddenInputwidget=SelectMultipledefault_error_messages={'invalid_choice':_('Select a valid choice. %(value)s is not one of the available choices.'),'invalid_list':_('Enter a list of values.'),}defto_python(self,value):ifnotvalue:return[]elifnotisinstance(value,(list,tuple)):raiseValidationError(self.error_messages['invalid_list'],code='invalid_list')return[smart_text(val)forvalinvalue]defvalidate(self,value):""" Validates that the input is a list or tuple. """ifself.requiredandnotvalue:raiseValidationError(self.error_messages['required'],code='required')# Validate that each value in the value list is in self.choices.forvalinvalue:ifnotself.valid_value(val):raiseValidationError(self.error_messages['invalid_choice'],code='invalid_choice',params={'value':val},)defhas_changed(self,initial,data):ifinitialisNone:initial=[]ifdataisNone:data=[]iflen(initial)!=len(data):returnTrueinitial_set=set(force_text(value)forvalueininitial)data_set=set(force_text(value)forvalueindata)returndata_set!=initial_set

[documentos]classTypedMultipleChoiceField(MultipleChoiceField):def__init__(self,*args,**kwargs):self.coerce=kwargs.pop('coerce',lambdaval:val)self.empty_value=kwargs.pop('empty_value',[])super(TypedMultipleChoiceField,self).__init__(*args,**kwargs)def_coerce(self,value):""" Validates that the values are in self.choices and can be coerced to the right type. """ifvalue==self.empty_valueorvalueinself.empty_values:returnself.empty_valuenew_value=[]forchoiceinvalue:try:new_value.append(self.coerce(choice))except(ValueError,TypeError,ValidationError):raiseValidationError(self.error_messages['invalid_choice'],code='invalid_choice',params={'value':choice},)returnnew_valuedefclean(self,value):value=super(TypedMultipleChoiceField,self).clean(value)returnself._coerce(value)defvalidate(self,value):ifvalue!=self.empty_value:super(TypedMultipleChoiceField,self).validate(value)elifself.required:raiseValidationError(self.error_messages['required'],code='required')

[documentos]classComboField(Field):""" A Field whose clean() method calls multiple Field clean() methods. """def__init__(self,fields=(),*args,**kwargs):super(ComboField,self).__init__(*args,**kwargs)# Set 'required' to False on the individual fields, because the# required validation will be handled by ComboField, not by those# individual fields.forfinfields:f.required=Falseself.fields=fieldsdefclean(self,value):""" Validates the given value against all of self.fields, which is a list of Field instances. """super(ComboField,self).clean(value)forfieldinself.fields:value=field.clean(value)returnvalue

[documentos]classMultiValueField(Field):""" A Field that aggregates the logic of multiple Fields. Its clean() method takes a "decompressed" list of values, which are then cleaned into a single value according to self.fields. Each value in this list is cleaned by the corresponding field -- the first value is cleaned by the first field, the second value is cleaned by the second field, etc. Once all fields are cleaned, the list of clean values is "compressed" into a single value. Subclasses should not have to implement clean(). Instead, they must implement compress(), which takes a list of valid values and returns a "compressed" version of those values -- a single value. You'll probably want to use this with MultiWidget. """default_error_messages={'invalid':_('Enter a list of values.'),'incomplete':_('Enter a complete value.'),}def__init__(self,fields=(),*args,**kwargs):self.require_all_fields=kwargs.pop('require_all_fields',True)super(MultiValueField,self).__init__(*args,**kwargs)forfinfields:f.error_messages.setdefault('incomplete',self.error_messages['incomplete'])ifself.require_all_fields:# Set 'required' to False on the individual fields, because the# required validation will be handled by MultiValueField, not# by those individual fields.f.required=Falseself.fields=fieldsdef__deepcopy__(self,memo):result=super(MultiValueField,self).__deepcopy__(memo)result.fields=tuple(x.__deepcopy__(memo)forxinself.fields)returnresultdefvalidate(self,value):passdefclean(self,value):""" Validates every value in the given list. A value is validated against the corresponding Field in self.fields. For example, if this MultiValueField was instantiated with fields=(DateField(), TimeField()), clean() would call DateField.clean(value[0]) and TimeField.clean(value[1]). """clean_data=[]errors=[]ifnotvalueorisinstance(value,(list,tuple)):ifnotvalueornot[vforvinvalueifvnotinself.empty_values]:ifself.required:raiseValidationError(self.error_messages['required'],code='required')else:returnself.compress([])else:raiseValidationError(self.error_messages['invalid'],code='invalid')fori,fieldinenumerate(self.fields):try:field_value=value[i]exceptIndexError:field_value=Noneiffield_valueinself.empty_values:ifself.require_all_fields:# Raise a 'required' error if the MultiValueField is# required and any field is empty.ifself.required:raiseValidationError(self.error_messages['required'],code='required')eliffield.required:# Otherwise, add an 'incomplete' error to the list of# collected errors and skip field cleaning, if a required# field is empty.iffield.error_messages['incomplete']notinerrors:errors.append(field.error_messages['incomplete'])continuetry:clean_data.append(field.clean(field_value))exceptValidationErrorase:# Collect all validation errors in a single list, which we'll# raise at the end of clean(), rather than raising a single# exception for the first error we encounter. Skip duplicates.errors.extend(mformine.error_listifmnotinerrors)iferrors:raiseValidationError(errors)out=self.compress(clean_data)self.validate(out)self.run_validators(out)returnout

[documentos]defcompress(self,data_list):""" Returns a single value for the given list of values. The values can be assumed to be valid. For example, if this MultiValueField was instantiated with fields=(DateField(), TimeField()), this might return a datetime object created by combining the date and time in data_list. """raiseNotImplementedError('Subclasses must implement this method.')

[documentos]classSplitDateTimeField(MultiValueField):widget=SplitDateTimeWidgethidden_widget=SplitHiddenDateTimeWidgetdefault_error_messages={'invalid_date':_('Enter a valid date.'),'invalid_time':_('Enter a valid time.'),}def__init__(self,input_date_formats=None,input_time_formats=None,*args,**kwargs):errors=self.default_error_messages.copy()if'error_messages'inkwargs:errors.update(kwargs['error_messages'])localize=kwargs.get('localize',False)fields=(DateField(input_formats=input_date_formats,error_messages={'invalid':errors['invalid_date']},localize=localize),TimeField(input_formats=input_time_formats,error_messages={'invalid':errors['invalid_time']},localize=localize),)super(SplitDateTimeField,self).__init__(fields,*args,**kwargs)defcompress(self,data_list):ifdata_list:# Raise a validation error if time or date is empty# (possible if SplitDateTimeField has required=False).ifdata_list[0]inself.empty_values:raiseValidationError(self.error_messages['invalid_date'],code='invalid_date')ifdata_list[1]inself.empty_values:raiseValidationError(self.error_messages['invalid_time'],code='invalid_time')result=datetime.datetime.combine(*data_list)returnfrom_current_timezone(result)returnNone