Django Setup

Posted by Daksh on Wednesday, August 3, 2022

Quick start guide to Django

  1. Firstly activate venv and install django

  2. Creating a new project: $ django-admin startproject project_name
    To avoid sub directory with the same name, use a period in the end $ django-admin startproject project_name .

  3. From virtual env folder, $ cd project_name

  4. To start a server: $ python manage.py runserver —-> press ctrl + c to quit running server

  5. To create Django apps: $ python manage.py startapp app_name

Request/Response Flow

Client requests server -> server is running Django -> Server looks in URLconf, and selects the respective view -> this view returns response back to client

Django Project Structure

Django administrative tasks

django-admin -> Ready to use admin interface, in the form of command line utility named as django-admin. It is the Django’s command line utility for administrative tasks. This utility is present in the scripts folder of your django’s env directory. It is executed from inside the terminal. It is automatically installed and it should be located on your system path. If you do not find it on your system path, make sure you have your virtual env activated.

manage.py -> It is a script that is the local version of Django admin and is located inside the project folder. It sets the Django settings module environment variable so that it points to project settings.py file. This file is also autocreated. It is specific to the virtual environment of the project. It points to settings.py of the project.

However, if you need to switch between multiple Django setting files, use the django-admin command with the DJANGO_SETTINGS_MODULE or the settings command line option.

Development Server

The Development Web Server by default runs on port 8000, on the IP address 127.0.0.1, therefore the URL looks like -> http://127.0.0.1:8000/. It is possible to change this, as you can pass the IP address and port number explicitly. A user with normal privilidges might not access to start to a port on a low port number(e.g. 1024). This is because, the low port number are reserved for the superuser or root user.

To run the server on a different port, use the following command: $ python manage.py runserver 9000

Django recommends that you should never use this development server in production, as it has not gone through security audits or performance tests. The development server automatically reloads the Python code for each request as needed. No restart required. Adding files don’t trigger restart, and in these cases you yourself have to quit the server and start it again.

What is a Django app?

As mentioned above, an app is responsible for performing one single task out of the many involved in the complete web application, represented by the Django project.

It implies that a project comprises many independent sub-applications, yet they may communicate among themselves. However, the important feature of the Django app is that it is reusable.

App Structure

views.py

In Django, a view is a user-defined function that’s called when Django’s URL dispatcher identifies the client’s request URL and matches it with a URL pattern defined in the urls.py file. The request object is the mandatory argument for the view function.

It takes a request and returns a response. Mapping a URL to a view is called Routing.

The auto-created views file is empty at the beginning.

from django.http import HttpResponse 
def index(request): 
    return HttpResponse("Hi from Daksh!") 

Views are mainly of two types, namely, function-based views and class-based views. Function-based views uses if-else statements to handle different requests. Class-based views use instances methods to handle different requests.

Advantages of class-based views:

  • Removes the conditional logic from the view function, hence simplifying the code.
  • Simplifies the code and separates the logic, making it easier to understand and maintain.
  • Extends the functionality of the view by using mixins. Mixins are class based generic views that are flexible compared to function-based views equivalent. Similar to any form of multiple inheritance. It’s important to note that when using mixins, they can’t be all used together.

Common view for CRUD operations:

  • CreateView
  • UpdateView
  • DeleteView
  • ListView
  • DetailView

Overriding the default error views: (More on this later in the same post)

  1. To override the default error views, you can define views in the view.py file in the project folder.
  2. Specify the appropriate handler in the project’s URLConf. There are predefined handlers for customizing error views, such as Handler404 for page_not_found().

The request object can do the following:

  • request.GET and request.POST: The attributes return a dictionary-like object containing GET and POST parameters, respectively.
  • request.COOKIES: Along with the parameters, the browser also packs the request objects with cookies inserted by the server’s previous interactions. It is a dictionary of string keys and values.
  • request.FILES: When the user uploads one or more files with a multipart form, they are present in this attribute in the form of UploadedFile objects. By appropriate logic in the view, these uploaded files are saved in the designated folder on the server.
  • request.user: The request object also contains information about the current user. This attribute is an object of django.contrib.auth.models.User class. However, if the user is unauthenticated, it returns AnonymousUser. Inside the view, you can lay down separated separate logic for either of them.
  • request.has_key(): This is a method available to the request object. It helps check whether the GET or POST parameter dictionary has a value for the given key.
  • request.path: This attribute returns the path of the request URL.

Methods of the HttpResponse object:

  • status_code: returns the HTTP status code corresponding to the response
  • content: returns the byte string of the response.
  • __getitem__(): method that returns the value of a header
  • __setitem__(): method used to add a header
  • write(): This method creates a file-like object.

urls.py

Structure of any URL:

https://www.example.com/example1/anything
https: scheme (protocol)
www: subdomain
example.com: domain 
example1: file path (location of the resource)
anything: parameter

The project package has a file of this name that defines the URL patterns for the project.

On similar lines, you need to provide the URL routing mechanism for the app.

The app folder doesn’t have a file of this name when created. Hence, you have to create one.

The path converters capture path parameters from the URL. Use <place_holder> to capture the path parameter. The path parameter is passed to the view function as a keyword argument. The name of the keyword argument is the same as the name of the place holder.

  • str - Matches any non-empty string, excluding the path separator ("/") and the query string.
  • int - Matches any positive or negative integer.
  • slug - Matches any slug string consisting of ASCII letters or digits, hyphens, or underscores.
  • uuid - Matches a Universally Unique Identifier (UUID), formatted as a hexadecimal string with hyphens.
  • path - Matches any string, including the path separator ("/") and the query string.

Create urls.py in the app_name folder.

