If you are as lazy as i am, you wouldn’t like writing the same codes over and over again for every project you work on.

When working on django-cms, a great way to save time is to use django-cms apphooks (after all one of django’s features is ‘being reusable’).

One of my most used self created plugin would have to be my django-login, so i would love to show you all how i built it.

This plugin will work more like an apphook for a django-cms page.

Lets begin!!

First we create a fresh django-cms project

$ pip install --upgrade virtualenv

$ virtualenv env

$ source env/bin/activate(mac)

env\scripts\activate(windows)

(env) $ pip install djangocms-installer

(env) $ djangocms mysite

Next we create our app

cd mysite

$ python manage.py startapp django_login

Now run the server and see your site

$ python manage.py runserver 8080

We haven't done anything yet!!!

Open your project in a text editor, my favorite text editor for python project is pycharm, but you can use any one you like. Your project folder structure should look like this



django_login/ # the newly-created application

migrations/

__init__.py

admin.py

models.py

tests.py

views.py

media/

mysite/

static/

manage.py

project.db

requirements.txt

Include the django_login app inside mysite/settings.py.

INSTALLED_APPS = [

'mysite’,

'django.contrib.admin’,

'django.contrib.auth’,

...

'django_login’, ]

Now, we start working on our app,

import django.contrib.auth.views in your mysite/urls.py

from django.contrib.auth import views as auth_views

We want the login page to open when user goes to /login. To use the login view append the following urlpatterns in mysite/urls.py

url(r'^en/login/$', auth_views.login, {'template_name': 'registration/login.html'})

The view by default renders a template that will be in registration/login.html.

Add a folder to your django_login and name is templates, add another folder inside templates, name it registration, now add an html page named login.html.

We will be using a beautiful login template from codepen

Our registration/login.html includes the following simple HTML form:

<!DOCTYPE html>

<html>

<head>

<title>Login</title>

</head>

<body>

<div class="login-page">

<div class="form">

<form method="POST" class="login-form">

{% csrf_token %}

<input type="text" name="username" placeholder="username"/>

<input type="password" name="password" placeholder="password"/>

<button type="submit">login</button>

<p class="message">Not registered? <a href="/register/">Create an account</a></p>

</form>

</div>

</div>

</body>



<style>@import url(https://fonts.googleapis.com/css?family=Roboto:300);



.login-page {

width: 360px;

padding: 8% 0 0;

margin: auto;

}

.form {

position: relative;

z-index: 1;

background: #FFFFFF;

max-width: 360px;

margin: 0 auto 100px;

padding: 45px;

text-align: center;

box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);

}

.form input {

font-family: "Roboto", sans-serif;

outline: 0;

background: #f2f2f2;

width: 100%;

border: 0;

margin: 0 0 15px;

padding: 15px;

box-sizing: border-box;

font-size: 14px;

}

.form button {

font-family: "Roboto", sans-serif;

text-transform: uppercase;

outline: 0;

background: #4CAF50;

width: 100%;

border: 0;

padding: 15px;

color: #FFFFFF;

font-size: 14px;

-webkit-transition: all 0.3 ease;

transition: all 0.3 ease;

cursor: pointer;

}

.form button:hover,.form button:active,.form button:focus {

background: #43A047;

}

.form .message {

margin: 15px 0 0;

color: #b3b3b3;

font-size: 12px;

}

.form .message a {

color: #4CAF50;

text-decoration: none;

}

.form .register-form {

display: none;

}

.container {

position: relative;

z-index: 1;

max-width: 300px;

margin: 0 auto;

}

.container:before, .container:after {

content: "";

display: block;

clear: both;

}

.container .info {

margin: 50px auto;

text-align: center;

}

.container .info h1 {

margin: 0 0 15px;

padding: 0;

font-size: 36px;

font-weight: 300;

color: #1a1a1a;

}

.container .info span {

color: #4d4d4d;

font-size: 12px;

}

.container .info span a {

color: #000000;

text-decoration: none;

}

.container .info span .fa {

color: #EF3B3A;

}

body {

background: #76b852; /* fallback for old browsers */

background: -webkit-linear-gradient(right, #76b852, #8DC26F);

background: -moz-linear-gradient(right, #76b852, #8DC26F);

background: -o-linear-gradient(right, #76b852, #8DC26F);

background: linear-gradient(to left, #76b852, #8DC26F);

font-family: "Roboto", sans-serif;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}</style>





</html>

Please note that you can separate the css in a different file and link it.

After the user has logged in, we can define where the page should be redirected by specifying LOGIN_REDIRECT_URL in mysite/settings.py. By default, we will be redirected to /login if the login fails.

LOGIN_REDIRECT_URL = '/'

To handle logout, we add the url to mysite/urls.py urlptterns and create the template.

url(r'^logout/$', auth_views.logout)

The logout view renders the registration/logged_out.html template by default. Here is a our logout template:

<!DOCTYPE html>

<html>

<head>

<title></title>

</head>

<body>

You have successfully logged out.

<a href="/">Home</a>

</body>

</html>

As with login, you can change the template location by including an object with a ‘template_name’ key and the template’s location as the value.

We want our users to register for our website by navigating to /register. Before doing that, let’s clean up the project a bit. Append the following in mysite/urls.py

url(r'^', include('django_login.urls'))

Now we need to include the urlpattern for / in django_login/urls.py, so include the following urlpattern in it (after importing the relevant libraries)

from django.conf.urls import url, include

from django.contrib import admin

from .views import home, register urlpatterns = [



url(r'^register/', register),

]

register refers to the view for handling registration. For creating a user registration form, we will use Django’s in built forms. To do this, create a django_login/forms.py file and include the following:

from django import forms class UserRegistrationForm(forms.Form):

username = forms.CharField(

required = True,

label = 'Username',

max_length = 32

)

email = forms.CharField(

required = True,

label = 'Email',

max_length = 32,

)

password = forms.CharField(

required = True,

label = 'Password',

max_length = 32,

widget = forms.PasswordInput()

)

We want users to be able to view the form if they go to /register, as well as fill it in and submit it. These correspond to GET and POST requests on /register. Thus, we include the following in django_login/views.py:

from django.shortcuts import render

from django.contrib.auth.models import User

from django.contrib.auth import authenticate, login

from django.http import HttpResponseRedirect

from django import forms

from .forms import UserRegistrationForm

# Create your views here.



def register(request):

if request.method == 'POST':

form = UserRegistrationForm(request.POST)

if form.is_valid():

userObj = form.cleaned_data

username = userObj['username']

email = userObj['email']

password = userObj['password']

if not (User.objects.filter(username=username).exists() or User.objects.filter(email=email).exists()):

User.objects.create_user(username, email, password)

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

login(request, user)

return HttpResponseRedirect('/')

else:

raise forms.ValidationError('Looks like a username with that email or password already exists')

else:

form = UserRegistrationForm()



return render(request, 'django_login/register.html', {'form' : form})

For the register view, we check whether the request method is POST or not. If it isn’t, then we specify the form to be UserRegistrationForm and render, it by passing it as a parameter to django_login/register.html template(don’t forget to create the file in the correct directory):

<!DOCTYPE html>

<html>

<head>

<title>Login</title>

</head>

<body>

<div class="login-page">

<div class="form">

<form method="POST" class="register-form">

{% csrf_token %} {{ form.as_p }}



<button>create</button>

<p class="message">Already registered? <a href="/login/">Sign In</a></p>

</form>

</div>

</div>

</body>



<style>@import url(https://fonts.googleapis.com/css?family=Roboto:300);



.login-page {

width: 360px;

padding: 8% 0 0;

margin: auto;

}

.form {

position: relative;

z-index: 1;

background: #FFFFFF;

max-width: 360px;

margin: 0 auto 100px;

padding: 45px;

text-align: center;

box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);

}

.form input {

font-family: "Roboto", sans-serif;

outline: 0;

background: #f2f2f2;

width: 100%;

border: 0;

margin: 0 0 15px;

padding: 15px;

box-sizing: border-box;

font-size: 14px;

}

.form button {

font-family: "Roboto", sans-serif;

text-transform: uppercase;

outline: 0;

background: #4CAF50;

width: 100%;

border: 0;

padding: 15px;

color: #FFFFFF;

font-size: 14px;

-webkit-transition: all 0.3 ease;

transition: all 0.3 ease;

cursor: pointer;

}

.form button:hover,.form button:active,.form button:focus {

background: #43A047;

}

.form .message {

margin: 15px 0 0;

color: #b3b3b3;

font-size: 12px;

}

.form .message a {

color: #4CAF50;

text-decoration: none;

}

.form .register-form {

display: true;

}

.container {

position: relative;

z-index: 1;

max-width: 300px;

margin: 0 auto;

}

.container:before, .container:after {

content: "";

display: block;

clear: both;

}

.container .info {

margin: 50px auto;

text-align: center;

}

.container .info h1 {

margin: 0 0 15px;

padding: 0;

font-size: 36px;

font-weight: 300;

color: #1a1a1a;

}

.container .info span {

color: #4d4d4d;

font-size: 12px;

}

.container .info span a {

color: #000000;

text-decoration: none;

}

.container .info span .fa {

color: #EF3B3A;

}

body {

background: #76b852; /* fallback for old browsers */

background: -webkit-linear-gradient(right, #76b852, #8DC26F);

background: -moz-linear-gradient(right, #76b852, #8DC26F);

background: -o-linear-gradient(right, #76b852, #8DC26F);

background: linear-gradient(to left, #76b852, #8DC26F);

font-family: "Roboto", sans-serif;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}</style>



<script>

$('.message a').click(function(){

$('form').animate({height: "toggle", opacity: "toggle"}, "slow");

});

</script>



</html>

Now we include a CMSApp sub-class, which tells the CMS how to include that application.

Apphooks live in a file called cms_apps.py , so create one in your django_login i.e django_login/cms_apps.py:

from cms.app_base import CMSApp

from cms.apphook_pool import apphook_pool

from django.utils.translation import ugettext_lazy as _





class django_login(CMSApp):

app_name = "django_login"

name = _("LOGIN")



def get_urls(self, page=None, language=None, **kwargs):

return ["django_login.urls"]





apphook_pool.register(django_login) # register the application

Hurray!!!, We are done with our apphook, now lets test it.

Note:

Your apphook won’t work until the page has been published.

You can get the complete work here