Source code for fobi.dynamic

from collections import OrderedDict

from django.forms.forms import BaseForm
from django.forms.widgets import media_property
from django.http import HttpResponseRedirect
from django.urls import reverse
from formtools.wizard.views import (
    CookieWizardView,
    SessionWizardView,
    WizardView,
)
from six import with_metaclass

from .constants import WIZARD_TYPE_COOKIE, WIZARD_TYPE_SESSION

__title__ = "fobi.dynamic"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = (
    "assemble_form_class",
    "assemble_form_wizard_class",
)

# ****************************************************************************
# ****************************************************************************
# **************************** Form generator ********************************
# ****************************************************************************
# ****************************************************************************


[docs]def assemble_form_class( form_entry, base_class=BaseForm, request=None, origin=None, origin_kwargs_update_func=None, origin_return_func=None, form_element_entries=None, get_form_field_instances_kwargs={}, ): """Assemble a form class by given entry. :param form_entry: :param base_class: :param django.http.HttpRequest request: :param string origin: :param callable origin_kwargs_update_func: :param callable origin_return_func: :param iterable form_element_entries: If given, used instead of ``form_entry.formelemententry_set.all`` (no additional database hit). :param dict get_form_field_instances_kwargs: To be passed as **kwargs to the :method:`get_form_field_instances_kwargs`. """ if form_element_entries is None: form_element_entries = form_entry.formelemententry_set.all() # DeclarativeFieldsMetaclass class DeclarativeFieldsMetaclass(type): """Declarative fields meta class. Copied from ``django.forms.forms.DeclarativeFieldsMetaclass``. Metaclass that converts Field attributes to a dictionary called `base_fields`, taking into account parent class 'base_fields' as well. """ def __new__(cls, name, bases, attrs): """New.""" base_fields = [] for creation_counter, form_element_entry in enumerate( form_element_entries ): plugin = form_element_entry.get_plugin(request=request) # We simply make sure the plugin exists. We don't handle # exceptions relate to the non-existent plugins here. They # are instead handled in registry. if plugin: plugin_form_field_instances = ( plugin._get_form_field_instances( form_element_entry=form_element_entry, origin=origin, kwargs_update_func=origin_kwargs_update_func, return_func=origin_return_func, extra={"counter": creation_counter}, request=request, form_entry=form_entry, form_element_entries=form_element_entries, **get_form_field_instances_kwargs, ) ) for ( form_field_name, form_field_instance, ) in plugin_form_field_instances: base_fields.append( (form_field_name, form_field_instance) ) attrs["base_fields"] = OrderedDict(base_fields) new_class = super(DeclarativeFieldsMetaclass, cls).__new__( cls, name, bases, attrs ) if "media" not in attrs: new_class.media = media_property(new_class) return new_class # DynamicForm class DynamicForm(with_metaclass(DeclarativeFieldsMetaclass, base_class)): """Dynamically created form element plugin class.""" # Finally, return the DynamicForm return DynamicForm
[docs]def assemble_form_wizard_class( form_wizard_entry, base_class=SessionWizardView, request=None, origin=None, origin_kwargs_update_func=None, origin_return_func=None, form_wizard_form_entries=None, template_name=None, ): """Assemble form wizard class. :param form_wizard_entry: :param base_class: :param request: :param origin: :param origin_kwargs_update_func: :param origin_return_func: :param form_wizard_form_entries: :param template_name: :return: """ if form_wizard_entry.wizard_type == WIZARD_TYPE_SESSION: base_class = SessionWizardView elif form_wizard_entry.wizard_type == WIZARD_TYPE_COOKIE: base_class = CookieWizardView elif not issubclass(base_class, WizardView): base_class = SessionWizardView if form_wizard_form_entries is None: form_entries = [ form_wizard_form_entry.form_entry for form_wizard_form_entry in form_wizard_entry.formwizardformentry_set.all() ] else: form_entries = [ form_wizard_form_entry.form_entry for form_wizard_form_entry in form_wizard_form_entries ] # DeclarativeFormsMetaclass class DeclarativeFormsMetaclass(type): """Declarative forms meta class. Copied from ``django.forms.forms.DeclarativeFieldsMetaclass``. Metaclass that converts Forms attributes to a dictionary called `form_list`, taking into account parent class 'form_list' as well. """ def __new__(cls, name, bases, attrs): """New. Form list should be presented in the following way: ..code-block: python named_contact_forms = ( ('contactdata', ContactForm1), ('leavemessage', ContactForm2), ) In the example above, the fist item of the tuple would be the slug of the form included. The second one would be the form class. """ form_list = [] for creation_counter, form_entry in enumerate(form_entries): form_cls = assemble_form_class(form_entry, request=request) form_list.append((form_entry.slug, form_cls)) attrs["form_list"] = form_list attrs["template_name"] = template_name new_class = super(DeclarativeFormsMetaclass, cls).__new__( cls, name, bases, attrs ) # if 'media' not in attrs: # new_class.media = media_property(new_class) return new_class # DynamicFormWizard class DynamicFormWizard( with_metaclass(DeclarativeFormsMetaclass, base_class) ): """Dynamically created form wizard class.""" def done(self, form_list, **kwargs): """Done.""" # do_something_with_the_form_data(form_list) redirect_url = reverse( "fobi.form_wizard_entry_submitted", [form_wizard_entry.slug] ) return HttpResponseRedirect(redirect_url) # Finally, return the dynamic wizard return DynamicFormWizard