class BaseFormSet

from django.forms import BaseFormSet
A collection of instances of the same Form class.

Ancestors (MRO)

  1. BaseFormSet
  2. RenderableFormMixin
  3. RenderableMixin

Attributes

  Defined in
default_error_messages = {'missing_management_form': 'ManagementForm data is missing or has been tampered with. Missing fields: %(field_names)s. You may need to file a bug report if the issue persists.', 'too_many_forms': '', 'too_few_forms': ''} BaseFormSet
forms = <django.utils.functional.cached_property object at 0x10f03e5f0> BaseFormSet
management_form = <django.utils.functional.cached_property object at 0x10f03e580> BaseFormSet
template_name_div = 'django/forms/formsets/div.html' BaseFormSet
template_name_p = 'django/forms/formsets/p.html' BaseFormSet
template_name_table = 'django/forms/formsets/table.html' BaseFormSet
template_name_ul = 'django/forms/formsets/ul.html' BaseFormSet
Expand Collapse

Properties

def cleaned_data(): BaseFormSet

Getter

    @property
    def cleaned_data(self):
        """
        Return a list of form.cleaned_data dicts for every form in self.forms.
        """
        if not self.is_valid():
            raise AttributeError(
                "'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__
            )
        return [form.cleaned_data for form in self.forms]

def deleted_forms(): BaseFormSet

Getter

    @property
    def deleted_forms(self):
        """Return a list of forms that have been marked for deletion."""
        if not self.is_valid() or not self.can_delete:
            return []
        # construct _deleted_form_indexes which is just a list of form indexes
        # that have had their deletion widget set to True
        if not hasattr(self, "_deleted_form_indexes"):
            self._deleted_form_indexes = []
            for i, form in enumerate(self.forms):
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self.initial_form_count() and not form.has_changed():
                    continue
                if self._should_delete_form(form):
                    self._deleted_form_indexes.append(i)
        return [self.forms[i] for i in self._deleted_form_indexes]

def empty_form(): BaseFormSet

Getter

    @property
    def empty_form(self):
        form_kwargs = {
            **self.get_form_kwargs(None),
            "auto_id": self.auto_id,
            "prefix": self.add_prefix("__prefix__"),
            "empty_permitted": True,
            "use_required_attribute": False,
            "renderer": self.form_renderer,
        }
        form = self.form(**form_kwargs)
        self.add_fields(form, None)
        return form

def errors(): BaseFormSet

Getter

    @property
    def errors(self):
        """Return a list of form.errors for every form in self.forms."""
        if self._errors is None:
            self.full_clean()
        return self._errors

def extra_forms(): BaseFormSet

Getter

    @property
    def extra_forms(self):
        """Return a list of all the extra forms in this formset."""
        return self.forms[self.initial_form_count() :]

def initial_forms(): BaseFormSet

Getter

    @property
    def initial_forms(self):
        """Return a list of all the initial forms in this formset."""
        return self.forms[: self.initial_form_count()]

def media(): BaseFormSet

Getter

    @property
    def media(self):
        # All the forms on a FormSet are the same, so you only need to
        # interrogate the first form for media.
        if self.forms:
            return self.forms[0].media
        else:
            return self.empty_form.media

def ordered_forms(): BaseFormSet

Getter

    @property
    def ordered_forms(self):
        """
        Return a list of form in the order specified by the incoming data.
        Raise an AttributeError if ordering is not allowed.
        """
        if not self.is_valid() or not self.can_order:
            raise AttributeError(
                "'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__
            )
        # Construct _ordering, which is a list of (form_index, order_field_value)
        # tuples. After constructing this list, we'll sort it by order_field_value
        # so we have a way to get to the form indexes in the order specified
        # by the form data.
        if not hasattr(self, "_ordering"):
            self._ordering = []
            for i, form in enumerate(self.forms):
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self.initial_form_count() and not form.has_changed():
                    continue
                # don't add data marked for deletion to self.ordered_data
                if self.can_delete and self._should_delete_form(form):
                    continue
                self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))
            # After we're done populating self._ordering, sort it.
            # A sort function to order things numerically ascending, but
            # None should be sorted below anything else. Allowing None as
            # a comparison value makes it so we can leave ordering fields
            # blank.

            def compare_ordering_key(k):
                if k[1] is None:
                    return (1, 0)  # +infinity, larger than any number
                return (0, k[1])

            self._ordering.sort(key=compare_ordering_key)
        # Return a list of form.cleaned_data dicts in the order specified by
        # the form data.
        return [self.forms[i[0]] for i in self._ordering]

def template_name(): BaseFormSet

Getter

    @property
    def template_name(self):
        return self.renderer.formset_template_name
Expand Collapse

