Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Forms: Hidden model field?

Tags:

django

I've got a Form. I want to include a hidden field that returns a model. I'll set it's value in the view; I just need it to be posted along to the next page.

What field am I supposed to use in the form class?

like image 943
mpen Avatar asked Jan 14 '11 00:01

mpen


People also ask

What is forms Hiddeninput?

Definition and Usage. The <input type="hidden"> defines a hidden input field. A hidden field lets web developers include data that cannot be seen or modified by users when a form is submitted.

What is UserCreationForm in django?

Django UserCreationForm is used for creating a new user that can use our web application. It has three fields: username, password1, and password2(which is basically used for password confirmation). To use the UserCreationForm, we need to import it from django.


2 Answers

A hidden field that returns a model? So a model instance ID?

The forms.HiddenInput widget should do the trick, whether on a FK field or CharField you put a model instance ID in.

class MyForm(forms.Form):
    hidden_2 = forms.CharField(widget=forms.HiddenInput())
    hidden_css = forms.CharField(widget=forms.MostWidgets(attrs={'style': 'display:none;'}))

I suppose the fastest way to get this working is

class MyForm(forms.Form):
    model_instance = forms.ModelChoiceField(queryset=MyModel.objects.all(), widget=forms.HiddenInput())

form = MyForm({'model_instance': '1'})
form.cleaned_data['model_instance']

But I don't like the idea of supplying MyModel.objects.all() if you're going to specify one item anyways.

It seems like to avoid that behavior, you'd have to override the form __init__ with a smaller QuerySet.

I think I prefer the old fashioned way:

class MyForm(forms.Form):
    model_instance = forms.CharField(widget=forms.HiddenInput())

    def clean_model_instance(self):
        data = self.cleaned_data['model_instance']
        if not data:
            raise forms.ValidationError()
        try:
            instance = MyModel.objects.get(id=data)
        except MyModel.DoesNotExist:
            raise forms.ValidationError()
        return instance
like image 84
Yuji 'Tomita' Tomita Avatar answered Oct 27 '22 07:10

Yuji 'Tomita' Tomita


The approach in Yuji's answer uses a clean_model_instance method on the form which is fine if you're only ever doing this once in your code base. If you do it more often, then you might benefit from implementing a custom model field.

This is the code I have:

from django import forms

class ModelField(forms.Field):

    Model = None

    def prepare_value(self, value):
        """Inject entities' id value into the form's html data"""
        if isinstance(value, self.Model):
            return value.id
        return value

    def to_python(self, value):
        """More or less stolen from ModelChoiceField.to_python"""

        if value in self.empty_values:
            return None

        try:
            value = self.Model.objects.get(id=value)
        except (ValueError, self.Model.DoesNotExist):
            raise forms.ValidationError('%s does not exist'
                                         % self.Model.__class__.__name__.capitalize())

        return value

If you use that as a base class and then specialise it with your own models then it becomes a useful based. For example:

# In app/fields.py
from .models import CustomModel

class CustomModelField(ModelField):

    Model = CustomModel

Then you can pair that with whatever widget you need at the time:

# in app/forms.py
class MyForm(forms.Form):

    hidden_custom_model_field = CustomModelField(widget=forms.HiddenInput())

    other_widget_custom_model_field = CustomModelField(widget=MyCustomWidget())
like image 31
MichaelJones Avatar answered Oct 27 '22 06:10

MichaelJones