Django ModelForm user best practices

658 views
Skip to first unread message

RM

unread,
May 30, 2012, 3:45:42 PM5/30/12
to django...@googlegroups.com
Say there's a model:

class Notification(models.Model):
    user = models.ForeignKey(User)    
    board = models.ForeignKey(Board)
    post = models.ForeignKey(Post)    
    name = models.CharField()
    
    class Meta:
        unique_together = ('user', 'name', 'post', 'board')
        #i know this is funny.. just for purpose of this post

In every post there's a form that you can fill with "name" value.
This way the logged in user gets notification on every post update.

What is the best way to save the "user", "board", "post" data ?

1. Saving in the views directly (CBV):

class NotificationModelForm(forms.ModelForm):
    class Meta:
        model = Notification
        fields = ["name"]

class CreateNotificationView(CreateView):
    model = Notification
    form_class = NotificationForm
    def form_valid(self, form):
        data = form.save(commit=False)
        data.user = self.request.user
        data.board = ...
        data.post = ...
        data.save()

* if you have model validators (unique_together for user... this won't no longer work when submitting the form)

2. Displaying all the fields in the form, with some fields hidden

class NotificationModelForm(forms.ModelForm):
    class Meta:
        fields = ["user", "board", "post", "name"]

    def __init__(self, *args, **kwargs):
        super(NotificationModelForm, self).__init__(*args, **kwargs)
        for field in ["user", "board", "post"]:
            forms.field['field'].widget = forms.HiddenInput()
            #I know this can be done other ways too

Now we need to prepopulate fields with initial data for these fields

class CreateNotificationView(CreateView):
    model = Notification
    form_class = NotificationModelForm

    def get_initial(self):
        initial = super(CreateNotificationView, self).get_initial()
        initial['user'] = self.request.user
        initial['board'] = ...
        initial['post'] = ....
        return initial
  
If the same field pattern (user, board, post) is used in more forms/views... I can also create a MixinView

class CommonUserFormMixin(object):
    
    def get_form(self, form_class):
        form = super(CommonUserFormMixin, self).get_form(form_class)
        for field in ['user', 'board', 'post']:
            form.fields[field].widget = forms.HiddenInput()

Then:

class NotifcationModelForm(forms.ModelForm):
    class Meta:
        model = Notification
        fields = ["user", "board", "post", "name"]

class CreateNotificationView(CommonUserFormMixin, CreateView):
    form_class = NotificationModelForm
    model = Notification

* in 2 above scenarios we get model validators working.
3. Sending values to the form directly and overriding the form.save() method

class CreateNotificationView(CreateView):
    model = Notification
    form_class = NotificationModelForm

    def get_form_kwargs(**kwargs):
        kwargs = super(CreateNotificationView, self).get_form_kwargs(**kwargs)
        kwargs['user'] = self.request.user
        kwargs['board'] = ...
        kwargs['post'] = ...
        return kwargs

class NotificationModelForm(forms.ModelForm):
    class Meta:
        model = Notification
        fields = ["name"]

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        self.post = kwargs.pop('post')
        self.board = kwargs.pop('board')

        super(NotificationModelForm, self).__init__(*args, **kwargs)
        
    def save(*args, **kwargs):
        n = super(NotificationModelForm, self).save(commit=False)
        n.user = self.user
        n.post = self.post
        n.board = self.board
        n.save()
        return n


I'm sure there are more ways to achive the same result.
What are your thoughts on that ?

When should I 'hide' (by using HiddenInput() widget) ModelForm fields? when should I save them directly
in view? 

Does it all depend on needs or maybe there are some "best" standards ?
    

Kurtis Mullins

unread,
May 30, 2012, 3:55:46 PM5/30/12
to django...@googlegroups.com
I tend to put as much functionality in my forms as possible. I've
asked a similar question before (many months ago) and I believe that
was the consensus. One advantage is you can re-use your forms (and its
save functionality) for your Create and Update views.
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/django-users/-/xn6xNGKJ2CEJ.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-users?hl=en.

Alexandr Aibulatov

unread,
May 30, 2012, 3:56:15 PM5/30/12
to django...@googlegroups.com
I think that the first method is the most usefull. On second method
some experience users can override hidden data. And it's a bad idea to
override __init__ and save method in my humble opinion/

2012/5/31 RM <rmir...@gmail.com>:

Kurtis Mullins

unread,
May 30, 2012, 4:10:12 PM5/30/12
to django...@googlegroups.com
> On second method some experience users can
> override hidden data

For the second method, you'd just use -- class Meta: fields =
('board', 'post', 'name') to prohbit anyone from trying to override
the 'user', if that's what you're talking about.

> And it's a bad idea to
> override __init__ and save method in my humble opinion/

What better purpose for the save() method than to save? :) I'm not
sure why it's a bad idea to override __init__, though. Feel free to
elaborate. I'm always open for new information!

Alexandr Aibulatov

unread,
May 30, 2012, 4:25:35 PM5/30/12
to django...@googlegroups.com
For example on second method i can change post, board id's via html,
and write to board where i can be banned.
I think we shoudn't override standart methods if we con don't override
them(this about third method)

P.S. sorry for my terrible english.

2012/5/31 Kurtis Mullins <kurtis....@gmail.com>:
> --
> You received this message because you are subscribed to the Google Groups "Django users" group.

RM

unread,
May 31, 2012, 2:51:39 AM5/31/12
to django...@googlegroups.com
Good point. You can display the value but again.. you need to make sure it's a proper (not changed via post) value (clean() / save())

Peter of the Norse

unread,
Jun 2, 2012, 3:03:34 PM6/2/12
to django...@googlegroups.com
https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form has a highlighted note about this very issue. The way they recommend is: (based on #1 below)

    initial_notification = Notification(user=self.request.user, board=..., post=...)
    form = NotificationModelForm(self.request.POST, initial=initial_notification)
    if form.is_valid():
        form.save()

This should be the solution you’re looking for.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/xn6xNGKJ2CEJ.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Peter of the Norse



Reply all
Reply to author
Forward
0 new messages