Methods

def _construct_form(self, i, **kwargs): BaseFormSet

Instantiate and return the i-th form instance in a formset.
    def _construct_form(self, i, **kwargs):
        """Instantiate and return the i-th form instance in a formset."""
        defaults = {
            "auto_id": self.auto_id,
            "prefix": self.add_prefix(i),
            "error_class": self.error_class,
            # Don't render the HTML 'required' attribute as it may cause
            # incorrect validation for extra, optional, and deleted
            # forms in the formset.
            "use_required_attribute": False,
            "renderer": self.form_renderer,
        }
        if self.is_bound:
            defaults["data"] = self.data
            defaults["files"] = self.files
        if self.initial and "initial" not in kwargs:
            try:
                defaults["initial"] = self.initial[i]
            except IndexError:
                pass
        # Allow extra forms to be empty, unless they're part of
        # the minimum forms.
        if i >= self.initial_form_count() and i >= self.min_num:
            defaults["empty_permitted"] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)
        return form

def _should_delete_form(self, form): BaseFormSet

Return whether or not the form was marked for deletion.
    def _should_delete_form(self, form):
        """Return whether or not the form was marked for deletion."""
        return form.cleaned_data.get(DELETION_FIELD_NAME, False)

def add_fields(self, form, index): BaseFormSet

A hook for adding extra fields on to each form instance.
    def add_fields(self, form, index):
        """A hook for adding extra fields on to each form instance."""
        initial_form_count = self.initial_form_count()
        if self.can_order:
            # Only pre-fill the ordering field for initial forms.
            if index is not None and index < initial_form_count:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(
                    label=_("Order"),
                    initial=index + 1,
                    required=False,
                    widget=self.get_ordering_widget(),
                )
            else:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(
                    label=_("Order"),
                    required=False,
                    widget=self.get_ordering_widget(),
                )
        if self.can_delete and (
            self.can_delete_extra or (index is not None and index < initial_form_count)
        ):
            form.fields[DELETION_FIELD_NAME] = BooleanField(
                label=_("Delete"),
                required=False,
                widget=self.get_deletion_widget(),
            )

def add_prefix(self, index): BaseFormSet

    def add_prefix(self, index):
        return "%s-%s" % (self.prefix, index)

def as_div(self): RenderableFormMixin

Render as <div> elements.
    def as_div(self):
        """Render as <div> elements."""
        return self.render(self.template_name_div)

def as_p(self): RenderableFormMixin

Render as <p> elements.
    def as_p(self):
        """Render as <p> elements."""
        return self.render(self.template_name_p)

def as_table(self): RenderableFormMixin

Render as <tr> elements excluding the surrounding <table> tag.
    def as_table(self):
        """Render as <tr> elements excluding the surrounding <table> tag."""
        return self.render(self.template_name_table)

def as_ul(self): RenderableFormMixin

Render as <li> elements excluding the surrounding <ul> tag.
    def as_ul(self):
        """Render as <li> elements excluding the surrounding <ul> tag."""
        return self.render(self.template_name_ul)

def clean(self): BaseFormSet

Hook for doing any extra formset-wide cleaning after Form.clean() has
been called on every form. Any ValidationError raised by this method
will not be associated with a particular form; it will be accessible
via formset.non_form_errors()
    def clean(self):
        """
        Hook for doing any extra formset-wide cleaning after Form.clean() has
        been called on every form. Any ValidationError raised by this method
        will not be associated with a particular form; it will be accessible
        via formset.non_form_errors()
        """
        pass

def full_clean(self): BaseFormSet

