form classes collect fields declared on the base classes def newmcs name bases attrs collect fields from current class and remove them from attrs attrsdeclaredfields key attrs popkey for key value in listattrs items if isinstancevalue field newclass super newmcs name bases attrs walk through the mro declaredfields for base in reversednewclass mro collect fields from base class if hasattrbase declaredfields declaredfields updatebase declaredfields field shadowing for attr value in base dict items if value is none and attr in declaredfields declaredfields popattr newclass basefields declaredfields newclass declaredfields declaredfields return newclass class baseformrenderableformmixin defaultrenderer none fieldorder none prefix none userequiredattribute true templatenamediv djangoformsdiv html templatenamep djangoformsp html templatenametable djangoformstable html templatenameul djangoformsul html templatenamelabel djangoformslabel html def init self datanone filesnone autoidids prefixnone initialnone errorclasserrorlist labelsuffixnone emptypermittedfalse fieldordernone userequiredattributenone renderernone self isbound data is not none or files is not none self data multivaluedict if data is none else data self files multivaluedict if files is none else files self autoid autoid if prefix is not none self prefix prefix self initial initial or self errorclass errorclass translators this is the default suffix added to form field labels self labelsuffix labelsuffix if labelsuffix is not none else self emptypermitted emptypermitted self errors none stores the errors after clean has been called the basefields class attribute is the classwide definition of fields because a particular instance of the class might want to alter self fields we create self fields here by copying basefields instances should always modify self fields they should not modify self basefields self fields copy deepcopyself basefields self boundfieldscache self orderfieldsself fieldorder if fieldorder is none else fieldorder if userequiredattribute is not none self userequiredattribute userequiredattribute if self emptypermitted and self userequiredattribute raise valueerror the emptypermitted and userequiredattribute arguments may not both be true initialize form renderer use a global default if not specified either as an argument or as self defaultrenderer if renderer is none if self defaultrenderer is none renderer getdefaultrenderer else renderer self defaultrenderer if isinstanceself defaultrenderer type renderer renderer self renderer renderer def orderfieldsself fieldorder if fieldorder is none return fields for key in fieldorder try fieldskey self fields popkey except keyerror ignore unknown fields pass fields updateself fields add remaining fields in original order self fields fields def reprself if self errors is none isvalid unknown else isvalid self isbound and not self errors return clss boundbounds validvalids fieldsfieldss cls self class name bound self isbound valid isvalid fields joinself fields def bounditemsself yield the form s fields as boundfield objects for name in self fields yield selfname def getitemself name return an errordict for the data provided for the form if self errors is none self fullclean return self errors def isvalidself return the field name with a prefix appended if this form has a prefix set subclasses may wish to override add an initial prefix for checking dynamic initial values return initials self addprefixfieldname def widgetdatavalueself widget htmlname valuefromdatadict gets the data from the data dictionaries each widget type knows how to retrieve its own data because some widgets split data over several html fields return widget valuefromdatadictself data self files htmlname property def templatenameself return self renderer formtemplatename def getcontextself fields hiddenfields toperrors self nonfielderrors copy for name bf in self bounditems if bf ishidden if bf errors toperrors hidden field names errors name name error stre for e in bf errors hiddenfields appendbf else fields appendbf bf errors return form self fields fields hiddenfields hiddenfields errors toperrors def nonfielderrorsself return self errors get nonfielderrors self errorclasserrorclassnonfield rendererself renderer def adderrorself field error if not isinstanceerror validationerror normalize to validationerror and let its constructor do the hard work of making sense of the input error validationerrorerror if hasattrerror errordict if field is not none raise typeerror the argument field must be none when the error argument contains errors for multiple fields else error error errordict else error field or nonfielderrors error errorlist for field errorlist in error items if field not in self errors if field nonfielderrors and field not in self fields raise valueerror s has no field named s self class name field if field nonfielderrors self errorsfield self errorclass errorclassnonfield rendererself renderer else self errorsfield self errorclassrendererself renderer self errorsfield extenderrorlist if field in self cleaneddata del self cleaneddatafield def haserrorself field codenone return field in self errors and code is none or anyerror code code for error in self errors asdatafield def fullcleanself self errors errordict if not self isbound stop further processing return self cleaneddata if the form is permitted to be empty and none of the form data has changed from the initial data short circuit any validation if self emptypermitted and not self haschanged return self cleanfields self cleanform self postclean def cleanfieldsself for name bf in self bounditems field bf field value bf initial if field disabled else bf data try if isinstancefield filefield value field cleanvalue bf initial else value field cleanvalue self cleaneddataname value if hasattrself cleans name value getattrself cleans name self cleaneddataname value except validationerror as e self adderrorname e def cleanformself try cleaneddata self clean except validationerror as e self adderrornone e else if cleaneddata is not none self cleaneddata cleaneddata def postcleanself pass def cleanself return self cleaneddata def haschangedself return all media required to render the widgets on this form media media for field in self fields values media field widget media return media def ismultipartself return anyfield widget needsmultipartform for field in self fields values def hiddenfieldsself return field for field in self if field ishidden def visiblefieldsself return field for field in self if not field ishidden def getinitialforfieldself field fieldname value self initial getfieldname field initial if callablevalue value value if this is an autogenerated default date nix the microseconds for standardized handling see 22502 if isinstancevalue datetime datetime datetime time and not field widget supportsmicroseconds value value replacemicrosecond0 return value class formbaseform metaclassdeclarativefieldsmetaclass a collection of fields plus their associated data this is a separate class from baseform in order to abstract the way self fields is specified this class form is the one that does the fancy metaclass stuff purely for the semantic sugar it allows one to define a form using declarative syntax baseform itself has no way of designating self fields collect fields declared on the base classes collect fields from current class and remove them from attrs walk through the mro collect fields from base class field shadowing the main implementation of all the form logic note that this class is different than form see the comments by the form class for more info any improvements to the form api should be made to this class not to the form class translators this is the default suffix added to form field labels stores the errors after clean has been called the base_fields class attribute is the class wide definition of fields because a particular instance of the class might want to alter self fields we create self fields here by copying base_fields instances should always modify self fields they should not modify self base_fields initialize form renderer use a global default if not specified either as an argument or as self default_renderer rearrange the fields according to field_order field_order is a list of field names specifying the order append fields not included in the list in the default order for backward compatibility with subclasses not overriding field_order if field_order is none keep all fields in the order defined in the class ignore unknown fields in field_order to allow disabling fields in form subclasses without redefining ordering ignore unknown fields add remaining fields in original order yield name bf pairs where bf is a boundfield object yield the form s fields as boundfield objects return a boundfield with the given name return an errordict for the data provided for the form return true if the form has no errors or false otherwise return the field name with a prefix appended if this form has a prefix set subclasses may wish to override add an initial prefix for checking dynamic initial values value_from_datadict gets the data from the data dictionaries each widget type knows how to retrieve its own data because some widgets split data over several html fields return an errorlist of errors that aren t associated with a particular field i e from form clean return an empty errorlist if there are none update the content of self _errors the field argument is the name of the field to which the errors should be added if it s none treat the errors as non_field_errors the error argument can be a single error a list of errors or a dictionary that maps field names to lists of errors an error can be either a simple string or an instance of validationerror with its message attribute set and a list or dictionary can be an actual list or dict or an instance of validationerror with its error_list or error_dict attribute set if error is a dictionary the field argument must be none and errors will be added to the fields that correspond to the keys of the dictionary normalize to validationerror and let its constructor do the hard work of making sense of the input clean all of self data and populate self _errors and self cleaned_data stop further processing if the form is permitted to be empty and none of the form data has changed from the initial data short circuit any validation an internal hook for performing additional cleaning after form cleaning is complete used for model validation in model forms hook for doing any extra form wide cleaning after field clean has been called on every field any validationerror raised by this method will not be associated with a particular field it will have a special case association with the field named __all__ return true if data differs from initial return all media required to render the widgets on this form return true if the form needs to be multipart encoded i e it has fileinput or false otherwise return a list of all the boundfield objects that are hidden fields useful for manual form layout in templates return a list of boundfield objects that aren t hidden fields the opposite of the hidden_fields method return initial data for field on form use initial data from the form or the field in that order evaluate callable values if this is an auto generated default date nix the microseconds for standardized handling see 22502 this is a separate class from baseform in order to abstract the way self fields is specified this class form is the one that does the fancy metaclass stuff purely for the semantic sugar it allows one to define a form using declarative syntax baseform itself has no way of designating self fields
import copy
import datetime

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.forms.fields import Field, FileField
from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin
from django.forms.widgets import Media, MediaDefiningClass
from django.utils.datastructures import MultiValueDict
from django.utils.functional import cached_property
from django.utils.translation import gettext as _

