FormView with get method doesn't show the form - django

Thanks for read :/
When I remove the "get" from the view, the form works, but when I put it back, it doesn't render.
URL at the browser:
http://127.0.0.1:8000/activate?tpr=1140277&idpr=42
URLs:
url(r'^activate', Activation_vw.as_view(), name='activate')
VIEW:
class Activation_vw(FormView):
template_name = 'activate.html'
form_class = Activation_Form
success_url = '/dashboard/'
def get(self, request, *args, **kwargs):
tokenProspect_v = request.GET.get('tpr')
idProspect_v = request.GET.get('idpr')
USER = USERS.objects.filter(
id=idProspect_v).values('id', 'email', 'token')
if int(tokenProspect_v) != int(USER[0]['token']):
message = "Check the URL"
else:
message = USER[0]['email']
context = {'msg': message}
return self.render_to_response(context)
def form_valid(self, form):
# No code yet
return super(Activation, self).form_valid(form)
FORM:
class Activation_Form(forms.Form):
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput())
TEMPLATE:
Hello, {{ msg }}
<form action="" method="POST">
{%csrf_token%}
{{ form.as_p }}
<button type="submit">Activate</button>
</form>
All of them have the imports at the top of each file.
The get function works perfectly, I receive the tpr and the idpr but the form doesn't and because of that the form form_valid and the success_url doesn't work neither.
I suspect something is wrong in the return of my get, but can't figured out.

It is because your form is not being passed in the context:
context = {'msg': message}
return self.render_to_response(context)
You can use get_context_data to get the context and update it as you go:
context = self.get_context_data(msg=message)
return self.render_to_response(context)
This will call the get_context_data from the super class and include the form to the context along with adding the message.

Related

how to have multiple forms in a class based view

I have a simple blog app that you can post news and blog on it. I wanted to show the latest posts on the main page, so I created this class like this:
class MainListView(TemplateView):
template_name = 'Blog/Home.html'
def get_context_data(self, **kwargs):
context = super(MainListView, self).get_context_data(**kwargs)
context['news'] = NEWS.objects.order_by('-published')[:2]
context['posts'] = POSTS.objects.order_by('-published')[:3]
return context
and it works great. however, I wanted to add two additional forms to the main page too. so I added these two functions to the class:
def get(self, request, *args, **kwargs):
return self.render_to_response({'aform': HomeGholakForm(prefix='aform_pre'), 'bform':
HomeContactForm(prefix='bform_pre')})
def post(self, request, *args, **kwargs):
aform = _get_form(request, HomeGholakForm, 'aform_pre')
bform = _get_form(request, HomeContactForm, 'bform_pre')
if aform.is_bound and aform.is_valid():
aform.save()
return redirect('Home-Page')
elif bform.is_bound and bform.is_valid():
bform.save()
return redirect('Home-Page')
return self.render_to_response({'aform': aform, 'bform': bform})
and above this class I created a _get_form function in order for it to work:
def _get_form(request, formcls, prefix):
data = request.POST if prefix in request.POST else None
return formcls(data, prefix=prefix)
in the final stage I added the forms to my template like this:
<form action="">
{% csrf_token %}
{{bform.email}}
<input type="submit" name="{{bform.prefix}}" class="btn" />
</form>
<form action="">
{% csrf_token %}
{{aform.info}}
<input type="submit" name="{{aform.prefix}}" class="btn" />
</form>
After I did this, the forms both work fine, but the latest blog and news are not shown. what am I supposed to do?
get_context_data is normally called by the get or post method whichever is used (only get in TemplateView) and passed as the context while rendering. You override these methods but never call get_context_data. Change your get and post methods like so:
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context.update({'aform': HomeGholakForm(prefix='aform_pre'), 'bform': HomeContactForm(prefix='bform_pre')})
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
aform = _get_form(request, HomeGholakForm, 'aform_pre')
bform = _get_form(request, HomeContactForm, 'bform_pre')
if aform.is_bound and aform.is_valid():
aform.save()
return redirect('Home-Page')
elif bform.is_bound and bform.is_valid():
bform.save()
return redirect('Home-Page')
context = self.get_context_data(**kwargs)
context.update({'aform': aform, 'bform': bform})
return self.render_to_response(context)
You should consider making some Mixin along the lines of FormMixin which would make your work more easier.

Pass variable using class view in django

