Cleaned passing of session data between ChainedForms in EventCreateView.

This commit is contained in:
2018-01-17 22:58:08 +01:00
parent 0cf77a4ab0
commit fe3d726162
3 changed files with 167 additions and 136 deletions

View File

@@ -2,15 +2,14 @@ import datetime
import logging
import re
from django.http import QueryDict
from . import models
logger = logging.getLogger(__name__)
class SessionDict(QueryDict):
_re_iso8601 = re.compile(r'^ISO8601:(?P<date>'
class Iso8601Serializer(object):
marker = 'ISO8601'
separator = ':'
_re = re.compile(r'^(?P<date>'
r'(?P<year>\d{4})'
r'-(?P<mon>(0[1-9])|(1[012]))'
r'-(?P<day>(0[1-9])|([12][0-9])|(3[01]))'
@@ -19,75 +18,56 @@ class SessionDict(QueryDict):
r'(?P<hour>([01][0-9])|(2[0123]))'
r':(?P<min>[0-5][0-9])'
r':(?P<sec>[0-5][0-9])'
r'([\,\.]\d{1,10})?'
r'([,.]\d{1,10})?'
r')?'
r'(?P<offset>[\-+](([01][0-9])|(2[0123]))(:[0-5][0-9])?)?$')
def __init__(self, instance=None, *args, **kwargs):
if 'mutable' in kwargs:
raise TypeError('__init__(): got an unexpected keyword argument \'mutable\'')
kwargs['mutable'] = True
super(SessionDict, self).__init__(*args, **kwargs)
def __init__(self, instance=None, text=None):
if instance is not None:
self.update(instance)
self.instance = instance
elif text is not None:
self.instance = Iso8601Serializer.deserialize(text)
def _deserialize_value(self, value):
if isinstance(value, basestring):
m = self._re_iso8601.match(value)
@property
def str(self):
return Iso8601Serializer.serialize(self.instance)
@classmethod
def serialize(cls, value, ignore_unsupported_input=False):
strval = None
if isinstance(value, datetime.datetime):
strval = value.isoformat()
elif isinstance(value, datetime.date):
strval = value.isoformat()
elif isinstance(value, datetime.time):
strval = value.isoformat()
elif not ignore_unsupported_input:
raise ValueError('Expected datetime.datetime, datetime.date or datetime.time,'
' not {}'.format(value.__class__.__name__))
if strval is not None:
value = '{marker}{sep}{str}'.format(marker=cls.marker, sep=cls.separator, str=strval)
return value
@classmethod
def deserialize(cls, value, ignore_unsupported_input=False):
prefix = '{marker}{sep}'.format(marker=cls.marker, sep=cls.separator)
if isinstance(value, basestring) and value.startswith(prefix):
haystack = value[len(prefix):]
m = cls._re.match(haystack)
if m is not None:
# logger.debug('SessionDict._deserialize_value(): found iso8601 date \'%s\'', value)
gd = m.groupdict()
if gd['hour'] is None:
value = datetime.date(int(gd['year']), int(gd['mon']), int(gd['day']))
# logger.debug('SessionDict._deserialize_value(): converted into datetime.date \'%s\'', value)
elif gd['year'] is None:
value = datetime.time(hour=int(gd['hour']), minute=int(gd['min']), second=int(gd['sec']))
# logger.debug('SessionDict._deserialize_value(): converted into datetime.time \'%s\'', value)
else:
value = datetime.datetime(int(gd['year']), int(gd['mon']), int(gd['day']),
hour=int(gd['hour']), minute=int(gd['min']), second=int(gd['sec']))
# logger.debug('SessionDict._deserialize_value(): converted into datetime.datetime \'%s\'', value)
elif not ignore_unsupported_input:
raise ValueError('Format not recognized \'{str}\''.format(str=haystack))
elif not ignore_unsupported_input:
raise ValueError('Expected basestring,'
' not {}'.format(value.__class__.__name__))
return value
def _serialize_value(self, value):
if isinstance(value, datetime.datetime):
# logger.debug('SessionDict._serialize_value(): found datetime.datetime \'%s\'', value)
value = u'ISO8601:{}'.format(value.isoformat())
# logger.debug('SessionDict._serialize_value(): converted into unicode \'%s\'', value)
elif isinstance(value, datetime.date):
# logger.debug('SessionDict._serialize_value(): found datetime.date \'%s\'', value)
value = u'ISO8601:{}'.format(value.isoformat())
# logger.debug('SessionDict._serialize_value(): converted into unicode \'%s\'', value)
elif isinstance(value, datetime.time):
# logger.debug('SessionDict._serialize_value(): found datetime.time \'%s\'', value)
value = u'ISO8601:{}'.format(value.isoformat())
# logger.debug('SessionDict._serialize_value(): converted into unicode \'%s\'', value)
return value
@property
def session_data(self):
ret = dict()
for k in self:
ret[k] = self._serialize_value(self[k])
return ret
def update(self, data):
for k in data:
self[k] = self._deserialize_value(data[k])
def event_factory(data):
kwargs = dict()
if 'deadline' in data:
buf = data['deadline']
if isinstance(buf, basestring):
deadline_choice = buf.lower()
deadline_field = 'deadline_{}'.format(deadline_choice)
if deadline_field in data:
data['deadline'] = data[deadline_field]
for field in data:
if hasattr(models.Event, field):
kwargs[field] = data[field]
return models.Event(**kwargs)

View File

@@ -5,11 +5,14 @@ import logging
from babel.dates import format_date
from django import forms
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import get_language, ugettext, ugettext_lazy as _
from datetimewidget.widgets import DateWidget, TimeWidget, DateTimeWidget
from . import choices
from . import config
from . import converters
from . import models
logger = logging.getLogger(__name__)
DEVELOPMENT_INIT_FORMS = True
@@ -19,13 +22,30 @@ class ChainedForm(forms.Form):
_next_form_name = None
def __init__(self, *args, **kwargs):
previous_data = kwargs.pop('previous_data', dict())
self._session_data = dict()
if 'session_data' in kwargs:
self.add_session_data(kwargs.pop('session_data'))
super(ChainedForm, self).__init__(*args, **kwargs)
self._proceed_previous_data(previous_data)
self._proceed_session_data(self._session_data)
def _proceed_previous_data(self, previous_data):
def _proceed_session_data(self, session_data):
pass
def _serialize_value(self, value):
return converters.Iso8601Serializer.serialize(value, ignore_unsupported_input=True)
def _deserialize_value(self, value):
return converters.Iso8601Serializer.deserialize(value, ignore_unsupported_input=True)
def _get_model(self):
if not hasattr(self, '_model'):
raise ImproperlyConfigured('{cls} is missing a Model.'
' Define {cls}._model'.format(cls=self.__class__.__name__))
return self._model
def _get_object(self, model_kwargs):
return self._get_model()(**model_kwargs)
@property
def form_name(self):
return self.__class__.__name__
@@ -51,8 +71,43 @@ class ChainedForm(forms.Form):
def get_next_form_name(cls):
return cls._next_form_name
def get_session_data(self):
data = dict()
for k in self._session_data:
data[k] = self._serialize_value(self._session_data[k])
for k in self.cleaned_data:
data[k] = self._serialize_value(self.cleaned_data[k])
return data
class ModeForm(ChainedForm):
def add_session_data(self, data):
for k in data:
self._session_data[k] = self._deserialize_value(data[k])
def save(self):
object_kwargs = dict()
data = self._session_data
data.update(self.cleaned_data)
if 'deadline' in data:
buf = data['deadline']
if isinstance(buf, basestring):
deadline_choice = buf.lower()
deadline_field = 'deadline_{}'.format(deadline_choice)
if deadline_field in data:
data['deadline'] = data[deadline_field]
model = self._get_model()
for field in data:
if hasattr(model, field):
object_kwargs[field] = data[field]
return self._get_object(object_kwargs)
class EventCreateForm(ChainedForm):
_model = models.Event
class ModeForm(EventCreateForm):
_form_title = _(u'Veranstaltungsmodus')
_next_form_name = 'LocationForm'
@@ -151,7 +206,7 @@ class ModeForm(ChainedForm):
return super(ModeForm, self).next_form_name
class TrainingForm(ChainedForm):
class TrainingForm(EventCreateForm):
_form_title = _(u'Kursinhalte / Kursziele')
_next_form_name = 'LocationForm'
@@ -194,7 +249,7 @@ class TrainingForm(ChainedForm):
widget=forms.TextInput(attrs={'placeholder': _(u'Kann frei gelassen werden')}))
class LocationForm(ChainedForm):
class LocationForm(EventCreateForm):
_form_title = _(u'Ort')
_next_form_name = 'ApproachForm'
@@ -216,10 +271,10 @@ class LocationForm(ChainedForm):
u' wo das ganze stattfindet.'),
widget=forms.TextInput(attrs={'placeholder': u'Karlsruhe'}))
def _proceed_previous_data(self, previous_data):
super(LocationForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(LocationForm, self)._proceed_session_data(session_data)
sport = previous_data.get('sport', None)
sport = session_data.get('sport', None)
if sport == 'B':
self.fields['terrain'].initial = 'alpine'
self.fields['location'].widget.attrs['placeholder'] = u'Ramsau, Berchtesgadener Alpen'
@@ -237,7 +292,7 @@ class LocationForm(ChainedForm):
self.fields['location'].initial = self.fields['location'].widget.attrs['placeholder']
class ApproachForm(ChainedForm):
class ApproachForm(EventCreateForm):
_form_title = _(u'An- und Abreise / Unterkunft')
_next_form_name = 'RequirementsForm'
@@ -316,13 +371,13 @@ class ApproachForm(ChainedForm):
label=_(u'Andere Verpflegung'),
)
def _proceed_previous_data(self, previous_data):
super(ApproachForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(ApproachForm, self)._proceed_session_data(session_data)
last_day = previous_data.get('last_day', None)
sport = previous_data.get('sport', None)
country = previous_data.get('country', None)
terrain = previous_data.get('terrain', None)
last_day = session_data.get('last_day', None)
sport = session_data.get('sport', None)
country = session_data.get('country', None)
terrain = session_data.get('terrain', None)
self.fields['transport_other'].widget.attrs['placeholder'] = _(u'Nebenstehendes Feld beachten')
self.fields['meeting_point_other'].widget.attrs['placeholder'] = _(u'Nebenstehendes Feld beachten')
@@ -345,7 +400,7 @@ class ApproachForm(ChainedForm):
self.fields['accommodation_other'].widget = forms.HiddenInput()
class RequirementsForm(ChainedForm):
class RequirementsForm(EventCreateForm):
_form_title = _(u'Voraussetungen / Vorbedingungen')
_next_form_name = 'DescriptionForm'
@@ -400,12 +455,12 @@ class RequirementsForm(ChainedForm):
},
bootstrap_version=3))
def _proceed_previous_data(self, previous_data):
super(RequirementsForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(RequirementsForm, self)._proceed_session_data(session_data)
sport = previous_data.get('sport', None)
level = previous_data.get('level', None)
terrain = previous_data.get('terrain', None)
sport = session_data.get('sport', None)
level = session_data.get('level', None)
terrain = session_data.get('terrain', None)
if sport == 'B':
self.fields['equipment'].initial = u'%s (%s)' % (
@@ -465,7 +520,7 @@ class RequirementsForm(ChainedForm):
self.fields['requirements'].initial = _(u'Gehzeit X-Y Stunden, ca. X km, ca. X Hm')
class DescriptionForm(ChainedForm):
class DescriptionForm(EventCreateForm):
_form_title = _(u'Titel / Beschreibung')
_next_form_name = 'RegistrationForm'
@@ -475,14 +530,14 @@ class DescriptionForm(ChainedForm):
description = forms.CharField(label=_(u'Beschreibung'),
widget=forms.Textarea(attrs={'placeholder': u''}))
def _proceed_previous_data(self, previous_data):
super(DescriptionForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(DescriptionForm, self)._proceed_session_data(session_data)
mode = previous_data.get('mode', None)
sport = previous_data.get('sport', None)
level = previous_data.get('level', None)
terrain = previous_data.get('terrain', None)
last_day = previous_data.get('last_day', None)
mode = session_data.get('mode', None)
sport = session_data.get('sport', None)
level = session_data.get('level', None)
terrain = session_data.get('terrain', None)
last_day = session_data.get('last_day', None)
title_prefix = u''
if mode == 'training':
@@ -510,7 +565,7 @@ class DescriptionForm(ChainedForm):
self.fields['title'].initial = title_prefix
class RegistrationForm(ChainedForm):
class RegistrationForm(EventCreateForm):
_form_title = _(u'Teilnehmer / Anmeldung')
_next_form_name = 'TrainerForm'
@@ -544,10 +599,10 @@ class RegistrationForm(ChainedForm):
},
bootstrap_version=3))
def _proceed_previous_data(self, previous_data):
super(RegistrationForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(RegistrationForm, self)._proceed_session_data(session_data)
first_day = previous_data.get('first_day', None)
first_day = session_data.get('first_day', None)
if first_day:
new_choices = []
for key, desc in self.fields['deadline'].choices:
@@ -571,7 +626,7 @@ class RegistrationForm(ChainedForm):
self.fields['deadline'].choices = new_choices
class TrainerForm(ChainedForm):
class TrainerForm(EventCreateForm):
_form_title = _(u'Tourenleitung')
_next_form_name = 'ChargesForm'
@@ -616,8 +671,8 @@ class TrainerForm(ChainedForm):
help_text=_(u'Kann freigelassen werden'),
)
def _proceed_previous_data(self, previous_data):
super(TrainerForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(TrainerForm, self)._proceed_session_data(session_data)
self.fields['trainer_firstname'].widget.attrs['placeholder'] = _(u'Erik')
self.fields['trainer_familyname'].widget.attrs['placeholder'] = _(u'Müller')
@@ -636,7 +691,7 @@ class TrainerForm(ChainedForm):
self.fields['trainer_email'].initial = self.fields['trainer_email'].widget.attrs['placeholder']
class ChargesForm(ChainedForm):
class ChargesForm(EventCreateForm):
_form_title = _(u'Kosten')
_next_form_name = 'OverviewForm'
@@ -669,19 +724,19 @@ class ChargesForm(ChainedForm):
label=_(u'Zusätzliche Kosten (Text)'),
)
def _proceed_previous_data(self, previous_data):
super(ChargesForm, self)._proceed_previous_data(previous_data)
def _proceed_session_data(self, session_data):
super(ChargesForm, self)._proceed_session_data(session_data)
mode = previous_data.get('mode', None)
sport = previous_data.get('sport', None)
ski_lift = previous_data.get('ski_lift', False)
terrain = previous_data.get('terrain', None)
country = previous_data.get('country', None)
first_day = previous_data.get('first_day', None)
arrival_previous_day = previous_data.get('arrival_previous_day', False)
last_day = previous_data.get('last_day', None)
pre_meeting_1 = previous_data.get('pre_meeting_1', None)
pre_meeting_2 = previous_data.get('pre_meeting_2', None)
mode = session_data.get('mode', None)
sport = session_data.get('sport', None)
ski_lift = session_data.get('ski_lift', False)
terrain = session_data.get('terrain', None)
country = session_data.get('country', None)
first_day = session_data.get('first_day', None)
arrival_previous_day = session_data.get('arrival_previous_day', False)
last_day = session_data.get('last_day', None)
pre_meeting_1 = session_data.get('pre_meeting_1', None)
pre_meeting_2 = session_data.get('pre_meeting_2', None)
if ski_lift:
charge_key = 'K'
@@ -801,5 +856,5 @@ class ChargesForm(ChainedForm):
self.fields['additional_costs'].initial = additional_costs_text
class OverviewForm(ChainedForm):
class OverviewForm(EventCreateForm):
_form_title = _(u'Übersicht')

View File

@@ -6,7 +6,6 @@ from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.views import generic
from . import converters
from . import forms
from . import models
@@ -64,18 +63,15 @@ class EventCreateView(generic.FormView):
return context
def form_valid(self, form):
previous_data = converters.SessionDict(
self.request.session.get('dav_events_event_create_previous_data', dict()))
previous_data.update(form.cleaned_data)
event = converters.event_factory(previous_data)
form.add_session_data(self.request.session.get('dav_events_event_create_previous_data', dict()))
event = form.save()
next_form_name = form.next_form_name
if next_form_name:
self.request.session['dav_events_event_create_previous_data'] = previous_data.session_data
self.request.session['dav_events_event_create_previous_data'] = form.get_session_data()
self.request.session['dav_events_event_create_next_form_name'] = next_form_name
next_form_class = self.get_form_class(next_form_name)
next_form = next_form_class(previous_data=previous_data)
next_form = next_form_class(session_data=form.get_session_data())
return self.render_to_response(self.get_context_data(form=next_form, event=event))
else:
event.save()