Add form builder and the basic form workflow

also add the implementation of all form field types. The form will save
the submitted data to the database (using the FormSubmission) model.
pull/173/merge
Serafeim Papastefanos 2014-03-20 23:04:44 +02:00
rodzic a3b7259f98
commit 92563ff535
2 zmienionych plików z 145 dodań i 24 usunięć

Wyświetl plik

@ -0,0 +1,67 @@
import django.forms
from django.utils.datastructures import SortedDict
from django.utils.text import slugify
from unidecode import unidecode
class FormBuilder():
formfields = SortedDict()
def __init__(self, fields):
for field in fields:
options = self.get_options(field)
f = getattr(self, "create_"+field.field_type+"_field" )(field, options)
# unidecode will return an ascii string while slugify wants a unicode string
# on the other hand, slugify returns a safe-string which will be converted
# to a normal str
field_name = str(slugify(unicode(unidecode(field.label))))
self.formfields[field_name] = f
def get_options(self, field):
options = {}
options['label'] = field.label
options['help_text'] = field.help_text
options['required'] = field.required
options['initial'] = field.default_value
return options
def create_singleline_field(self, field, options):
# TODO: This is a default value - it may need to be changed
options['max_length'] = 255
return django.forms.CharField(**options)
def create_multiline_field(self, field, options):
return django.forms.CharField(widget=django.forms.Textarea, **options)
def create_date_field(self, field, options):
return django.forms.DateField(**options)
def create_datetime_field(self, field, options):
return django.forms.DateTimeField(**options)
def create_email_field(self, field, options):
return django.forms.EmailField(**options)
def create_url_field(self, field, options):
return django.forms.URLField(**options)
def create_number_field(self, field, options):
return django.forms.DecimalField(**options)
def create_dropdown_field(self, field, options):
options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(','))
return django.forms.ChoiceField(**options)
def create_radio_field(self, field, options):
options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(','))
return django.forms.ChoiceField(widget=django.forms.RadioSelect, **options)
def create_checkboxes_field(self, field, options):
options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(','))
options['initial'] = field.default_value.split(',')
return django.forms.MultipleChoiceField(widget=django.forms.CheckboxSelectMultiple, **options)
def create_checkbox_field(self, field, options):
return django.forms.BooleanField(**options)
def get_form_class(self):
return type('WagtailForm', (django.forms.Form,), self.formfields )

Wyświetl plik

@ -1,32 +1,53 @@
from django.conf import settings
from django.db import models
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
import json
import re
from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
from modelcluster.fields import ParentalKey
from .forms import FormBuilder
FORM_FIELD_CHOICES = (
('SINGLELINE', _('Single line text')),
('MULTILINE', _('Multi-line text')),
('EMAIL', _('Email')),
('NUMBER', _('Number')),
('URL', _('URL')),
('CHECKBOX', _('Checkbox')),
('CHECKBOXES', _('Checkboxes')),
('DROPDOWN', _('Drop down')),
('RADIO', _('Radio buttons')),
('DATE', _('Date')),
('DATETIME', _('Date/time')),
('singleline', _('Single line text')),
('multiline', _('Multi-line text')),
('email', _('Email')),
('number', _('Number')),
('url', _('URL')),
('checkbox', _('Checkbox')),
('checkboxes', _('Checkboxes')),
('dropdown', _('Drop down')),
('radio', _('Radio buttons')),
('date', _('Date')),
('datetime', _('Date/time')),
)
HTML_EXTENSION_RE = re.compile(r"(.*)\.html")
class FormSubmission(models.Model):
"""Data for a Form submission."""
form_data = models.TextField()
form_page = models.ForeignKey('wagtailcore.Page',related_name='+')
submit_time = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
def __unicode__(self):
return self.form_data
class AbstractFormFields(models.Model):
#page = ParentalKey('wagtailforms.AbstractForm', related_name='form_fields')
label = models.CharField(max_length=255)
"""Database Fields required for building a Django Form field."""
label = models.CharField(max_length=255, help_text=_('The label of the form field') )
field_type = models.CharField(max_length=16, choices = FORM_FIELD_CHOICES)
required = models.BooleanField( default=True)
choices = models.CharField(max_length=512, blank=True, help_text='Comma seperated list of choices')
required = models.BooleanField(default=True)
choices = models.CharField(max_length=512, blank=True, help_text=_('Comma seperated list of choices'))
default_value = models.CharField(max_length=255, blank=True)
help_text = models.CharField(max_length=255, blank=True)
@ -42,31 +63,64 @@ class AbstractFormFields(models.Model):
class Meta:
abstract = True
class AbstractForm(Page):
is_abstract = True #Don't display me in "Add"
"""A Form Page. Pages with form should inhert from it"""
form_builder = FormBuilder
is_abstract = True # Don't display me in "Add"
def __init__(self, *args, **kwargs):
super(Page, self).__init__(*args, **kwargs)
if not hasattr(self, 'landing_page_template'):
template_wo_ext = re.match(HTML_EXTENSION_RE, self.template).group(1)
self.landing_page_template = template_wo_ext + '_landing.html'
class Meta:
abstract = True
def serve(self, request):
# Get fields
form_fields = self.form_fields
fb = self.form_builder(self.form_fields.all() )
form_class = fb.get_form_class()
if request.method == 'POST':
self.form = form_class(request.POST)
if self.form.is_valid():
# remove csrf_token from form.data
form_data = dict(
i for i in self.form.data.items()
if i[0] != 'csrfmiddlewaretoken'
)
FormSubmission.objects.create(
form_data = json.dumps(form_data),
form_page = self.page_ptr,
user = request.user,
)
# TODO: Do other things like sending email
# render the landing_page
# TODO: It is much better to redirect to it
return render(request, self.landing_page_template, {
'self': self,
})
else:
self.form = form_class()
return render(request, self.template, {
'self': self,
'form': self.form,
})
######## TEST
class ConcreteFormFields(Orderable, AbstractFormFields):
page = ParentalKey('wagtailforms.ConcreteForm', related_name='form_fields')
class ConcreteForm(AbstractForm):
pass
thank_you = models.CharField(max_length=255)
ConcreteForm.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('thank_you', classname="full"),
InlinePanel(ConcreteForm, 'form_fields', label="Form Fields"),
]