Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 %}
like image 602
Sue-May Xu Avatar asked Jan 02 '23 06:01

Sue-May Xu


1 Answers

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.

django-1.9 and higher

Since djang-1.9, 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.

django-1.8 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')
like image 148
Willem Van Onsem Avatar answered Jan 05 '23 14:01

Willem Van Onsem