pythondjangoformsdjango-forms

How to disable a field in crispy form django


I am using crispy form to let user create a new issue post, but I want user not change 'project' field', so that issues can be put under the right project. I used form.fields['project'].widget.attrs['readonly'] = True ,but this one only has styling disable effect, not really functionally.

models.py

def new_issue(request, project_id):
    if request.method == 'POST':
        form = IssueForm(request.POST)
        if form.is_valid():
            issue = form.save(commit=False)
            issue.author = request.user
            issue.save()
            return redirect('project:issue_tracker:issue_detail',project_id=project_id,issue_id=issue.id)
    else:
        form = IssueForm(initial={'project': project_id})
        form.fields['project'].widget.attrs['readonly'] = True
    template = 'issue_tracker/issue/new_issue.html'
    context = {'form': form,'project_id':project_id}
    return render(request, template, context)

form.py

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('body',)

class IssueForm(forms.ModelForm):
    class Meta:
        model = Issue
        fields = ('title','content','project','status')

class NewIssueForm(forms.ModelForm):
    class Meta:
        model = Issue
        fields = ('title','content','project','status')

new_issue.html

{% extends 'base.html' %}
{% load crispy_forms_tags %}

{% block content %}
    <h1>Add New Issue </h1>
    <form method="POST" class="Issue-form">{% csrf_token %}
        {{form|crispy}}
        <button type="submit" class="btn btn-success">Submit</button>
    </form>

{% endblock %}

Solution

  • Setting the value to readonly is not enough. This only has impact on how the HTML has been rendered. Depending on what the browser does in that case, it is possible that the user can still edit the form field.

    But nevertheless, even if the user can not edit the form, a person with some knowledge of how HTTP requests work, can still maliciously enter value in the request, and therefore thus change values in the database that are not supposed to get changed.

    and higher

    Since , you can alter the .disabled attribute on a form. In that case not only will this render the field as disabled or readonly, it will also prevent a person from maliciously entering values. If the user enters some data, that data is simply ignored (for that specific field). So we can write:

    def new_issue(request, project_id):
        if request.method == 'POST':
            form = IssueForm(request.POST, initial={'project': project_id})
            form.fields['project'].disabled = True
            if form.is_valid():
                issue = form.save(commit=False)
                issue.author = request.user
                issue.save()
                return redirect('project:issue_tracker:issue_detail',project_id=project_id,issue_id=issue.id)
        else:
            form = IssueForm(initial={'project': project_id})        
            form.fields['project'].disabled = True
        template = 'issue_tracker/issue/new_issue.html'
        context = {'form': form,'project_id':project_id}
        return render(request, template, context)

    Note that you will probably need some initial or an instance, since otherwise, there is no data at all to fallback on.

    Moving this into the form

    In the above code fragment, we wrote the same expression twice: each time we construct a form. This is against the DRY principle. Typically you specify the field, or - for example in the case the field is not always disabled, you can specify that by overriding the __init__ constructor.

    For example:

    class IssueForm(forms.ModelForm):
    
        def __int__(self, *args, disabled_project=True, **kwargs):
            super(IssueForm, self).__init__(*args, **kwargs)
            self.fields['project'].disabled = disabled_project
    
        class Meta:
            model = Issue
            fields = ('title','content','project','status')

    So now we can use a parameter to initialize the IssueForm, and specify if the disabled_project flag is True or False.

    and lower

    In case you work with older Django versions (not recommended, since there is no support anymore), we can still however ensure that the data is ignored, by adding a clean_somefield function. For example:

    class IssueForm(forms.ModelForm):
    
        def __int__(self, *args, **kwargs):
            super(IssueForm, self).__init__(*args, **kwargs)
            widget = self.fields['project'].widget
            widget.attrs['readonly'] = widget.attrs['disabled'] = widget.disabled = True
    
        def clean_project(self):
            return self.instance.project
    
        class Meta:
            model = Issue
            fields = ('title','content','project','status')