Anton Danilchenko
Anton Danilchenko

Reputation:

Set field value in Django Form clean() method, if this field not passed in constructor

I need set field value, not passed to Django Form constructor.

I have model and form like this:

class Message(models.Model):
    created     = models.DateTimeField()
    text        = models.CharField(max_length=200, blank=True, null=True)
    active      = models.BooleanField(default=False)

class MessageForm(forms.ModelForm):
    class Meta:
        model   = Message
        exclude = ('created', 'active')

    def clean(self):
        # check if user is blocked
        if user.is_admin():
            self.cleaned_data['active'] = True
        return self.cleaned_data

Expected: if current user is admin - I need automatically set message as active. User should not pass this parameter by form.

Actual: I see that saved message always have flag "False" (I can delete condition and in this case I also see that message is not active).

Please help me understand, how can I do set this "active" flag in clean() method.

Upvotes: 5

Views: 12739

Answers (3)

zsquare
zsquare

Reputation: 10146

The previous answer would work, but I like encapsulating all the form's internal operations like what to show and what not, within the form. I know you mentioned you don't want to send a field value to the constructor, but if you don't mind sending the user, your solution would work. i.e., your constructor:

def __init__(self, user):
    self.user = user
    super(BaseForm, self).__init__()

then in your clean, you just change the user to self.user. There is another added benefit to this. Say tomorrow you want to assign more fields based on your user, you don't need to add anything to the views, you can simply add it to the form.

EDIT: When you add a field to exclude, it is not available in the cleaned data. Instead, set its widget as hidden.

active = forms.BooleanField(widget=forms.HiddenInput)

EDIT 2: If you really don't want the field in the form In this case, instead of overriding the clean, why don't you override the save?

def save (self):
    super(BaseForm, self).save()
    if user.is_admin():
        self.instance.active=True
    super(BaseForm, self).save()

Upvotes: 5

Tomasz Zieliński
Tomasz Zieliński

Reputation: 16377

Use this:

def message_form_factory(user):
    class MessageForm(forms.ModelForm):
        def clean(self):
            # check if user is blocked
            if user.is_admin():
                self.cleaned_data['active'] = True
            return self.cleaned_data
    return MessageForm

And in your view use:

form = message_form_factory(request.user)()
form = message_form_factory(request.user)(request.POST)

Upvotes: 1

chefsmart
chefsmart

Reputation: 6991

Don't do this in the form's clean() method, do this in the view.

def your_view(request):
    if request.method == 'POST':
        form = MessageForm(data=request.POST)
        if form.is_valid():
            new_message = form.save(commit=False)
            if user.is_admin():
                new_message.active = True

However, if you also want to handle the case where your user is not admin using the same form, you can look at incorporating similar logic in the form's init() instead of the view, probably by passing info about the user from the view to the form's init()

Upvotes: 3

Related Questions