I personally use the (undocumented?) formfield() method, which takes the
model defaults and lets you override things. I think it's pretty elegant,
though maybe it could use some less verbose syntax (maybe have a
forms.ModelDefault(label='Note') that does this for you).

class NoteModalForm(forms.ModelForm):
    comment = Note._meta.get_field('comment').formfield(label='Note')
    is_blocker = Note._meta.get_field('is_blocker').formfield(label='Blocking
Issue', initial=False)
    picture = Note._meta.get_field('picture').formfield(label='Picture',
required=True, widget=DragNDropFileButtonInput(button_text='Select file to
Upload', button_classes='btn btn-bordered uploader'))

    class Meta:
        model = Note
        fields = ['comment', 'is_blocker', 'equipment', 'picture']

I should also note, you can change fields without overriding __init__; just
use base_fields:
NoteModalForm.base_fields['picture'].required = True
NoteModalForm.base_fields['is_blocker'].initial = False

As far as client-side validation goes, yes, Django really only does
client-side validation that's available from plain html. Do you have some
ideas for how dependent fields should work? I personally think it would be
hard to find a one-size-fits all solution for more complicated cases, but
it's probably possible. I think there are some 3rd party libraries that do
this. I think a first step would be to natively handle dependent-fields
programmatically in the back-end, so the form "knows" that those fields are
related. Then maybe django could pass that relationship information to the
html as data-* attributes.

On Wed, Jan 31, 2018 at 10:31 AM, Robert Roskam <raiderrob...@gmail.com>
wrote:

> 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
>    
> <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 django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> 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
> <https://groups.google.com/d/msgid/django-developers/8fc29cb6-d059-4e49-bb76-309d0404ecd5%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
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/CAFO84S5QEgN8tnP2Xk%2BS6UxeNqjNdZ%2B13_J5rRnsDY9g4j88Rw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to