Directly how can I pass the context in the same time inside the class to my html pages?
the class is an updateView but I need the context in the page too:
class GL_PRUP(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Gas_liftM
template_name = 'Home/WELLINFO/W_lift/GL_liftUPARMTS.html'
form_class = Gas_liftFORM
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
WeelN = self.get_object()
GLParameters = Wcomlption.objects.filter(WellID__exact=WeelN)[0]
print(GLParameters)
context={'GLParameters': GLParameters,} # need to pass this variable to my html page?
if self.request.user== WeelN.author:
return (True, context)
else:
return False
Every thing works fine in the update html page but only can't get the GLParameters??
{% if GLParameters %}
{{ GLParameters.WellID }} it works Tyaeb {{ GLParameters.Mndrn2 }}
{% else %}
nothing passes and happens
{% endif %}
Thank you in advance
UpdateView inherits from multiple mixins and one of them is SingleObjectMixin that inherits from ContextMixin and thus you can override get_context_data method in order to extend the context with what you need.
Should be something like (untested):
...
def get_context_data(self, **kwargs):
context = super().context(**kwargs)
WeelN = self.get_object()
GLParameters = Wcomlption.objects.filter(WellID__exact=WeelN).first()
context.update(
{
'GLParameters': GLParameters,
'author': self.request.user == WeelN.author
}
)
return context

Django: Page not found (404) when posting form data

I am trying to create a form to submit a blog post on an author detail page, so that the blog post will automatically use the current author as its "blog_author" foreign key. I'm aware that this approach isn't "secure" - it's a project site, and I'm trying to learn a new design pattern.
The Django docs recommended using 1 parent view and 2 subviews to handle get and post respectively (https://docs.djangoproject.com/en/3.0/topics/class-based-views/mixins/).
The page renders fine with the get, but the post gives me an error reading "Page not found (404) - no blog post found matching the query." The exception is raised by my parent view (blog.views.AuthorDetail), but there is no traceback.
Edit: Form should have been a ModelForm from the beginning
Here are my views:
class BlogAuthorDetailView(generic.DetailView):
model = BlogAuthor
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = BlogSubmitForm()
return context
class BlogSubmit(SingleObjectMixin, FormView):
template_name = 'blogauthor_detail.html'
form_class = BlogSubmitForm
model = BlogPost
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
#Should I be overriding form_valid() to use the line above? Not sure if I'm doing my data
#handling in the right place
return super().post(request, *args, **kwargs)
def form_valid(self, form):
blogpost = form.save(commit=False)
blogpost.blog_author = self.object
blogpost.save()
return redirect('blog_author-detail', pk=self.object.id)
class AuthorDetail(View):
def get(self, request, *args, **kwargs):
view = BlogAuthorDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = BlogSubmit.as_view()
return view(request, *args, **kwargs)
URLs:
urlpatterns = [
path('', views.index, name='index'),
path('blogs/', views.BlogPostListView.as_view(), name='blogs'),
path('blog/<int:pk>', views.BlogPostDetailView.as_view(), name='blogpost-detail'),
path('bloggers/', views.BlogAuthorListView.as_view(), name='bloggers'),
path('blogger/<int:pk>', views.AuthorDetail.as_view(), name='blog_author-detail'),
path('blog/<int:pk>/create', views.BlogCommentCreate.as_view(), name='comment_create')
]
the template:
{% extends "base_generic.html" %}
{% block content %}
<h1>Title: {{ blogauthor.title }}</h1>
<p><strong>Author:</strong> {{ blogauthor }}</p>
<p><strong>Biography:</strong> {{ blogauthor.biography }}</p>
<p><strong>User:</strong> {{ blogauthor.user }}</p>
<p><strong>Posts:</strong>
{% for blog in blogauthor.blogpost_set.all %}
<p> {{ blog.title }} </p>
{% endfor %} </p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
<div style="margin-left:20px;margin-top:20px">
<h4>Comments: Coming Soon!</h4>
{% endblock %}
Model:
class BlogPost(models.Model):
date_created = models.DateField(blank=False, default = date.today)
blog_author = models.ForeignKey('BlogAuthor', on_delete = models.SET_NULL, null=True)
title = models.TextField(max_length=70)
content = models.TextField(max_length=400, null=False)
class Meta:
ordering = ['date_created']
def get_absolute_url(self):
"""Returns the url to access a particular blog post instance."""
return reverse('blogpost-detail', args=[str(self.id)])
def __str__(self):
return self.title
And the forms.py:
class BlogSubmitForm(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea(attrs={'cols': 40, 'rows': 8}))
date_created = forms.DateField()
At this point, I suspect that the problem is related to my redirect() call in the form_valid override.
The things I have tried include:
Changing the form’s action from blank to the same URL as in my URL paths (possible I did this wrong)
Changing the code in form_valid() to read form.instance.blog_author = self.object (same exact error message, so I don’t think it’s this)
Fiddling with the form_valid()’s redirect call, including: using self.object instead or a URL, using a hardcoded url, getting rid of the second argument, and changing the 2nd arg to pk=, slug=.
Adding a get_success_url override (don’t really know why this would work)
edit: one of the excepted post calls that showed up in my local server went to blog/blogger/4, which is the url I want. Not sure what the issue is.
This is confusing on how you are using the template. Anyway, I think the simplest solution here is to get the BlogAuthor data from request.user and that is most logical, otherwise, anyone can post anything from another user as long as they can predict their primary key(which is a security hole). Here is how you can try:
from django.contrib.auth.mixins import LoginRequiredMixin
class BlogSubmit(LoginRequiredMixin, CreateView):
template_name = 'blogauthor_detail.html'
form_class = BlogSubmitForm
model = BlogPost
def get_success_url(self):
return reverse('blog_author-detail', pk=self.object.id)
def form_valid(self, form):
form.blog_author = self.request.user.blogauthor # assuming BlogAuthor has OneToOne relation with User
return super(BlogSubmit, self).form_valid(form)
Update
Purpose of FormView is to collect data from Forms, where CreateView is to store and create a new instance. Anyway, you need to change your code like this to make it work:
class BlogSubmit(LoginRequiredMixin, SingleObjectMixin, FormView):
template_name = 'blogauthor_detail.html'
form_class = BlogSubmitForm
model = BlogAuthor
def get_success_url(self):
return reverse('blog_author-detail', pk=self.object.id)
def form_valid(self, form):
self.object = self.get_object()
form.blog_author = self.object
form.save()
return super(BlogSubmit, self).form_valid(form)
Also update the form:
class BlogSubmitForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title', 'date_created', 'content']
FYI, to make SingleObjectMixin work, you need to change the model from BlogPost to BlogAuthor

Django - ModelChoiceField validation

I have a simple form witch ModelChoiceField. This is a part of my view.py file:
def premium(request, id):
context = {}
try:
site = Site.objects.get(id=id)
except Site.DoesNotExist:
raise Http404("Nie ma takiej strony")
if request.method == 'POST':
premium_form = PremiumForm(request.POST)
if premium_form.is_valid():
# group = Group.objects.get(id=request.POST["kod"])
print('OK')
else:
print('NOT OK')
else:
premium_form = PremiumForm(site)
premium_form.fields['group'].queryset =
premium_form.fields['group'].queryset.exclude(group_name=site.group)
context['site'] = site
context['form'] = premium_form
context['category'] = site.category
context['subcategory'] = site.subcategory
return render(request, 'mainapp/premium.html', context)
This is my form:
class PremiumForm(forms.Form):
def __init__(self, site, *args, **kwargs):
super(PremiumForm, self).__init__(*args, **kwargs)
self.fields['group'].initial = 2
self.fields['group'].empty_label = None
group = forms.ModelChoiceField(
queryset=Group.objects.filter(is_active=True),
help_text="<div id='group'></div>",
label="Some text",
required=False)
My premium.html file:
<form method="post" action="" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form form layout='horizontal'%} <br>
{% bootstrap_button "Submit" size='large' button_type="submit" button_class="btn-primary btn-main-add" %}
</form>
When I press "Submit" button I get "NOT OK". I can't resolve this problem. I don't have any idea how to validate forms.ModelChoiceField. Thanks for any help.
Form should be initialised with kwargs:
premium_form = PremiumForm(site=site)
And inside init:
def __init__(self, *args, **kwargs):
site = kwargs['site']
However, site is not used inside form initialization so you can just remove it and it will solve the issue.

How to pass bound form as context to TemplateView via HttpResponseRedirect

I'm trying to put multiple account management forms on the one page with a TemplateView as follows:
class AccountManagement(TemplateView):
""" Generic view to display the account management template """
template_name = 'accountmanagement.html'
def get_context_data(self, **kwargs):
context = super(AccountManagement, self).get_context_data(**kwargs)
context['user'] = self.request.user
# pass unbound form instances to context
# if there aren't bound instances already there
if context.get('usercreate_form') is None:
context['usercreate_form'] = UserCreateForm()
if context.get('password_form') is None:
context['password_form'] = PasswordChangeForm()
return context
I'm handling UserCreation with a FormView (because this example is simplified; I also need some non-model data, and CreateView needs a ModelForm). This view processes the POST request, and is supposed to redirect to the TemplateView with a success message, or pass the invalid bound form back to the context so that the template can render the errors. Trouble is, it doesn't do the part in bold italics (obviously HttpResponseRedirect doesn't pass the context). Why? How can I get the bound form back into the TemplateView context here so that the form.errors will be available and the user doesn't have to retype the data?
class UserCreate(FormView):
"""
Generic view to create a User.
"""
form_class = UserCreateForm
http_method_names = ['post',]
success_url = reverse_lazy('accountmanagement')
failure_url = reverse_lazy('accountmanagement')
def form_valid(self, form):
#password1 == password2 as per UserCreateForm.clean()
try:
new_user = User.objects.create_user(
username=form.cleaned_data['email'],
first_name=form.cleaned_data['first_name'],
last_name=form.cleaned_data['last_name'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password1']
)
new_user.save()
messages.success(self.request, new_user.username + str(_(": successfully saved.") ))
return HttpResponseRedirect(self.success_url)
except IntegrityError:
#duplicate username
messages.error(self.request, _("Duplicate email address."))
return HttpResponseRedirect(self.failure_url, {'usercreate_form': form})
def form_invalid(self, form):
messages.error(self.request, _("Unable to create user."))
return HttpResponseRedirect(self.failure_url, {'usercreate_form': form})
template:
<form method="post" action="{% url 'usercreate' %}">{% csrf_token %}
{{ usercreate_form.errors }}
{{ usercreate_form.as_p }}
<button type="submit">{% trans 'Save' %}</button>
Final related question: is the right way to put several related forms on the one page a TemplateView? And then process non-model forms using a POST-only FormView with a redirect back to the TemplateView? Or should I do this a different way?