Hey All,
Something I've regularly run into as a challenge is the implementation of
Django forms work well in the simple use cases, but as the use case grows
in complexity, Django forms become more difficult to work with.
I know that's a super general statement, but here's the simplest complex
example I can give you. Lets say you're making an application for a home
builder, so that their Project Managers can better coordinate the builds.
One of the features is the ability to take notes and pictures on anything
that's not yet done and specifically if it relates to a specific piece of
equipment (AC, furnace, water pump, etc), they can add that too. Below is a
moderately simplistic example:
class Note(models.Model):
project = models.ForeignKey('project_management.Project',
related_name="notes")
equipment = models.ForeignKey('equipment.Equipment', null=True,
blank=True, related_name="notes")
picture = models.FileField(null=True, blank=True)
is_blocker = models.BooleanField(default=True)
comment = models.TextField()
created_by = models.ForeignKey('users.User', verbose_name="Created By")
created_date = models.DateTimeField(default=timezone.now,
verbose_name="Created Date")
class NoteModalForm(forms.ModelForm):
class Meta:
fields = ('comment', 'is_blocker','equipment', 'picture')
model = Note
labels = {
'comment': 'Note',
'is_blocker': 'Blocking Issue',
'picture': 'Picture',
}
widgets = {
'picture': DragNDropFileButtonInput(button_text='Select file to
Upload',
button_classes='btn
btn-bordered uploader'),
}
General comments first:
1. I would say there's no better way to accomplish what is currently on
that form given the current Form Meta API. (Willing to be challenged on
this point, btw.)
2. The repetition of picture 3 times over (fields tuple, labels dict,
widgets, dict) seems to be inherently not DRY. If this gets very long, then
it becomes harder to manage.
3. The API on the Model Form itself behaves not quite like you'd expect
initially. You'd expect redeclaring fields directly on a form for it to
function like a dictionary update, if the value doesn't exist in the
incoming dictionary, it keeps what's there. It actually behaves like
re-declaration. This very significant behavior is buried in a note
(https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#overriding-the-default-fields).
Additionally, you'll have sources like pydanny basically tell you this is
an anti-pattern: https://www.pydanny.com/overloading-form-fields.html
4. The API on Meta leads you to believe initially that you can override
lots of things via Meta, and it's difficult to discover what is or is not
supported. (I usually dig into django.forms.models, and then wander around
until I get to ModelForm.)
- Here's the list: *model, fields, exclude, widgets,
localized_fields, labels, help_texts, error_messages, field_classes.*
- What's missing that's on a *default* field? *Required,
initial, show_hidden_initial, validators, disabled, label_suffix. *
- Anything not on this list you basically need to override in __init__
5. The Django Form API both does and does not care about the client side
implementation. I say it does care because it sets the default values in
the DOM if you use the form as recommended in the docs. However, there is
no recommended approach for anything beyond this.
Here are some simple scenarios that are made difficult:
Scenario #1
If I'd like on this form--and this form only--to make *picture* always
required or change the default value of *is_blocker, *we have to override
__init__. And generally it looks like this:
def __init__(self, *args, **kwargs):
super(NoteModalForm, self).__init__(*args, **kwargs)
self.fields['picture'].required = True
self.fields['is_blocker'].initial = False
Scenario #2
If I have the feature request that notes that are blockers require pictures
or notes that have equipment requires pictures, then you have to implement
this pattern:
https://docs.djangoproject.com/en/2.0/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
Oh, but wait! The best in class approach to this problem would be to inform
the user before they submit the form that this is a problem. This catches
it only on the server side. How should we implement that? You're on your
own to figure it out.
So in summary, I feel like this could be improved. And I'd like to take an
whack at it!
Robert Roskam
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/8fc29cb6-d059-4e49-bb76-309d0404ecd5%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.