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 nine.versions import (
DJANGO_GTE_1_8,
DJANGO_GTE_1_10,
)
from six import with_metaclass
from .constants import WIZARD_TYPE_COOKIE, WIZARD_TYPE_SESSION
if DJANGO_GTE_1_8:
from formtools.wizard.views import (
CookieWizardView,
SessionWizardView,
WizardView,
)
else:
from django.contrib.formtools.wizard.views import (
CookieWizardView,
SessionWizardView,
WizardView,
)
if DJANGO_GTE_1_10:
from django.urls import reverse
else:
from django.core.urlresolvers import reverse
__title__ = 'fobi.dynamic'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2018 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