Django
Quick Start
- Create a new directory and change to it
- Create a virtual environment using
python -m venv venv
- Activate the virtual env using
venv\Scripts\activate
- Install latest Django using
pip install django
- Post installation create a django project using
django-admin startproject smartnotes .
- To run this boilerplate project just run
python manage.py runserver <port>
- The project should start serving on localhost on the port provided
Expanding the project
- Django projects are organized by apps
- Apps can be created using
django-admin startapp <appname>
- This will create a '
' folder in the project which will have multiple files - To add a basic view add the final state of the views.py file should be:
- The request is sent by the urls.py from the root project. Its final state should be:
- The default localhost page will start returning a 404 now but new end points will be added one of them would be the app name
- The views are served at that end point, example:
http://127.0.0.1:8001/home
- Django uses a framework called MVT i.e. Model view template
- In order to pass a proper html to the app we need to create a template directory inside the appname directory and create another appname directory inside templates directory
- In the templates/appname directory add the html file and include the required markup
- Change the views.py file to render this html page by returning the render function output:
render(request, 'appname/home.html', {'today', datetime.today()})
- The braces at the end is a way to pass on information to the view which can be accessed on the front end using
{{{today}}}
- Django projects are designed to be modular. The apps should be organized in a way that even if one app is deleted the project should not go down.
- In order to do this apps should be self contained and have just 1 concern
- Best practice is to have urls.py in each app which handles the routing and use include function in the project urls.py
Built-in user management
-
Django Admin:
- To activate the admin interface you do not need to do anything special. Admin is alreay implemented.
- You need to create a superuser though to access it.
- First migrate the database using
python manage.py migrate
- Then simply run
python manage.py createsuperuser
to create a super user. The app will ask questions and create a user based on responses - Access admin panel using the credentials
- By default the django admin provides way to create users and specify passwords and set previliges
- Any changes here is stored in the database
-
Simple user authentication based access
- In order to restrict access to authorized users we can simply do the below in views.py:
- In addition to the view we need to update the view in the urls.py:
- That's about it
Connecting databases
-
Creating the ORM model
- The sequence of flow that Django follows to create a model is:
- Create ORM model in models.py of the app
- Run
Makemigrations
- Run
migrations
- ORMs basically allow us to configure a object oriented model of the RDBMs
- In the models.py define a class where the class name should be the name of the table & attribute to be name of the columns
- A typical class can be:
- After creating this run
python manage.py makemigrations
to create the migrations - After this run
python manage.py migrate
to migrate the ORMs
- The sequence of flow that Django follows to create a model is:
-
Exposing the model via Django Admin:
- Choose the admin.py from the app
- Override the
admin.ModelAdmin
class with the ModelAppname class - either return a pass in case no extra customization is needed or make customizations
- Register this admin
- The sample class is as follows:
- Once saved the model will be available from the django admin menu
-
Adding and querying data from Django shell:
- Invoking Django shell:
python manage.py shell
- Import the model for the app e.g.
from notes.model import Notes
- In the shell use
notes = Notes.objects.all()
to get a QueryList of all notes in the db - In the shell use
new_note = Notes.objects.create(title='', note='')
to create a new note - In the shell use
Notes.objects.filter(title__startswith = 'My')
to filter during querying
- Invoking Django shell:
Building dynamic webpages
- Showing data from the database on html pages:
- We need to make changes to views.py and urls.py in the app
- We need to change the urls.py from the project
- We need to add a template html file
Example code: appname/views.py
from django.shortcuts import render
from .models import Notes
# Create your views here.
def list(request):
list_notes = Notes.objects.all()
return render(request, 'notes/smart_notes.html', {'notes':list_notes})
Example code: appname/urls.py
Example html code to read data:Class Based Views: - Class based views are alternative way to create views: - Example class view:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
# simple class view
class HomeView(TemplateView):
template_name = 'home/welcome.html'
extra_context = {'today': datetime.today()}
# authorized view
class AuthorizedView(LoginRequiredMixin, TemplateView):
template_name = 'home/auth.html'
login_url = '/admin'
Building a front end
-
- Create a directory called "static" on the root level
- You need to tell Django that this will host the static files
- The way to do it is the settings.py
-
The settings.py should have the following setup:
Setting up static directory:
- In order to invoke the contents of this directory in the template htmls we need to do following:
- Where the base html can be:
- External CSS also can be used like Bootstrap or Tailwind
CRUD - remaining parts
Until now we have dealt with the Read part of the CRUD operations. We will not deal with the CREATE, UPDATE AND DELETE parts
CREATE:
- We will need to add a create end point
- Use the following code in views.py:
- urls.py:
- Template html:
- Form validations are an important part and Django makes it easy to work with them
- Create a forms.py file in the app and add the code as illustrated below:
from django import forms from django.core.exceptions import ValidationError from .models import Notes class NotesForm(forms.ModelForm): class Meta: model = Notes fields = ('title', 'note') widgets = { 'title': forms.TextInput(attrs={'class' : 'form-control my-5'}), 'note': forms.Textarea(attrs={'class' : 'form-control mb5'}) } labels = { 'note': "What's on your mind?" } def clean_title(self): title = self.cleaned_data['title'] if 'Django' not in title: raise ValidationError('Not a django related note') return title
- The above class needs to be simply included in the views.py in the create view section
- The above also adds ways to control UX elements like label and css injection using labels and widgets dictionary
- The form errors can be exluded by using css selectors and hiding them
- They can also be styled by adding the below code to the template:
UPDATE
- Updating is really easy as it is a natural extension of the create part.
- First add the UpdateView in views.py:
- Update the NoteUpdateView in urls.py which will add an edit end point
- This will basically add the functionality update the fields.
- We can beautify it by adding relevant buttons to the templates
- For example and edit button on the detail page
<a href="{% url 'notes.update' pk=note.id %}" class="btn btn-secondary">Edit</a>
- And cancel button on the edit page:
<a href="{% url 'notes.list' %}" class="btn btn-secondary">Cancel</a>
DELETE
- Deletion is quite similar to update.
- Create a DeleteView in views and include it in urls.py
- Inclusion for urls.py
- Add a template to specify a confirmation message.
- Easy peasy.
User management
- Django already has a user database with one user admin with pk=1
- We first need to create migrations and run them to associate all notes to the admin user
- This will also make the app user aware i.e. we should be able to show only those notes to users who created them
- First update the model:
from django.db import models from django.contrib.auth.models import User # Create your models here. class Notes(models.Model): title = models.CharField(max_length=200) note = models.TextField() created = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="notes")
- Once done create migrations using
python manage.py makemigrations
- Then run the migration
python manage.py migrate
- It will ask for a default user. Enter 1 i.e. pk for admin
- This will associate all existing notes with the admin user
- Next update the views that need logins for example the list view:
from django.contrib.auth.mixins import LoginRequiredMixin # Code before --- class NoteListView(LoginRequiredMixin, ListView): model = Notes context_object_name = "notes" template_name = 'notes/smart_notes.html' login_url = '/admin' def get_queryset(self): return self.request.user.notes.all() # Code after ---
- Assigning a user to a new note:
- To do this add the following method to the createview class:
- This will essentially stop the form from being saved, fetch the object and add a user to it.
Login/Logout/Signup
- Login/Logout/Singups are really easy too.
- Home/Views.py:
from django.contrib.auth.views import LoginView, LogoutView from django.views.generic.edit import CreateView from django.contrib.auth.forms import UserCreationForm from django.shortcuts import redirect class SignupView(CreateView): form_class = UserCreationForm template_name = 'home/register.html' success_url = '/smart/notes' def get(self, request, *args, **kwargs): if self.request.user.is_authenticated: return redirect('notes.list') return super.get(request, *args, **kwargs) class LogoutInterfaceView(LogoutView): template_name = 'home/logout.html' class LoginInterfaceView(LoginView): template_name = 'home/login.html'
- Home/Urls.py:
from django.urls import path from . import views urlpatterns = [ path('', views.HomeView.as_view(), name='home'), path('login', views.LoginInterfaceView.as_view(), name='login'), path('logout', views.LogoutInterfaceView.as_view(), name='logout'), path('signup', views.SignupView.as_view(), name='signup'), ]
- Templates:
<!-- login --> {%extends 'base.html'%} {%block content%} <form method="POST">{%csrf_token%} {{form.as_p}} <input type="submit" class="btn btn-secondary"> </form> {%endblock%} <!-- logout --> {%extends 'base.html'%} {%block content%} <h1>Buh bye! 👋</h1> {%endblock%} <!-- register --> {%extends 'base.html'%} {%block content%} <form method="POST" style="text-align: left; margin: 0 auto; width: 600px;">{%csrf_token%} {{form.as_p}} <input type="submit" class="btn btn-secondary" name="Submit"> </form> {%endblock%}
Finishing touches
- Add finishing touches by adding a navbar to the base.html
<nav class="navbar navbar-dark bg-dark"> <div class="ms-auto"> {%if user.is_authenticated%} <a href="{% url 'home' %}" class="btn btn-outline-light me-1">Home</a> <a href="{% url 'notes.list' %}" class="btn btn-outline-light me-1">Notes</a> <a href="{% url 'notes.new' %}" class="btn btn-outline-light me-1">Create</a> <a href="{% url 'logout' %}" class="btn btn-outline-light me-1">Logout</a> {%else%} <a href="{% url 'login' %}" class="btn btn-outline-light me-1">Login</a> <a href="{% url 'signup' %}" class="btn btn-outline-light me-1">Signup</a> {%endif%} </div> </nav>
Connecting to Postgres
- Your Postgres settings.py should look like this:
- After this create migrations using
python manage.py makemigrations
- Next step is to run migrations
python manage.py migrate
- The data (models) should now show up in postgresql database
Example
- The full coded example (notes application) can be found on this github repository
- To run this project just run
python manage.py runserver <port>