kopia lustrzana https://github.com/wagtail/wagtail
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
rodzic
a3b7259f98
commit
92563ff535
|
@ -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 )
|
||||||
|
|
|
@ -1,32 +1,53 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
from wagtail.wagtailcore.models import Page, Orderable
|
from wagtail.wagtailcore.models import Page, Orderable
|
||||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
|
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
|
||||||
|
|
||||||
from modelcluster.fields import ParentalKey
|
from modelcluster.fields import ParentalKey
|
||||||
|
|
||||||
|
from .forms import FormBuilder
|
||||||
|
|
||||||
FORM_FIELD_CHOICES = (
|
FORM_FIELD_CHOICES = (
|
||||||
('SINGLELINE', _('Single line text')),
|
('singleline', _('Single line text')),
|
||||||
('MULTILINE', _('Multi-line text')),
|
('multiline', _('Multi-line text')),
|
||||||
('EMAIL', _('Email')),
|
('email', _('Email')),
|
||||||
('NUMBER', _('Number')),
|
('number', _('Number')),
|
||||||
('URL', _('URL')),
|
('url', _('URL')),
|
||||||
('CHECKBOX', _('Checkbox')),
|
('checkbox', _('Checkbox')),
|
||||||
('CHECKBOXES', _('Checkboxes')),
|
('checkboxes', _('Checkboxes')),
|
||||||
('DROPDOWN', _('Drop down')),
|
('dropdown', _('Drop down')),
|
||||||
('RADIO', _('Radio buttons')),
|
('radio', _('Radio buttons')),
|
||||||
('DATE', _('Date')),
|
('date', _('Date')),
|
||||||
('DATETIME', _('Date/time')),
|
('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):
|
class AbstractFormFields(models.Model):
|
||||||
#page = ParentalKey('wagtailforms.AbstractForm', related_name='form_fields')
|
"""Database Fields required for building a Django Form field."""
|
||||||
label = models.CharField(max_length=255)
|
|
||||||
|
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)
|
field_type = models.CharField(max_length=16, choices = FORM_FIELD_CHOICES)
|
||||||
required = models.BooleanField( default=True)
|
required = models.BooleanField(default=True)
|
||||||
choices = models.CharField(max_length=512, blank=True, help_text='Comma seperated list of choices')
|
choices = models.CharField(max_length=512, blank=True, help_text=_('Comma seperated list of choices'))
|
||||||
default_value = models.CharField(max_length=255, blank=True)
|
default_value = models.CharField(max_length=255, blank=True)
|
||||||
help_text = 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:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractForm(Page):
|
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:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def serve(self, request):
|
def serve(self, request):
|
||||||
# Get fields
|
fb = self.form_builder(self.form_fields.all() )
|
||||||
form_fields = self.form_fields
|
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, {
|
return render(request, self.template, {
|
||||||
'self': self,
|
'self': self,
|
||||||
|
'form': self.form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######## TEST
|
||||||
class ConcreteFormFields(Orderable, AbstractFormFields):
|
class ConcreteFormFields(Orderable, AbstractFormFields):
|
||||||
page = ParentalKey('wagtailforms.ConcreteForm', related_name='form_fields')
|
page = ParentalKey('wagtailforms.ConcreteForm', related_name='form_fields')
|
||||||
|
|
||||||
|
|
||||||
class ConcreteForm(AbstractForm):
|
class ConcreteForm(AbstractForm):
|
||||||
pass
|
thank_you = models.CharField(max_length=255)
|
||||||
|
|
||||||
ConcreteForm.content_panels = [
|
ConcreteForm.content_panels = [
|
||||||
FieldPanel('title', classname="full title"),
|
FieldPanel('title', classname="full title"),
|
||||||
|
FieldPanel('thank_you', classname="full"),
|
||||||
InlinePanel(ConcreteForm, 'form_fields', label="Form Fields"),
|
InlinePanel(ConcreteForm, 'form_fields', label="Form Fields"),
|
||||||
]
|
]
|
||||||
|
|
Ładowanie…
Reference in New Issue