Source code for fobi.dynamic

from six import with_metaclass

from django.core.urlresolvers import reverse
from django.forms.forms import BaseForm  # , get_declared_fields
from django.forms.widgets import media_property
from django.http import HttpResponseRedirect

from nine.versions import DJANGO_GTE_1_7, DJANGO_GTE_1_8

from .constants import WIZARD_TYPE_COOKIE, WIZARD_TYPE_SESSION

if DJANGO_GTE_1_8:
    from formtools.wizard.views import (
        WizardView, SessionWizardView, CookieWizardView
    )
else:
    from django.contrib.formtools.wizard.views import (
        WizardView, SessionWizardView, CookieWizardView
    )

if DJANGO_GTE_1_7:
    from collections import OrderedDict
else:
    from django.utils.datastructures import SortedDict as OrderedDict

__title__ = 'fobi.dynamic'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2016 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): """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). """ 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 ) 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.""" 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