from django.urls import path 
from . import views 

# The list must be named urlpatterns
urlpatterns = [ 
    path('', views.index, name='index-url'), 
] 

Then update the urlpatterns list in the project folder’s urls.py and include the app’s URL configurations. By using the include() function, you can reference the app’s urls.py file from the project’s urls.py file. To add a URL pattern with regex, you use the re_path() function instead of the path() function. Regular expressions can be used for extraction & validation, Advanced Searching, Group Searches, Finding & Replace, etc.

The updated project_name/urls.py should look like this:

from django.contrib import admin 
from django.urls import include, path, re_path

urlpatterns = [ 
    path('demo/', include('app_name.urls')), 
    path('admin/', admin.site.urls), 
    re_path(r'^other/(?P<year>[0-9]{4})/$', views.year_archive),
] 

Name the URL pattern in the app’s urls.py file. This is done by adding the name parameter (optional) to the path() function. This name is used to identify the URL pattern in the templates. It is used to define the URL namespace. It is also used by the reverse() function to fetch the URL mapped with the view function.

Namespcae: The problem comes when the view function of the same name is defined in more than one app. This is where the idea of a namespace is needed. The application namespace is created by defining app_name variable in the application’s urls.py and assigning it the name of the app.

# inside app_name/urls.py
from django.urls import path  
from . import views    
app_name='app_name' 
urlpatterns = [  
    path('', views.index, name='index'),      
] 

# using the namespace in reverse()
>>> from django.urls import reverse
>>> reverse('app_name:index')

# this way, if you have another app with the same view function name, you can use the namespace to differentiate between the two.

# instance namespace
# You can also use the namespace parameter in the include() function while adding an app’s urlpattern to that of a project.
#in demoproject/urls.py 
urlpatterns=[ 
    # ... 
    path('app_name/', include('app_name.urls', namespace='app_name')), 
    # ... 
] 

Namespaces in templates:

{% url 'app_name:index' %} 

models.py

The data models required for processing in this app are created in this file. It is empty by default. A data model is a Python class based on django.db.modelsclass.All the models present here are migrated to the database tables. For now, leave his file as it is without adding any models. You will learn how to work with models later.

tests.py

You’ll write the tests to be run on the app in this file. Let’s keep this file as it is without modifying it.

Update settings.py

Lastly, you need to update the list of INSTALLED_APPS in the project’s setting file. This list already contains some pre-installed apps. Add the name of demoapp so that it looks like this:

INSTALLED_APPS = [ 
    'django.contrib.admin', 
    'django.contrib.auth',  
    'django.contrib.contenttypes', 
    'django.contrib.sessions', 
    'django.contrib.messages', 
    'django.contrib.staticfiles', 
    'app_name', 
] 

Error Handling

Django handles all error cases by raising an exception which is invoked via an error handling view that you can configure. These error handle views are added to a separate views.py file that must be created at the project level to get applied across the project.

The views to use for these cases are specified by four variables. They are

  • handler400
  • Handler403
  • Handler404
  • handler500

To customize the views for these cases, you can override the default values for these.

# in project_name/urls.py
# setting them in any other URL configuration file will have no effect
handler400 = 'project_name.views.handler400' # bad request
handler403 = 'project_name.views.handler403' # permission denied
handler404 = 'project_name.views.handler404' # page not found
handler500 = 'project_name.views.handler500' # server error

# in project_name/views.py
from django.shortcuts import render
from django.http import HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, HttpResponseServerError

# same name as the handler variable defined in urls.py of the project
def handler400(request, exception):
    return HttpResponseBadRequest('Bad Request')

In order to implement a custom view the view function needs to accept the appropriate request argument and return the appropriate response. The custom views that you create using these handlers return different http response sub classes that handle different types of http responses. For example HttpResponseNotFound. Acts just like http response but uses a 404 status code class http response. Similarly, HttpResponseBadRequest is used for 400 status code, HttpResponseForbidden for 403 status code and HttpResponseServerError for 500 status code.

If you use HttpResponseNotFound in the view function, then Django internally sends an error code 404. The appropriate page for 404 can then be configured and rendered, else the browser displays its default 404 view. If you still want to use HttpResponse then you will have to manually set the status code to 404 HttpResponse('<h1>Page not found</h1>', status_code='404'). You can also raise an exception like Http404('Error message').

Exception classes

The exception classes are defined in the django.core.exceptions module. The most commonly used exception classes are:

  • ObjectDoesNotExist: All the exceptions of the DoesNotExist are inherited from this base exception.
  • EmptyResultSet: This exception is raised if a query does not return any result.
  • FieldDoesNotExist: This exception is raised when the requested field does not exist.
  • MultipleObjectsReturned: When you expect a certain query to return only a single object, however multiple objects are returned. This is when you need to raise this exception.
  • PermissionDenied: This exception is raised when a user does not have permission to perform the action requested.
  • ViewDoesNotExist: This exception is raised by django.urls when a requested view does not exist, possibly because of incorrect mapping defined in the URLconf.

Code to raise an exception:

# field does not exist
try: 
    field = model._meta.get_field(field_name) 
except FieldDoesNotExist: 
    return HttpResponse("Field Does not exist") 

# Permission denied
def myview(request): 
    if not request.user.has_perm('myapp.view_mymodel'): 
        raise PermissionDenied() 
    return HttpResponse() 

Designing a 404 Page

I personally like to include two main things in my 404 page. One is a search bar, to help users find what they are looking for. The other is a link to home page, to help users navigate back to the home page. Django picks up the 404.html template from the templates folder of the project. If not found, it uses the default 404.html template provided by Django.