how do I add template conditions in the ListView class? - django

I have a ListView class in views.py, I want to add a condition if the authenticated user displays another template
urls.py
from django.urls import path, include
from django.contrib.auth import views as auth_views
from .views import (
PostListView,
)
urlpatterns = [
path('', PostListView.as_view(), name='index'),
]
Views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import (
ListView,
)
from .models import Post
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
class PostListView(ListView):
model = Post
template_name = 'page/index.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 7
i want to add
if self.request.user.is_authenticated:
template_name = 'page/index.html'
else:
template_name = 'page/home.html'
Django 2.2.x

You can override the get_template_names function [Django-doc]:
class PostListView(ListView):
model = Post
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 7
def get_template_names(self):
if self.request.user.is_authenticated:
return ['page/index.html']
else:
return ['page/home.html']
As the documentation says, this function:
Returns a list of template names to search for when rendering the template. The first template that is found will be used.
If template_name is specified, the default implementation will return a list containing template_name (if it is specified).
That being said, if you do not plan to render the list on your home.html page, it might be better to perform a redirect to another page, instead of just rendering a page. Otherwise, if you later want to add more content to your home.html page, you will each time need to update all the views that render this.
The basic implementation [GitHub] in the TemplateResponseMixin [Django-doc] is thus:
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if render_to_response() is overridden.
"""
if self.template_name is None:
raise ImproperlyConfigured(
"TemplateResponseMixin requires either a definition of "
"'template_name' or an implementation of 'get_template_names()'")
else:
return [self.template_name]

Related

How to properly implement Django form session wizard

I've been attempting to construct a multi-step form using the Django session wizard for hours, but I keep getting the error, AttributeError: 'function' object has no property 'as_view'. I'm not sure why this mistake occurred. Any ideas?
views
from django.shortcuts import render
from formtools.wizard.views import SessionWizardView
from .forms import WithdrawForm1, WithdrawForm2
class WithdrawWizard(SessionWizardView):
template_name = 'withdraw.html'
form_list = [WithdrawForm1, WithdrawForm2]
def done(self, form_list, **kwargs):
form_data = [form.cleaned_data for form in form_list]
return render(self.request, 'done.html', {'data': form_data})
forms
from django import forms
from .models import Investment, Withdraw
from .models import WithdrawStepOne, WithdrawStepTwo
class WithdrawForm1(forms.ModelForm):
class Meta:
model = WithdrawStepOne
fields = ['investment_id',]
class WithdrawForm2(forms.ModelForm):
class Meta:
model = WithdrawStepTwo
fields = [
'proof_of_address',
'user_pic'
]
urls
from django.urls import path
from .forms import WithdrawForm1, WithdrawForm2
from . import views
urlpatterns = [
path('withdraw/', views.WithdrawWizard.as_view(), name='withdraw'),
]
You used #login_required decorator on WithdrawWizard class, but the decorator works only for function based views.
Use LoginRequiredMixin for class based views.

Reverse for 'cbvdetail' not found. 'cbvdetail' is not a valid view function or pattern name

i can't call my detail class using reverse_lazy
from django.shortcuts import render, redirect
from django.urls import reverse_lazy, reverse
from . models import Task
from . forms import Taskform
from django.views.generic import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView
class Tasklistview(ListView):
model = Task
template_name = 'home.html'
context_object_name = 'task'
class Detailview(DetailView):
model=Task
template_name = "details.html"
context_object_name = 'task'
class Updateview(UpdateView):
model = Task
template_name = "update.html"
context_object_name = "task"
fields = ('name', 'priority', 'date')
def get_success_url(self):
return reverse_lazy("cbvdetail",kwargs={'pk':self.object.id})
urls.py
from django.urls import path
from . import views
app_name='todoapp'
urlpatterns = [
path('',views.home,name='home'),
# path('details', views.details,name='ere')
path('delete/<int:id>/',views.delete,name='delete'),
path('edit/<int:id>/',views.update,name='update'),
path('cbvhome/',views.Tasklistview.as_view(),name='home'),
path('cbvdetail/<int:pk>/',views.Detailview.as_view(),name='cbvdetail'),
path('cbvupdate/<int:pk>/',views.Updateview.as_view(),name='edit'),
]
i want to resolve this
You specified an app_name in the urls.py file. That means you need to prefix the name of the view with that app label, so:
def get_success_url(self):
return reverse_lazy('todoapp:cbvdetail', kwargs={'pk':self.object.id})
If you override get_success_url, it does not make much sense to work with reverse_lazy, since that method will (normally) only be triggered in case the urls are already loaded, you can thus work with:
from django.urls import reverse
# &vellip;
def get_success_url(self):
return reverse('todoapp:cbvdetail', kwargs={'pk':self.object.id})

Method Not Allowed (POST): /cbvdelete/5/ Method Not Allowed: /cbvdelete/5/

i am not able to run this code
viwes.py
from django.shortcuts import render, redirect
from django.urls import reverse_lazy, reverse
from . models import Task
from . forms import Taskform
from django.views.generic import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView,DeleteView
class Tasklistview(ListView):
model = Task
template_name = 'home.html'
context_object_name = 'task'
class Detailview(DetailView):
model=Task
template_name = "details.html"
context_object_name = 'task'
class Updateview(UpdateView):
model = Task
template_name = "update.html"
context_object_name = "task"
fields = ('name', 'priority', 'date')
def get_success_url(self):
return reverse_lazy('todoapp:cbvdetail',kwargs={'pk':self.object.id})
class Deleteview(DetailView):
model = Task
template_name = 'delete.html'
success_url = reverse_lazy('todoapp:home')
urls.py
from django.urls import path
from . import views
app_name='todoapp'
urlpatterns = [
path('',views.home,name='home'),
path('delete/<int:id>/',views.delete,name='delete'),
path('edit/<int:id>/',views.update,name='update'),
path('cbvhome/',views.Tasklistview.as_view(),name='home'),
path('cbvdetail/<int:pk>/',views.Detailview.as_view(),name='cbvdetail'),
path('cbvupdate/<int:pk>/',views.Updateview.as_view(),name='edit'),
]
when i run this code i am getting a error this page isn't working right now
i am not able to run this code
gngnitgbnugriujvnnvtvnviuvntnvtvitu
You are inheriting you Deleteview from a DetailView, not a DeleteView, hence the error:
from django.views.generic import DeleteView
class Deleteview(DeleteView):
model = Task
template_name = 'delete.html'
success_url = reverse_lazy('todoapp:home')
I would however strongly advise not to give your views names like Deleteview, since it is easy to confuse this with the Django builtin class-based views. Usually the model is specified in the name, so TaskDeleteView instead of Deleteview:
class TaskDeleteView(DeleteView):
model = Task
template_name = 'delete.html'
success_url = reverse_lazy('todoapp:home')

Django - Sending emails using class based view

I have been making a website containing a listview and formview. I have a form.py which looks like this :
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
message = forms.CharField(required=True)
and the views.py :
from django.shortcuts import render
from .forms import ContactForm
from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from blog.models import Post
from django.core.mail import EmailMessage
class PostListAndFormView(FormMixin,ListView):
queryset = Post.objects.all().order_by("-date")[:2]
template_name = 'personal/index.html'
form_class = ContactForm
success_url = 'personal/index.html'
This view also deals with a Listview functionality which you can ignore.
My HTML template is:
<form action="/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="SEND MESSAGE" />
</form>
Finally urls.py :
from django.conf.urls import url,include
from . import views
from homepage.views import PostListAndFormView
urlpatterns = [
url(r'^$', PostListAndFormView.as_view(), name='PostListAndFormView'),
]
What I want to do here is take the data from the form and send an email to my address with the entered data. Someone help me do this.
Don't know why you mixed up listview with formview. From your model it's kind a blog app. Normally in a blog app the articles/posts list page doesn't contain any form with email field, except for article detail or Contact Us page. I prefer to split it into different class than using mixin.
My answer based on Daniel Greenfeld's example here with little bit modification.
In yourapp/forms.py:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
import floppyforms as forms
class ContactForm(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
subject = forms.CharField(required=True)
message = forms.CharField(widget=forms.Textarea)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.add_input(Submit('submit', 'Submit'))
super(ContactForm, self).__init__(*args, **kwargs)
In yourapp/views.py:
from django.views.generic.list import ListView
from django.views.generic.edit import FormView
from django.conf import settings
from django.core.mail import send_mail
from yourapp.models import Post
from yourapp.forms import ContactForm
class PostListView(ListView):
model = Post
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['latest_articles'] = Post.objects.all()[:5]
return context
class ContactFormView(FormView):
form_class = ContactForm
template_name = "myapp/email_form.html"
success_url = '/email-sent/'
# this is what you want
def form_valid(self, form):
message = "{name} / {email} said: ".format(
name=form.cleaned_data.get('name'),
email=form.cleaned_data.get('email'))
message += "\n\n{0}".format(form.cleaned_data.get('message'))
send_mail(
subject=form.cleaned_data.get('subject').strip(),
message=message,
from_email='contact-form#myapp.com',
recipient_list=[settings.LIST_OF_EMAIL_RECIPIENTS],
)
return super(ContactFormView, self).form_valid(form)
Have a look at the documentation, check the methods of the FormMixin and pick the one that fits.
form_valid() might be an option.
in python3:
def form_valid(self, form):
# do your stuff
# call the parents class method
return super().form_valid(form)
This is a quite common approach when using class based views. They usually provide a bunch of attributes and methods - sometimes it is enough to just change some attributes, which change the behavior of the methods (e.g. success_url). But often you will need to override some of there methods. It is always a good idea to read their code in this case, cause you need to decide whether you want to call the mixins implementation at the beginning, at the end (as in the example) or if you really want to override it and provide their logic on your own...

Django Template Context Processor with Class Based Views

I have a Login Class Based View which I want to include in all my pages.
Is there a way to include Class Based View as template context processor, or do we have any other way to include in all pages ?
class LoginView(RedirectAuthenticatedUserMixin, FormView):
form_class = LoginForm
template_name = "account/login.html"
success_url = None
redirect_field_name = "next"
def form_valid(self, form):
success_url = self.get_success_url()
return form.login(self.request, redirect_url=success_url)
def get_success_url(self):
# Explicitly passed ?next= URL takes precedence
ret = (get_next_redirect_url(self.request,
self.redirect_field_name)
or self.success_url)
return ret
def get_context_data(self, **kwargs):
ret = super(LoginView, self).get_context_data(**kwargs)
signup_url = passthrough_next_redirect_url(self.request,
reverse("account_signup"),
self.redirect_field_name)
redirect_field_value = self.request.REQUEST \
.get(self.redirect_field_name)
ret.update({"signup_url": signup_url,
"site": Site.objects.get_current(),
"redirect_field_name": self.redirect_field_name,
"redirect_field_value": redirect_field_value})
return ret
login = LoginView.as_view()
You can decorate the view:
Either in urls.py:
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView
from .views import VoteView
urlpatterns = patterns('',
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
)
Or in views.py:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
Or, you can inherit from LoginRequiredMixin from django-braces:
from braces.views import LoginRequiredMixin
class Index(LoginRequiredMixin, TemplateView):
template_name = 'sekret.html'
All these examples show how to require authentication for class based views (which I believe is what you are trying to do). The first two are directly from the documentation.
Once you implement either of the above examples, your application will behave like this:
A user enters a URL that maps to one of the above views.
django middleware checks if the user is logged in.
If not, django will redirect to LOGIN_URL, by default its /accounts/login/, and add a ?next parameter, pointing to the original URL from #1.
The view from #3 will display a login form.
Once the user successfully authenticates, the view from #3 will update the session, and redirect the user to the URL from the next parameter.
If the user is logged in, then the decorated view will be executed as normal.