Vexed by csrf_token -- though it's there - django

I keep getting a verification failed missing csrf_token. I'm very puzzeled/stuck on this. as you can see by my code below it's in fact there. Any help will be very much appreciated.
Django==1.6.1 btw
MIDDLEWARE_CLASSES = (.... 'django.middleware.csrf.CsrfViewMiddleware', ...)
class BaseSignUp(View):
model = None
form = None
template = None
def get(self, request):
return render_to_response(self.template,
{'user_form': UserCreateForm, 'form': self.form})
def post(self, request):
user = UserCreateForm(request.POST)
obj = self.form(request.POST)
if user.is_valid():
user.save()
else:
return render_to_response(self.template, {'user_form': user, 'form': obj})
<form method="post">
{% csrf_token %}
<table>
{{ user_form }}
{{ form }}
</table>
<button type="submit">Save</button>
</form>

Found the answer here Django - {% csrf_token %} was used in a template, but the context did not provide the value
I was missing the context_instance=RequestContext(request) in the post/get methods in the veiw
Lesson learned: just user render.

Related

Problems with Django when trying to show error message when form insn't valid

Description
In my application i have a user registration form, but, when simulating not following the recommendations for form validation, i get an httpresponse error. But, what i'm trying to do is display an error message using the message framework from django
Code
My template
{% block body %}
{% if messages %}
{% for message in messages %}
<div class="text-center alert alert-{{ message.tags }}">
{{ message|safe }}
</div>
{% endfor %}
{% endif %}
{% load crispy_forms_tags %}
<form class = "" method="post" action="{% url 'registro' %}">
{% csrf_token %}
{{registro_form|crispy}}
<button class = "btn btn-primary" type="submit">Registrar</button>
</form>
{% endblock body %}
My Form
class NovoUsuarioForm(UserCreationForm):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
def save(self, commit = True):
user = super(NovoUsuarioForm, self).save(commit=False)
user.username = self.cleaned_data['first_name']
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user
My View
class RegistroView(generic.View):
def get(self,request, *args, **kwargs):
return render(request, 'main/registro.html', {'registro_form': NovoUsuarioForm()})
def post(self,request, *args, **kwargs):
form = NovoUsuarioForm(request.POST)
if form.is_valid():
user = form.save()
login(request,user)
messages.success(request, "Registration successful." )
return HttpResponseRedirect(reverse('pesquisa'))
messages.error(request, "Unsuccessful registration. Invalid information.")
Other Info
When i go back to the form page, from the error page after the post method, i get the error message
Your view does not return an HTTP response if the form is NOT valid; the view does not have a return for that case, so it returns None.
This is the overview for your code:
class RegistroView(generic.View):
...
def post(self,request, *args, **kwargs):
...
if form.is_valid():
...
return HttpResponseRedirect(reverse('pesquisa'))
...
I suggest adding something like return HttpResponse(...) for the case when the form is not valid. The exact think you return depends on your use case.

Django ModelForm form.is_valid is false but with no form.errors