from .renderers import get_default_renderer

__all__ = ("BaseForm", "Form")


class DeclarativeFieldsMetaclass(MediaDefiningClass):
    

    def __new__(mcs, name, bases, attrs):
        
        attrs["declared_fields"] = {
            key: attrs.pop(key)
            for key, value in list(attrs.items())
            if isinstance(value, Field)
        }

        new_class = super().__new__(mcs, name, bases, attrs)

        
        declared_fields = {}
        for base in reversed(new_class.__mro__):
            
            if hasattr(base, "declared_fields"):
                declared_fields.update(base.declared_fields)

            
            for attr, value in base.__dict__.items():
                if value is None and attr in declared_fields:
                    declared_fields.pop(attr)

        new_class.base_fields = declared_fields
        new_class.declared_fields = declared_fields

        return new_class


class BaseForm(RenderableFormMixin):
    

    default_renderer = None
    field_order = None
    prefix = None
    use_required_attribute = True

    template_name_div = "django/forms/div.html"
    template_name_p = "django/forms/p.html"
    template_name_table = "django/forms/table.html"
    template_name_ul = "django/forms/ul.html"
    template_name_label = "django/forms/label.html"

    def __init__(
        self,
        data=None,
        files=None,
        auto_id="id_%s",
        prefix=None,
        initial=None,
        error_class=ErrorList,
        label_suffix=None,
        empty_permitted=False,
        field_order=None,
        use_required_attribute=None,
        renderer=None,
    ):
        self.is_bound = data is not None or files is not None
        self.data = MultiValueDict() if data is None else data
        self.files = MultiValueDict() if files is None else files
        self.auto_id = auto_id
        if prefix is not None:
            self.prefix = prefix
        self.initial = initial or {}
        self.error_class = error_class
        
        self.label_suffix = label_suffix if label_suffix is not None else _(":")
        self.empty_permitted = empty_permitted
        self._errors = None  

        
        
        
        
        
        self.fields = copy.deepcopy(self.base_fields)
        self._bound_fields_cache = {}
        self.order_fields(self.field_order if field_order is None else field_order)

        if use_required_attribute is not None:
            self.use_required_attribute = use_required_attribute

        if self.empty_permitted and self.use_required_attribute:
            raise ValueError(
                "The empty_permitted and use_required_attribute arguments may "
                "not both be True."
            )

        
        
        if renderer is None:
            if self.default_renderer is None:
                renderer = get_default_renderer()
            else:
                renderer = self.default_renderer
                if isinstance(self.default_renderer, type):
                    renderer = renderer()
        self.renderer = renderer

    def order_fields(self, field_order):
        
        if field_order is None:
            return
        fields = {}
        for key in field_order:
            try:
                fields[key] = self.fields.pop(key)
            except KeyError:  
                pass
        fields.update(self.fields)  
        self.fields = fields

    def __repr__(self):
        if self._errors is None:
            is_valid = "Unknown"
        else:
            is_valid = self.is_bound and not self._errors
        return "<%(cls)s bound=%(bound)s, valid=%(valid)s, fields=(%(fields)s)>" % {
            "cls": self.__class__.__name__,
            "bound": self.is_bound,
            "valid": is_valid,
            "fields": ";".join(self.fields),
        }

    def _bound_items(self):
        
        for name in self.fields:
            yield name, self[name]

    def __iter__(self):
        
        for name in self.fields:
            yield self[name]

    def __getitem__(self, name):
        
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError(
                "Key '%s' not found in '%s'. Choices are: %s."
                % (
                    name,
                    self.__class__.__name__,
                    ", ".join(sorted(self.fields)),
                )
            )
        if name not in self._bound_fields_cache:
            self._bound_fields_cache[name] = field.get_bound_field(self, name)
        return self._bound_fields_cache[name]

    @property
    def errors(self):
        
        if self._errors is None:
            self.full_clean()
        return self._errors

    def is_valid(self):
        
        return self.is_bound and not self.errors

    def add_prefix(self, field_name):
        
        return "%s-%s" % (self.prefix, field_name) if self.prefix else field_name

    def add_initial_prefix(self, field_name):
        
        return "initial-%s" % self.add_prefix(field_name)

    def _widget_data_value(self, widget, html_name):
        
        
        
        return widget.value_from_datadict(self.data, self.files, html_name)

    @property
    def template_name(self):
        return self.renderer.form_template_name

    def get_context(self):
        fields = []
        hidden_fields = []
        top_errors = self.non_field_errors().copy()
        for name, bf in self._bound_items():
            if bf.is_hidden:
                if bf.errors:
                    top_errors += [
                        _("(Hidden field %(name)s) %(error)s")
                        % {"name": name, "error": str(e)}
                        for e in bf.errors
                    ]
                hidden_fields.append(bf)
            else:
                fields.append((bf, bf.errors))
        return {
            "form": self,
            "fields": fields,
            "hidden_fields": hidden_fields,
            "errors": top_errors,
        }

    def non_field_errors(self):
        
        return self.errors.get(
            NON_FIELD_ERRORS,
            self.error_class(error_class="nonfield", renderer=self.renderer),
        )

    def add_error(self, field, error):
        
        if not isinstance(error, ValidationError):
            
            
            error = ValidationError(error)

        if hasattr(error, "error_dict"):
            if field is not None:
                raise TypeError(
                    "The argument `field` must be `None` when the `error` "
                    "argument contains errors for multiple fields."
                )
            else:
                error = error.error_dict
        else:
            error = {field or NON_FIELD_ERRORS: error.error_list}

        for field, error_list in error.items():
            if field not in self.errors:
                if field != NON_FIELD_ERRORS and field not in self.fields:
                    raise ValueError(
                        "'%s' has no field named '%s'."
                        % (self.__class__.__name__, field)
                    )
                if field == NON_FIELD_ERRORS:
                    self._errors[field] = self.error_class(
                        error_class="nonfield", renderer=self.renderer
                    )
                else:
                    self._errors[field] = self.error_class(renderer=self.renderer)
            self._errors[field].extend(error_list)
            if field in self.cleaned_data:
                del self.cleaned_data[field]

    def has_error(self, field, code=None):
        return field in self.errors and (
            code is None
            or any(error.code == code for error in self.errors.as_data()[field])
        )

    def full_clean(self):
        
        self._errors = ErrorDict()
        if not self.is_bound:  
            return
        self.cleaned_data = {}
        
        
        if self.empty_permitted and not self.has_changed():
            return

        self._clean_fields()
        self._clean_form()
        self._post_clean()

    def _clean_fields(self):
        for name, bf in self._bound_items():
            field = bf.field
            value = bf.initial if field.disabled else bf.data
            try:
                if isinstance(field, FileField):
                    value = field.clean(value, bf.initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, "clean_%s" % name):
                    value = getattr(self, "clean_%s" % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

    def _clean_form(self):
        try:
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        else:
            if cleaned_data is not None:
                self.cleaned_data = cleaned_data

    def _post_clean(self):
        
        pass

    def clean(self):
        
        return self.cleaned_data

    def has_changed(self):
        
        return bool(self.changed_data)

    @cached_property
    def changed_data(self):
        return [name for name, bf in self._bound_items() if bf._has_changed()]

    @property
    def media(self):
        
        media = Media()
        for field in self.fields.values():
            media += field.widget.media
        return media

    def is_multipart(self):
        
        return any(field.widget.needs_multipart_form for field in self.fields.values())

    def hidden_fields(self):
        
        return [field for field in self if field.is_hidden]

    def visible_fields(self):
        
        return [field for field in self if not field.is_hidden]

    def get_initial_for_field(self, field, field_name):
        
        value = self.initial.get(field_name, field.initial)
        if callable(value):
            value = value()
        
        
        if (
            isinstance(value, (datetime.datetime, datetime.time))
            and not field.widget.supports_microseconds
        ):
            value = value.replace(microsecond=0)
        return value


class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass):
    "A collection of Fields, plus their associated data."