Hi all,
(Apologies if this belongs on django-users instead. To clarify, I'm
interested in what appear to be potentially missing or useful features in
Django, and whether it's worth implementing them and submitting pull
requests, or creating separate reusable modules to implement them. I'm
also not asking for anyone to fix these issues for me.)
We're building an application which has some large forms including
repeating formsets and file upload fields. It's intended for use in
developing countries with slow and unreliable Internet connections. Thus
it's important for users to be able to save the form regularly, and to not
lose uploaded files even if there's a form validation error.
We encountered a number of issues which feel like they might either be:
* bugs in Django, or
* deliberate limitations of Django core, that could be useful as
a third-party module.
And I'd like your feedback about where you feel these issues lie. I'm
hoping that we're not the only ones to encounter them!
1. GCBV and Vanilla Views do a great job for simple forms, but they leave
out embedded formsets entirely. (For example our large form has repeating
sections for employment history, education, etc.) It feels like it would
be great to have formsets handled "more automatically" - instantiating,
validating and saving them just like we do for forms. A lot of our code is
shared between all our formsets, and potentially reusable.
2. Adding instances to a formset on the client side using Javascript.
There is a django-formset-js package on PyPI, but it requires special
attributes to be added to form elements that would have been difficult
with crispy forms (which we're also using) and it feels almost like this
functionality ought to be in Django core (maybe not today, but one day?)
3. We couldn't change the "extra" of a formset without redefining the
formset class. We wanted to do this because the required repeating
sections (employment history etc.) must not have a blank form in them if
they already have some valid forms, otherwise the blank forms prevent the
form being submitted because HTML5 client-side validation fails. So we had
to do all this:
def get_context_data(self, **kwargs):
...
for name, formset_class in self.formsets.iteritems():
# doesn't exist yet, so we can't call its queryset() method
queryset = formset_class.model._default_manager.filter(
**{formset_class.fk.name: application})
extra = 0 if queryset.exists() else 1
# need to reconstruct the formset class to change extra?
formset_class = inlineformset_factory(
Application,
formset_class.model,
formset_class.form,
formset_class,
extra=extra,
can_delete=formset_class.can_delete,
fields=formset_class.form.base_fields.keys(),
max_num=formset_class.max_num,
)
4. We needed to be able to save the form without validation. To do this,
we had to make all fields non-required on our model, and make them
required on the form instead. Since nearly all fields are required, we'd
have to redefine them all, making the ModelForm a bit pointless, but
instead we were naughty and looped over the fields setting their required
values to True, and also updating the widget (which gets a copy of this
attribute on constructions). Naughty but DRY. I feel that a shortcut to
list required fields of a ModelForm in its Meta class would be useful
(overriding the blank and null status of the underlying Model fields).
def _save_data_without_validation(self, form):
# construct a new form with the required value turned off on all
# fields, and use that to save the instance.
all_optional_form = self.get_form(
data=self.request.POST,
files=self.request.FILES,
instance=form.instance
)
for field in all_optional_form:
field.field.required = False
all_optional_form.full_clean()
5. We have to be able to save a new instance of the model even if form
validation fails. So both the form_valid and form_invalid methods of the
CreateView return redirects to the UpdateView for the newly saved
instance. But that makes the form load through GET instead of POST, and
thus it isn't validated. This is fine for saving the form, but not for
submitting it, so we have to validate it like this:
def get_form(self, data=None, files=None, **kwargs):
# If we've been redirected here because of a form error, then we
need
# to validate the form again. To do that, we need to generate a
# simulated "POST data dict" from the model data, and clean the
form,
# which we wouldn't normally do on a GET request.
# http://stackoverflow.com/a/8996585/648162
request_origin = self.request.GET.get('type')
if data is None and request_origin == 'submit':
data = model_to_dict(self.get_object())
form = super(ApplicationViewMixin, self).get_form(data, files,
**kwargs)
if request_origin == 'submit':
form.full_clean()
return form
And the same for all subforms (formsets). So this is a case where the
opposite would be useful - to disable the required status of all fields on
the form, without poking around in its internals.
6. Setting the HTML5 "required" attribute in the HTML of required
controls, *except* file fields that already have a file in them, because
you don't have to upload another file to make the underlying field valid
in this case. We've got it now, but it feels like Django ought to be doing
this. This is part of the request on
https://code.djangoproject.com/ticket/15924, which was closed as "too
broad", and the required attribute part of it never made it into a
separate ticket, but it seems like it might be useful.
7. Uploaded files appear to be lost if form validation fails, since the
model isn't saved.
Thanks in advance for your consideration.
Cheers, Chris.
--
Aptivate | http://www.aptivate.org | Phone: +44 1223 967 838
Citylife House, Sturton Street, Cambridge, CB1 2QF, UK
Aptivate is a not-for-profit company registered in England and Wales
with company number 04980791.
--
You received this message because you are subscribed to the Google Groups "Django
developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/alpine.OSX.2.00.1403181842410.13253%40chris-macbook.lan.
For more options, visit https://groups.google.com/d/optout.