I have problems with Django ModelForm, I have the situation where form.is_valid() = false but with no form.errors. That's why I'm getting no updates in the database.
This is what I have:
#views.py
def blog_update(request, slug):
obj = get_object_or_404(BlogPost, slug=slug)
form = BlogModelForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
form = BlogModelForm()
template_name = 'form.html'
context = {"form": form, "title": f"Update {obj.title}"}
return render(request, template_name, context)
# forms.py
class BlogModelForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title', 'slug', 'content']
# form.html
{% extends 'base.html' %}
{% block content %}
<form method="POST" action="."> {% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
{% endblock %}
I hope you can help me to find where the problem is. Thanks in advance
To get the form errors you need to check if the form is valid first. See below:
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
form = BlogModelForm()
else:
print(form.errors)
I found the problem, It was a tiny thing I wasn't seeing.
I has just to change form.html
Where I had
<form method="POST" action='.'>
I has just to take off the period to
<form method="POST" action=''>
And ready! Thanks a lot for your time!

Django dropdown form submission invalid

Hey i am trying to use modelchoicefield to get a dropdown list in html. But the submission of form yields a invalid form. My code is given below.
views.py
class SubjectSelectFormView(View):
form_class = SubjectSelectForm
template_name = 'study/select_subject.html'
def get(self, request):
form = self.form_class(user=request.user)
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
subject = models.Subject.objects.get(name=form['name'])
return HttpResponseRedirect('study:quiz', subject.subject_id)
else:
return HttpResponse('<h1>Failed</h1>')
forms.py
class SubjectSelectForm(forms.Form):
name = forms.ModelChoiceField(queryset=Subject.objects.all().order_by('name'), widget=forms.Select())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(SubjectSelectForm,self).__init__(*args, **kwargs)
self.fields['name'].queryset = Subject.objects.filter(user_id=user)
html
{% extends 'basic_home_app/base.html' %}
{% block content %}
<br>
<form class="form-horizontal" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Start">
</form>
{% endblock %}
First you should always render the same template with the bound form when a posted form is found to not be valid, this way you can display errors to the user:
def post(self, request):
form = ...
if form.is_valid():
...
else:
return render(request, self.template_name, {'form':form})
Inside your template, you can display errors using either:
{{ form.errors }} # all form errors
{{ form.non_field_errors }} # form errors that aren't for one specific field, use this if you're displaying the field errors separately
or
{{ form.name.errors }} # just the errors for one specific field
Second, I assume you want to initialise your form the same way when it's posted as when it's first displayed (empty) to the user via the get() request:
def post(self, request):
form = self.form_class(request.POST, user=request.user) # note the user
Otherwise your form.__init__() method will set as queryset only Subject objects where user_id is None.

Django Template Form Render

My problem is not to show django form fields on template.It's silly but I just haven't found any solution.
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'text']
def __init__(self, content_type, id, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.content_type = content_type
self.id = id
def save(self, commit=True):
post_type = ContentType.objects.get_for_model(Post)
comment_type = ContentType.objects.get_for_model(Comment)
comment = super(CommentForm, self).save(commit=False)
if self.content_type == 'post':
comment.content_type = post_type
comment.post = self.id
else:
parent = Comment.objects.get(id=self.id)
comment.content_type = comment_type
comment.post = parent.post
comment.object_id = self.id
if commit:
comment.save()
return comment
my view:
def add_comment(request, content_type, id):
if request.method == 'POST':
data = request.POST.copy()
form = CommentForm(content_type, id, data)
if form.is_valid():
form.save()
return redirect(reverse('index'))
my add_comment template:
<form method="post" action="{% url 'add_comment' 'post' post.id %}">
{% csrf_token %}
{% if not user.is_authenticated %}
{{ form.name.label_tag }}
{{ form.name }}
{{ form.email.label_tag }}
{{ form.email }}
{% endif %}
{{ form.text.label_tag }}
{{ form.text }}<br>
<input type="submit" value="Comment" />
</form>
and I included like:
<button id="button" type="button">Add Comment</button>
<div id="post_comment_form">{% include 'articles/add_comment.html' %}</div>
</article> <!-- .post.hentry -->
why not django rendered form fields,despite of showing buttons?
EDIT:
I'm rendering form in post view.
def post(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post.id)
return render(request,
'articles/post.html',
{'post': post,
'form': CommentForm,
'comments': comments,
# 'child_comments': child_comments
}
)
You forgot to instantiate the form, change this line:
'form': CommentForm,
to this
'form': CommentForm(),
In your view, you're not sending any context variables to the template, so your 'form' object isn't available for your template to process.
For example, the following return statement will render your .html and pass along all local variables, this isn't necessarily the best option (how much do you want your template to have access to), but is simple:
from django.shortcuts import render
...
return render(request, "template.html", locals())
you can also pass a dictionary instead of all local variables. Here's the documentation for render

Is it possible to have a form in a ListView template?

I built a listview which works fine and gives me exactly what I want.
In the template of this ListView I declared a form that points to a CreateView.
The form is like so,
{% if user.is_authenticated %}
<form action="{% url 'post_wall' %}" method="POST">
{% csrf_token %}
<input type='text' name='body' />
<input type='hidden' name='from_user' value='{{ user.id }}' />
<input type='hidden' name='to_user' value='{{ to_user }}' />
<input type='submit' value='POST'/>
</form>
{% endif %}
the post_wall url corresponds to
url(r'accounts/post_wall', WallCreate.as_view(), name='post_wall'),
The url which contains the form is
url(r'accounts/wall/(?P<slug>\w+)/$', WallList.as_view(), name='wall'),
This calls the CreateView,
class WallCreate(CreateView):
model = WallPost
def get_success_url(self):
url = reverse('wall', kwargs={'slug': request.POST.to_user})
return HttpResponseRedirect(url)
This gives me a
TemplateDoesNotExist at /accounts/post_wall
users/wallpost_form.html
Shouldn't this be working properly as a post is sent to a CreateView? Or have I misunderstood something about CBVs?
Yes, but all the form process will have to be made by the ListView itself. That is simple, considering you can inherit the behaviour from ModelFormMixin. You will only need one url (to the list view). The template will look like:
{% if user.is_authenticated %}
<form action="" method="POST">
{% csrf_token %}
{{ form }}
<input type='submit' value='POST'/>
</form>
{% endif %}
And your view:
from django.views.generic.list import ListView
from django.views.generic.edit import ModelFormMixin
class ListWithForm(ListView, ModelFormMixin):
model = MyModel
form_class = MyModelForm
def get(self, request, *args, **kwargs):
self.object = None
self.form = self.get_form(self.form_class)
# Explicitly states what get to call:
return ListView.get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
# When the form is submitted, it will enter here
self.object = None
self.form = self.get_form(self.form_class)
if self.form.is_valid():
self.object = self.form.save()
# Here ou may consider creating a new instance of form_class(),
# so that the form will come clean.
# Whether the form validates or not, the view will be rendered by get()
return self.get(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
# Just include the form
context = super(ListWithForm, self).get_context_data(*args, **kwargs)
context['form'] = self.form
return context