from six import with_metaclass from django.forms.forms import BaseForm # , get_declared_fields from django.forms.widgets import media_property from nine.versions import DJANGO_GTE_1_7 __title__ = 'fobi.dynamic' __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('assemble_form_class',) if DJANGO_GTE_1_7: from collections import OrderedDict else: from django.utils.datastructures import SortedDict as OrderedDict # **************************************************************************** # **************************************************************************** # **************************** Form generator ******************************** # **************************************************************************** # **************************************************************************** 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() 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 class DynamicForm(with_metaclass(DeclarativeFieldsMetaclass, base_class)): """Dynamically created form element plugin class.""" return DynamicForm