Django and formsets

348 views Asked by At

I try to understand how the internal of Django formsets work.

After a formset class is created by formset_factory function, inheriting/getting attributes from BaseFormSet, a object of the new created class is initialize, example:

 ArticleFormSet = formset_factory(ArticleFormA, extra=2)
 formset = ArticleFormSet()

If I check with dir(formset) both form and forms attributes are available, but if I try to print forms nothing is printed, I suppose this is related to the decorator @cached_property(but when is called ?)

In the initialization of the formset object there are no operations related to forms attribute.

So, I suppose is called when {{formset}} or {{formset.as_p}} etc is called.

The method has:

forms = ' '.join(form.as_p() for form in self)

Why in self, I don't understand, because form based on dir() is just a class, and self is the formset object. What is the logic behind ?

(PS I understand what is doing going to every form), but is not form in forms, besides the fact forms is now populated

And after that, the fields are created using management_form that before.

    return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
1

There are 1 answers

0
Håken Lid On BEST ANSWER

form in self results in a call to the special method self.__iter__(), which is how iterable classes are implemented in python.

object.__iter__(self)

This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container.

For django's formsets, this is the relevant code.

class BaseFormSet(object):
    """
    A collection of instances of the same Form class.
    """

    def __iter__(self):
        """Yields the forms in the order they should be rendered"""
        return iter(self.forms)

    @cached_property
    def forms(self):
        """
        Instantiate forms at first property access.
        """
        # DoS protection is included in total_form_count()
        forms = [self._construct_form(i, **self.get_form_kwargs(i))
                 for i in range(self.total_form_count())]
        return forms

link to full source