django-extra-views

repository·master·Indexed 23 days ago

https://github.com/andrewingram/django-extra-views

A Django package providing additional class-based generic views to simplify common design patterns. It includes specialized views for managing formsets and inline relationships—such as FormSetView, ModelFormSetView, InlineFormSetView, CreateWithInlinesView, and UpdateWithInlinesView—as well as the SearchableListMixin for adding search capabilities to ListViews.

Tokens
6.7K
Snippets
20
Records
21
Agent score
80%

What's inside django-extra-views

  1. Overview of django-extra-views features

    master

    django-extra-views provides several specialized class-based views and mixins to simplify common Django patterns, particularly those similar to the Django admin interface:

    • Formset Views: FormSetView and ModelFormSetView (equivalents of FormView and ModelFormView).
    • Inline Views: InlineFormSetView (for related models via inlineformset_factory) and GenericInlineFormSetView (for GenericForeignKeys).
    • Combined Views: CreateWithInlinesView and UpdateWithInlinesView for managing a model and multiple inlines simultaneously.
    • Mixins:
      • NamedFormsetsMixin: Allows naming each inline or formset in the template context.
      • SortableListMixin: Adds sorting functionality to views.
      • SearchableListMixin: Adds search functionality to views.
      • SuccessMessageMixin & FormSetSuccessMessageMixin: Displays success messages after form submission.
  2. Provide initial data for ModelFormSet and InlineFormSet

    master

    In ModelFormSet and InlineFormSet, the initial data behaves differently than standard FormSets. Data provided via the initial attribute is inserted into the extra forms. Only data from get_queryset() is inserted into the existing rows.

    To determine initial data dynamically at runtime, override the get_initial() method.

    from extra_views import ModelFormSetView
    from my_app.models import Item
    
    
    class ItemFormSetView(ModelFormSetView):
        template_name = 'item_formset.html'
        model = Item
        factory_kwargs = {'extra': 10}
        initial = [{'name': 'example1'}, {'name': 'example2'}]
        # This results in: [existing items] + [2 forms with initial data] + [8 empty forms]
    
        def get_initial(self):
            # Get a list of initial values for the formset here
            initial = [...]
            return initial
  3. Use CreateWithInlinesView and UpdateWithInlinesView

    master

    These views are powerful replacements for Django's CreateView and UpdateView. They allow you to manage a parent model form along with any number of inline formsets simultaneously, similar to the Django Admin interface.

    How to use:

    1. Define inline configurations by subclassing InlineFormSetFactory for each related model.
    2. Pass these factory classes to the inlines list in your view.
    3. In your template, access the parent form via {{ form }} and iterate through the inlines using the inlines context variable.

    Template pattern:

    <form method="post">
      {{ form }}
      {% for formset in inlines %}
        {{ formset }}
      {% endfor %}
      <input type="submit" value="Submit" />
    </form>
    from extra_views import CreateWithInlinesView, UpdateWithInlinesView, InlineFormSetFactory
    
    class ItemInline(InlineFormSetFactory):
        model = Item
        fields = ['sku', 'price', 'name']
    
    class ContactInline(InlineFormSetFactory):
        model = Contact
        fields = ['name', 'email']
    
    class CreateOrderView(CreateWithInlinesView):
        model = Order
        inlines = [ItemInline, ContactInline]
        fields = ['customer', 'name']
        template_name = 'order_and_items.html'
    
        def get_success_url(self):
            return self.object.get_absolute_url()
    
    class UpdateOrderView(UpdateWithInlinesView):
        model = Order
        inlines = [ItemInline, ContactInline]
        fields = ['customer', 'name']
        template_name = 'order_and_items.html
    
        def get_success_url(self):
            return self.object.get_absolute_url()
  4. Use GenericInlineFormSetView for ContentType relationships

    master

    Use GenericInlineFormSetView when you need to use django.contrib.contenttypes.forms.generic_inlineformset_factory(). This is useful for managing relationships via ContentTypes and Object IDs.

    To customize the relationship fields, pass ct_field and fk_field inside the factory_kwargs dictionary.

    There is also a GenericInlineFormSetFactory available for use within CreateWithInlinesView and UpdateWithInlinesView in the same way as standard InlineFormSetFactory classes.

    from extra_views.generic import GenericInlineFormSetView
    
    class EditOrderTags(GenericInlineFormSetView):
        model = Order
        inline_model = Tag
        factory_kwargs = {'ct_field': 'content_type', 'fk_field': 'object_id',
                          'max_num': 1}
        formset_kwargs = {'save_as_new': True}
  5. Use ModelFormSetView for model-based formsets

    master

    Use ModelFormSetView to work with django.forms.modelformset_factory(). It allows you to manage a collection of model instances through a formset.

    • Configuration: Define the model and either fields (list of fields to include) or exclude (list of fields to omit). If a form_class is provided, fields and exclude are not required.
    • Querysets: By default, it populates the formset with all instances of the model. Override get_queryset() to filter the instances (e.g., based on URL parameters).
    • Template Context: The formset is available in the template context as formset.
    from extra_views import ModelFormSetView
    from my_app.models import Item
    
    class ItemFormSetView(ModelFormSetView):
        model = Item
        fields = ['name', 'sku', 'price']
        template_name = 'item_formset.html'
    
        def get_queryset(self):
            sku = self.kwargs['sku']
            return super(ItemFormSetView, self).get_queryset().filter(sku=sku)
  6. Install django-extra-views

    master

    You can install the stable release from PyPI using pip, or install the current master branch directly from GitHub.

    After installation, you must add 'extra_views' to your Django INSTALLED_APPS setting.

    # Install stable release
    pip install django-extra-views
    
    # Or install master branch from GitHub
    pip install -e git://github.com/AndrewIngram/django-extra-views.git#egg=django-extra-views
    INSTALLED_APPS = [
        ...
        'extra_views',
        ...
    ]
  7. Add sorting functionality to ListViews with SortableListMixin

    master

    To add sorting capabilities to a Django ListView, mix in SortableListMixin.

    Configuration

    • Field Aliases: Use sort_fields_aliases to hide actual database field names in the URL query string. This maps a user-friendly alias to the real field name.
    • Direct Fields: Alternatively, define sort_fields to show the actual field names in the query string.

    Template Usage

    SortableListMixin provides a sort_helper object (an instance of SortHelper) to your template context. You can use the following helper methods to generate sort links or check current state:

    • {{ sort_helper.get_sort_query_by_FOO }}: Generates the query string for sorting by field FOO.
    • {{ sort_helper.get_sort_query_by_FOO_asc }}: Generates the query string for ascending sort by FOO.
    • {{ sort_helper.get_sort_query_by_FOO_desc }}: Generates the query string for descending sort by FOO.
    • {{ sort_helper.is_sorted_by_FOO }}: Returns whether the current list is sorted by field FOO.
    from django.views.generic import ListView
    from extra_views import SortableListMixin
    
    class SortableItemListView(SortableListMixin, ListView):
        sort_fields_aliases = [('name', 'by_name'), ('id', 'by_id'), ]
        model = Item
  8. Override the base formset class

    master

    To implement custom logic at the formset level (such as a custom clean() method), define a custom formset class by subclassing Django's BaseInlineFormSet and assign it to the formset_class attribute of your InlineFormSetView.

    from django.forms.models import BaseInlineFormSet
    from extra_views import InlineFormSetView
    from my_app.models import Item
    from my_app.forms import ItemForm
    
    class ItemInlineFormSet(BaseInlineFormSet):
        def clean(self):
            # Your custom clean logic goes here
            super().clean()
    
    class ItemInlineView(InlineFormSetView):
        model = Item
        form_class = ItemForm
        formset_class = ItemInlineFormSet     # enables our custom inline
  9. Use InlineFormSetView for related models

    master

    Use InlineFormSetView when you want to edit instances of a model that has a ForeignKey relationship to a parent model. It uses django.forms.inlineformset_factory() internally.

    Required attributes:

    • model: The parent model.
    • inline_model: The related model (the one containing the ForeignKey).
    from extra_views import InlineFormSetView
    
    class EditContactAddresses(InlineFormSetView):
        model = Contact
        inline_model = Address
  10. Use FormSetSuccessMessageMixin for single formset views

    master

    For views handling a single formset (like FormSetView), use extra_views.FormSetSuccessMessageMixin. To include data from the formset in the message, override get_success_message(self, formset).

    from extra_views import FormSetView, FormSetSuccessMessageMixin
    from my_app.forms import AddressForm
    
    
    class AddressFormSetView(FormSetView):
        form_class = AddressForm
        success_url = 'success/'
        success_message = 'Addresses Updated!'
    
        # Or override at runtime to use formset data:
        def get_success_message(self, formset):
            return '{} addresses were updated.'.format(len(formset.forms))
  11. Use FormSetView for non-model formsets

    master

    Use FormSetView when you want to display a single, non-model formset on a page. It is the formset equivalent of Django's FormView.

    Key features:

    • Renders the formset in the template context as the variable formset.
    • Calls formset_valid(formset) upon successful POST validation, where you should implement your handling logic.
    • Redirects to success_url after successful validation.

    To configure the formset, you can set attributes directly on the class or use factory_kwargs, formset_kwargs, and form_kwargs to pass arguments to the underlying Django formset constructor.

    from extra_views import FormSetView
    from my_app.forms import AddressForm
    
    class AddressFormSetView(FormSetView):
        template_name = 'address_formset.html'
        form_class = AddressForm
        success_url = 'success/'
    
        def get_initial(self):
            # return whatever you'd normally use as the initial data for your formset.
            return data
    
        def formset_valid(self, formset):
            # do whatever you'd like to do with the valid formset
            return super(AddressFormSetView, self).formset_valid(formset)
  12. Add search functionality to ListViews with SearchableListMixin

    master

    To add search capabilities to a Django ListView, mix in SearchableListMixin and define a search_fields list containing the model fields to search against.

    By default, the view filters the object_list if a q query string parameter is provided in the URL (e.g., ?q=query).

    • Custom Lookups: You can provide specific lookups by using tuples in search_fields, such as [('name', 'iexact'), 'sku']. The default lookup is icontains.
    • Manual Search Logic: You can override the get_search_query method to implement custom search behavior.
    • Field Types: It is recommended to use only string lookups. When searching number fields, they are converted to strings before comparison to prevent errors; this behavior is controlled by the check_lookups setting on SearchableListMixin.
    from django.views.generic import ListView
    from extra_views import SearchableListMixin
    
    class SearchableItemListView(SearchableListMixin, ListView):
        template_name = 'extra_views/item_list.html'
        search_fields = ['name', 'sku']
        model = Item