Using Django Model Primary Key in Custom Forms THE RIGHT WAY

OWASP Django form
Image: OWASP Django form (License: CC-BY-SA Marcelo Canina)

Use PK in Django Custom forms safely

Published: Tag: django

Overview

If for some reason you aren't using a ModelForm directly but you need to create a Django form and include a model instance primary key in it to later processing it at a POST request, then you need to make sure it can't be edited.

Short answer: Use forms.fields.Field.disabled.

Using the PK in a form

Suppose you have a form where you get a model instance passed as a parameter (sentence_main) and in its init initalize a new form field:

from django import forms

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
        """Generate a field for each text's sentence"""
        sentence_main = kwargs.pop('sentence_main')
        super().__init__(*args, **kwargs)
        
        self.fields["sentencemain"] = forms.CharField(
            required=False,
            label=sentence_main.display(),
            initial=sentence_main.display(), #"initiallll", #unit.literal_translation(text_l1),
        )

Hidden PK field form

To also have the primary key of this model instance in the form, we include it using a hidden field, with the forms.HiddenInput widget.

A HiddenInput field is a special Input class with input_type = 'hidden'.

Then in our forms.py:

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
	    ...
        #build the fields
        self.fields["sentencemain_pk"] = forms.IntegerField(
	        ...
            widget=forms.HiddenInput()
        )

Avoid PK edit in POST

Finally to avoid Web Parameter Tampering, we make sure the primary key can't be modified in a POST request.

This is a very important step often omitted in tutorials to include a primary key in a form, allowing malicious users to alter the primary key processed in a POST request.

To avoid the user altering this field, we use Django's disabled argument.

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
	    ...
        #build the fields
        self.fields["sentencemain_pk"] = forms.IntegerField(
	        ...
            disabled = True,  #it won’t be editable by users
            widget=forms.HiddenInput()
        )

And we can trust in the primary key passed in the form when processing the POST request in a view.

References

Uruguay
Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus


How to use a Primary Key from a model instance in a Django Form without the possibility of being altered in a POST request

Clutter-free software concepts.
Translations English Español
·