Clean all of self.data and populate self._errors and
self._non_form_errors.
    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and
        self._non_form_errors.
        """
        self._errors = []
        self._non_form_errors = self.error_class(
            error_class="nonform", renderer=self.renderer
        )
        empty_forms_count = 0

        if not self.is_bound:  # Stop further processing.
            return

        if not self.management_form.is_valid():
            error = ValidationError(
                self.error_messages["missing_management_form"],
                params={
                    "field_names": ", ".join(
                        self.management_form.add_prefix(field_name)
                        for field_name in self.management_form.errors
                    ),
                },
                code="missing_management_form",
            )
            self._non_form_errors.append(error)

        for i, form in enumerate(self.forms):
            # Empty forms are unchanged forms beyond those with initial data.
            if not form.has_changed() and i >= self.initial_form_count():
                empty_forms_count += 1
            # Accessing errors calls full_clean() if necessary.
            # _should_delete_form() requires cleaned_data.
            form_errors = form.errors
            if self.can_delete and self._should_delete_form(form):
                continue
            self._errors.append(form_errors)
        try:
            if (
                self.validate_max
                and self.total_form_count() - len(self.deleted_forms) > self.max_num
            ) or self.management_form.cleaned_data[
                TOTAL_FORM_COUNT
            ] > self.absolute_max:
                raise ValidationError(
                    self.error_messages["too_many_forms"] % {"num": self.max_num},
                    code="too_many_forms",
                )
            if (
                self.validate_min
                and self.total_form_count()
                - len(self.deleted_forms)
                - empty_forms_count
                < self.min_num
            ):
                raise ValidationError(
                    self.error_messages["too_few_forms"] % {"num": self.min_num},
                    code="too_few_forms",
                )
            # Give self.clean() a chance to do cross-form validation.
            self.clean()
        except ValidationError as e:
            self._non_form_errors = self.error_class(
                e.error_list,
                error_class="nonform",
                renderer=self.renderer,
            )

def get_context(self):

BaseFormSet

    def get_context(self):
        return {"formset": self}

RenderableMixin

    def get_context(self):
        raise NotImplementedError(
            "Subclasses of RenderableMixin must provide a get_context() method."
        )

def get_form_kwargs(self, index): BaseFormSet

Return additional keyword arguments for each individual formset form.

index will be None if the form being constructed is a new empty
form.
    def get_form_kwargs(self, index):
        """
        Return additional keyword arguments for each individual formset form.

        index will be None if the form being constructed is a new empty
        form.
        """
        return self.form_kwargs.copy()

def has_changed(self): BaseFormSet

Return True if data in any form differs from initial.
    def has_changed(self):
        """Return True if data in any form differs from initial."""
        return any(form.has_changed() for form in self)

def initial_form_count(self): BaseFormSet

Return the number of forms that are required in this FormSet.
    def initial_form_count(self):
        """Return the number of forms that are required in this FormSet."""
        if self.is_bound:
            return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
        else:
            # Use the length of the initial data if it's there, 0 otherwise.
            initial_forms = len(self.initial) if self.initial else 0
        return initial_forms

def is_multipart(self): BaseFormSet

Return True if the formset needs to be multipart, i.e. it
has FileInput, or False otherwise.
    def is_multipart(self):
        """
        Return True if the formset needs to be multipart, i.e. it
        has FileInput, or False otherwise.
        """
        if self.forms:
            return self.forms[0].is_multipart()
        else:
            return self.empty_form.is_multipart()

def is_valid(self): BaseFormSet

Return True if every form in self.forms is valid.
    def is_valid(self):
        """Return True if every form in self.forms is valid."""
        if not self.is_bound:
            return False
        # Accessing errors triggers a full clean the first time only.
        self.errors
        # List comprehension ensures is_valid() is called for all forms.
        # Forms due to be deleted shouldn't cause the formset to be invalid.
        forms_valid = all(
            [
                form.is_valid()
                for form in self.forms
                if not (self.can_delete and self._should_delete_form(form))
            ]
        )
        return forms_valid and not self.non_form_errors()

def non_form_errors(self): BaseFormSet

Return an ErrorList of errors that aren't associated with a particular
form -- i.e., from formset.clean(). Return an empty ErrorList if there
are none.
    def non_form_errors(self):
        """
        Return an ErrorList of errors that aren't associated with a particular
        form -- i.e., from formset.clean(). Return an empty ErrorList if there
        are none.
        """
        if self._non_form_errors is None:
            self.full_clean()
        return self._non_form_errors

def render(self, template_name=None, context=None, renderer=None): RenderableMixin

    def render(self, template_name=None, context=None, renderer=None):
        renderer = renderer or self.renderer
        template = template_name or self.template_name
        context = context or self.get_context()
        return mark_safe(renderer.render(template, context))

def total_error_count(self): BaseFormSet

Return the number of errors across all forms in the formset.
    def total_error_count(self):
        """Return the number of errors across all forms in the formset."""
        return len(self.non_form_errors()) + sum(
            len(form_errors) for form_errors in self.errors
        )

def total_form_count(self): BaseFormSet

Return the total number of forms in this FormSet.
    def total_form_count(self):
        """Return the total number of forms in this FormSet."""
        if self.is_bound:
            # return absolute_max if it is lower than the actual total form
            # count in the data; this is DoS protection to prevent clients
            # from forcing the server to instantiate arbitrary numbers of
            # forms
            return min(
                self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max
            )
        else:
            initial_forms = self.initial_form_count()
            total_forms = max(initial_forms, self.min_num) + self.extra
            # Allow all existing related objects/inlines to be displayed,
            # but don't allow extra beyond max_num.
            if initial_forms > self.max_num >= 0:
                total_forms = initial_forms
            elif total_forms > self.max_num >= 0:
                total_forms = self.max_num
        return total_forms