Introduction
In this article, we will build a note project to cover the major scope and aspects of building real-world projects using Django.
Create a virtual environment
A virtual environment is a tool that helps store dependencies for a specific Django application, not on a global scope on your laptop or computer.
#if you have python installed properly, pip should be available automatically
#pip is a python package manager/ installer
pipenv shell
#the command above should create a virtual environment if ran on your terminal in the project path
Installing Django
pipenv install django
After successfully running the command above a Pipfile
and Pipefile.lock
file will be created automatically.
-
Pipfile
Pipfile is the file used by the Pipenv virtual environment to manage project dependencies and keep track of each dependency version.
-
Pipfile.lock
Pipfile.lock
leverages the security of package hash validation in pip. This guarantees you're installing the same packages on any network as the one where the lock file was last updated, even on untrusted networks.
Create note models
from django.db import models
#use django user user model
from django.contrib.auth.models import User
# Create your models here.
#Note model
class Note(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
body = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
# ordering notes in list view with the just created or updated ones
class Meta:
ordering = ['-updated', '-created']
# if a note object is called 50 characters of the title will shown
def __str__(self):
return self.title[0:50]
Add Django auth URLs
Since we will not be creating custom views authentication add the code below to your project my_project
URL file
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('my_app.urls')),
#Django auth default url
path('', include('django.contrib.auth.urls')),
]
Create migration
The command below creates a file which will have a database schema based on the models in the models.py
file in the migration folder.
python manage.py makemigrations
#This should be the response diplayed after running it on the terminal
#Migrations for 'my_app':
#my_app\migrations\0001_initial.py
#- Create model Note
Create database
To create the project database run the command below. This command also creates necessary tables like notes
and schemas like user
, sessions
etc.
python manage.py migrate
#and you can run the server
python manage.py runserver
Create a superuser
In a Django project, there are superusers and normal users and the major difference between them is that superusers have access and permission to the project admin page to manage the project's models and data.
python manage.py createsuperuser
Add notes model to Django admin
To access your notes models and play around it using the Create, Read, Update, Delete (crud) functions on the Django admin, the notes model needs to be registered on the admin.py
file.
from django.contrib import admin
from .models import Note
# Register your models here.
admin.site.register(Note)
To have a view of the Django admin page make sure the server is up and running and open this link on the browser http://127.0.0.1:8000/admin/. You can only log in with a superuser account.
The CRUD functions can now be done on the Notes
models.
Create custom application interfaces and functions
Instead of using the Django admin page, a custom interface and functions for the project will be created.
-
Create forms(
forms.py
)The file
forms.py
will be created customarily in the application foldermy_app
from django import forms from .models import Note #create a form based on the Note model class noteForm(forms.ModelForm): class Meta: model = Note #form fields/inputs that will be shown fields = ['title', 'body'] #added a css class and html placeholder to the form inputs #the form-control css class is from bootstrap widgets={ 'title':forms.TextInput(attrs={'class':'form-control', 'placeholder':'Note title'}), 'body':forms.Textarea(attrs={'class':'form-control', 'placeholder':'Start writing...'}), }
-
Create the project functions(
views.py
)
from django.shortcuts import render, redirect from .models import Note #import the noteform from .forms import noteForm from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User # Create your views here. #login_required makes sure a user is authenticated before a view #get all the notes of the currently logged in user @login_required(login_url='login') def myNotesList(request): context = {} notes = Note.objects.filter(user=request.user) context['notes'] = notes return render(request, 'note-list.html', context) #add a new note @login_required(login_url='login') def addNote(request): context = {} form = noteForm if request.method == 'POST': note = Note.objects.create( user = request.user, title = request.POST.get('title'), body = request.POST.get('body') ) return redirect('view-note', pk=note.pk) context['form'] = form return render(request, 'add-note.html', context) #edit a specific note @login_required(login_url='login') def editNote(request, pk): note = Note.objects.get(pk = pk) if request.user.id == note.user.id: form = noteForm(instance=note) if request.method == 'POST': form = noteForm(request.POST, instance=note) if form.is_valid(): form.save() return redirect('view-note', pk=note.pk) context = {'form':form} return render(request, 'edit-note.html', context) return redirect('notes-list') #view a specific note @login_required(login_url='login') def viewNote(request, pk): context = {} note = Note.objects.get(pk = pk) if request.user.id == note.user.id: context['note'] = note return render(request, 'view-note.html', context) return redirect('notes-list') #delete a specific note @login_required(login_url='login') def deleteNote(request, pk): note = Note.objects.get(pk=pk) if request.user.id == note.user.id: note.delete() return redirect('notes-list') return redirect('notes-list') ##delete the logged in user account @login_required(login_url='login') def deleteUser(request): user = request.user user.delete() return redirect('login')
-
Create URLs patterns
#urls.py from django.urls import path from . import views urlpatterns =[ path('', views.myNotesList, name='notes-list'), path('add-note/', views.addNote, name='add-note'), path('edit-note/<str:pk>/', views.editNote, name='edit-note'), path('view-note/<str:pk>/', views.viewNote, name='view-note'), path('delete-note/<str:pk>/', views.deleteNote, name='delete-note'), path('delete-user/', views.deleteUser, name='delete-user'), ]
Add the Login and Logout Redirect URL
This piece of code is to be in the
settings.py
and its purpose is to redirect a user to a certain URL/page after logging in or logging out.
#settings.py import os #where your static files will be located e.g css, ja and images STATIC_ROOT = os.path.join(BASE_DIR, 'static') LOGIN_REDIRECT_URL = "notes-list" #we can access the login url through the Django defauly auth urls LOGOUT_REDIRECT_URL = "login"
Create template and static files(
.html
and.css
)This should be an overview of how the static and template files should be arranged.
-
-
Create
styles.css
fileThis is the file where we write the stylings of the app(CSS).
-
Create a
static
folder in the application foldermy_app
-
Inside the
static
folder create acss
folder-
Then create the
styles.css
file inside thecss folder
.
* { margin: 0; padding: 0; } body { font-family: "Montserrat", sans-serif; background-color: #1f2124; color: white; } .fa.fa-google { margin-right: 7px; } .page-header { text-align: center; font-size: 30px; font-weight: 600; color: orange; } .login-page-container { margin-top: 40px; } .login-container { border: 3px solid #252629; border-radius: 8px; margin-left: auto; margin-right: auto; margin-top: 50px; width: 30%; height: 40vh; } .login-container > p { text-align: center; font-size: 22px; margin-top: 15px; } .login-btn { margin-left: auto; margin-right: auto; display: block; width: 65%; margin-top: 70px; } .login-btn:hover { background-color: #252629; } .notes-container { margin-left: auto; margin-right: auto; margin-top: 60px; display: block; border: 3px solid #252629; width: 500px; border-radius: 8px; } .note-title { margin-left: 15px; font-size: 18px; padding: 15px; color: white; text-decoration: none; } .note-title > a { margin-left: 15px; font-size: 18px; color: white; text-decoration: none; } .note-title > a:hover { margin-left: 10px; color: orange; text-decoration: none; } .note-header { margin-top: 20px; padding-left: 30px; padding-right: 50px; color: orange; border-bottom: 3px solid orange; } .note-header > a { text-decoration: none; color: orange; } .note-logo { margin-right: 15px; } .note-count { font-size: 19px; float: right; padding-right: 10px; } .note-created { display: flex; justify-content: right; align-items: center; font-size: 10px; } .note-list { border-bottom: 1px solid #252629; height: 60px; } .note-list:hover { background-color: #252629; } #add-note-icon { display: flex; justify-content: right; align-items: center; font-size: 30px; } label { display: none; } .a-note-title { color: orange; text-align: center; padding: 10px; } .btn.btn-secondary { justify-content: right; align-items: center; background-color: rgba(255, 166, 0, 0.576); } #back-to-notes-list { font-size: 30px; /* padding: 8px; */ } .note-detail-header { padding-left: 10px; padding-right: 10px; } .icons > a { color: white; text-decoration: none; } .note-detail-header > a { color: white; text-decoration: none; } .icons { display: flex; align-items: center; justify-content: right; } #edit-note { font-size: 20px; } #delete-note { font-size: 20px; } .note-body { padding: 10px; margin-left: 7px; margin-right: 7px; display: block; } .markdown-support-note { margin-top: 10px; color: orange; text-align: center; } .dev-info { display: flex; justify-content: space-between; align-items: center; margin-top: 100px; font-size: 25px; color: orange; } .dev-info > span > a { text-decoration: underline !important; color: orange !important; } .dev-info > a > i { font-size: 40px; color: orange; } @media all and (min-width: 100px) and (max-width: 600px) { .notes-container { width: 100%; } .login-container { width: 100%; } }
-
-
-
-
Create the custom views template
Create a
template
folder in the application foldermy_app
and-
Create
registration
folder-
Create
login.html
file
{% extends 'base.html'%} {% load static %} <title>{% block title %}Sign In || My Note App{% endblock %}</title> {% block content %} {% if user.is_authenticated %} <p></p> {% else %} <div class="login-page-container"> <h3 class="page-header" >Sign In</h3> <div class="login-container"> <p>My Notes App</p> {% if not user.is_authenticated %} {% if messages %} {% for message in messages %} <div class="text-center alert alert-{{ message.tags }}">{{ message|safe }}</div> {% endfor %} {% endif %} {% if form.errors %} <p style="font-size: 20px; margin-left: 25px">Your username and password did not match. Please try again.</p> {% endif %} <form action="" method="post"> {% csrf_token %} <div class="mb-3"> <label id="login-label" for="username" class="form-label">Username:</label> <input id="login-form" type="text" class="form-control" name="username" id="username" placeholder="Username" /> </div> <div class="mb-3"> <label id="login-label" for="password" class="form-label">Password:</label> <input id="login-form" type="password" class="form-control" name="password" id="password" placeholder="Password" /> </div> <div id="login-btn"> <input class="btn btn-secondary" type="submit" value="Login" /> </div> </form> <h5 style="text-align: center; bottom: -50px; position: relative;"> Yet to have an account? <a href="register/">Sign up</a> </h5> {% else %} <br /><br /> <p style="font-size: 30px; text-align: center; font-weight: 600">You are already logged in.</p> {% endif %} </div> </div> <p class="markdown-support-note">markdown supported</p> {% endif %} {% endblock %}
-
-
Create
add-note.html
{% extends 'base.html' %} <title>{% block title %}Add note || My Note App{% endblock %}</title> {% block content %} <div class="notes-container"> <h4 class="a-note-title">Add a note</h4> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button class="btn btn-secondary sm" type="submit">save</button> </form> </div> {% endblock %}
-
Create
base.html
This file contains the
<head>base.html</head>
and thenav
of all the template pages
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet" /> <link rel="stylesheet" href="{% static 'css/styles.css' %}" /> <title>{% block title %}My Note App{% endblock %}</title> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <div class="container-fluid"> <span class="navbar-brand" style="color: orange;">☶ My Note App</span> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> {% if user.is_authenticated %} <ul class="navbar-nav me-auto mb-2 mb-lg-0""> <li class="nav-item"><a class="nav-link" href="{% url 'add-note' %}">Add a note</a></li> </ul> {% else %} <ul class="navbar-nav me-auto mb-2 mb-lg-0""> <li class="nav-item"><a class="nav-link" href="{% url 'add-note' %}">Add a note</a></li> </ul> {% endif %} {% if user.is_authenticated %} <div class="btn-group"> <ul class="nav justify-content-end"> <li class="nav-item dropdown" > <a style="color: orange;" class="nav-link dropdown-toggle" class="nav-link" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> {{ request.user }} </a> <ul class="dropdown-menu bg-dark" aria-labelledby="navbarDropdown"> <li class="nav-item"><a style="color: orange;" class="nav-link" href="{% url 'logout' %}">Log out</a></li> <li><a style="color: orange;" class="dropdown-item" href="{% url 'delete-user' %}">Delete acc</a></li> </ul> </div> {% endif %} </div> </nav> <div class="container">{% block content %} {% endblock %} <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script> </div> </body> </html>
-
Create
edit-note.html
{% extends 'base.html' %} <title>{% block title %}Edit note|| My Note App{% endblock %}</title> {% block content %} <div class="notes-container"> <h4 class="a-note-title" >Edit note</h4> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button class="btn btn-secondary sm" type="submit">save</button> </form> </div> {% endblock %}
-
Create
note-list.html
{% extends 'base.html' %} <title>{% block title %}Note list || My Note App{% endblock %}</title> {% block content %} <div class="notes-container"> <div class="note-header"> <h5> <span class="note-logo">☶</span>{{request.user}}'s note <span class="note-count">{{notes.count}}</span> </h5> <a href="{% url 'add-note' %}"><span id="add-note-icon" class="material-icons">add_circle</span></a> </div> {% for note in notes %} <div class="note-list"> <div class="note-title"> <a href="{% url 'view-note' note.pk %}">{{ note }}</a> <small class="note-created">{{note.created}}</small> </div> </div> {% endfor %} </div> {% endblock %}
-
Create
view-note.html
{% extends 'base.html' %} <title>{% block title %}Note || My Note App{% endblock %}</title> {% block content %} <div class="notes-container"> <div class="note-detail-header"> <a href="{% url 'notes-list' %}"><span id="back-to-notes-list" class="material-icons">chevron_left</span></a> <span class="icons"> <a href="{% url 'edit-note' note.pk%}"><span id="edit-note" class="material-icons">edit</span></a> <a href="{% url 'delete-note' note.pk%}"><span id="delete-note" class="material-icons"> delete </span></a> </span> </div> <h4 class="a-note-title">{{note}}</h4> <div class="note-body">{{ note.body}}</div> </div> {% endblock %}
-
Our note application should be up, running and functional. I hope you enjoyed reading this article and building along side.
Top comments (0)