Friday 24 June 2016

Two views inside one

It was some thing new for me- implementing two views inside one. In the administrator app in VMS, I used two views for report generation: one to display the form and other one to list the volunteer details. The following is the sample code that use single view that calls another two views.

class ShowReportListView(LoginRequiredMixin, ListView):
    template_name = "administrator/report.html"    
    organization_list = get_organizations_ordered_by_name()
    event_list = get_events_ordered_by_name()
    job_list = get_jobs_ordered_by_title()

    def post(self, request, *args, **kwargs):
        report_list = get_administrator_report(
            self.request.POST['first_name'],
            self.request.POST['last_name'],
            self.request.POST['organization'],
            self.request.POST['event_name'],
            self.request.POST['job_name'],
            self.request.POST['start_date'],
            self.request.POST['end_date'],
        )
        organization = self.request.POST['organization']
        event_name = self.request.POST['event_name']
        total_hours = calculate_total_report_hours(report_list)
        return render(request, 'administrator/report.html',
                      {'report_list': report_list, 
                      'total_hours': total_hours, 
                      'notification': True,
                      'organization_list': self.organization_list,                         
                      'selected_organization': organization,
                      'event_list': self.event_list,                                      
                      'selected_event': event_name, 
                      'job_list': self.job_list})

class GenerateReportView(LoginRequiredMixin, View):

    def get(self, request, *args, **kwargs):
        view = ShowFormView.as_view()
        return view(request, *args,**kwargs)

    def post(self, request, *args, **kwargs):
        view = ShowReportListView.as_view()

GenerateReportView calls ShowFormView that displays the form and ShowReportListView that list the volunteers.


Tuesday 7 June 2016

Django's authentication system

Django's authentication system can serve the most common needs like handling wide range of tasks, implementation of passwords and permissions etc.

My second module of the GSoC project is the migration of authentication app to class based views. Authentication app used function based view for login and logout process earlier which was bit lengthy. The following was the function based view used for authentication purpose earlier.


auhentication/views.py
from authentication.forms import AuthenticationForm
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, user_passes_test
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render

def index(request):
    return HttpResponse("Hello world")
    
def login_process(request):

    if request.method == 'POST':

        authentication_form = AuthenticationForm(request.POST)

        if authentication_form.is_valid():
            username = request.POST.get('username')
            password = request.POST.get('password')

            user = authenticate(username=username, password=password)

            if user:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(reverse('home:index'))
                else:
                    return HttpResponse("Your account is disabled.")
            else:
                return render(request, 'authentication/login.html', {'authentication_form' : authentication_form, 'is_invalid_credentials' : True,})
        else:
            return render(request, 'authentication/login.html', {'authentication_form' : authentication_form,})
    else:
        return render(request, 'authentication/login.html', {'is_invalid_credentials' : False,})

@login_requireddef logout_process(request):

    logout(request)
    return HttpResponseRedirect(reverse('home:index')
authentication/urls.py
from django.conf.urls import patterns, url
from authentication import views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index'),
    url(r'^login/$', views.login_process, name='login_process'),
    url(r'^logout/$', views.logout_process, name='logout_process'),
)
It can be written in a simpler way using Django's built in auth views. The rewritten code for authentication is as follows.
Create a views.py in vms

vms/views.py
from django.shortcuts import redirect
from django.views.generic.edit import FormView
from django.core.mail import send_mail
from django.core.mail.message import BadHeaderError
from django.http.response import HttpResponse

from vms import settings

def anonymous_required(func):
    def as_view(request, *args, **kwargs):
        redirect_to = kwargs.get('next', settings.LOGIN_REDIRECT_URL )
        if request.user.is_authenticated():
            return redirect(redirect_to)
        response = func(request, *args, **kwargs)
        return response
    return as_view
anonymous_required is the conventional decorator that allows the programmer to restrict access to some views only to logged in users




vms/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
#from importlib._bootstrap import _NamespaceLoaderfrom vms.views import anonymous_required
from django.contrib.auth.decorators import login_required
import registration.views as views
from django.contrib.auth import views as auth_views

#admin.autodiscover()
urlpatterns = patterns('',
    url(r'^portal', TemplateView.as_view(template_name='home/home.html'),name='home'),
    url(r'^login/$',
        anonymous_required(auth_views.login),
        {'template_name': 'authentication/login.html'},
        name='login_process'),
    url(r'^user/logout/$',
        auth_views.logout,
        {'template_name': 'home/home.html'},
        name='logout_process'),
)
Assuming that you are done setting up built-in Django user authentication, add this in your settings.py. NOTE: 'home' here is the URL of the homepage. It will redirect the user to homepage once he is logged in. It is up to you what to replace it with.
vms/settings.py
LOGIN_URL = reverse_lazy('auth:user_login')
LOGIN_REDIRECT_URL = 'home'