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 %}
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.
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.
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
.
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')