By Tushar Singh (email: email@example.com)
The purpose of this paper is to serve as a review board for exploring and understanding the major outliers in the tools available and how the Django-Framework is structured to allow developers in following the DRY architecture. The paper covers 3 of the core aspects of Django-Framework architecture namely:
- Django ORM
In the context of the Django framework, the settings file is a Python module that contains all the configuration settings for a Django project. It is a central location for defining various aspects of your project such as database connection, middleware, installed applications, time zone, static files, authentication, logging, and more.
The settings file is typically named settings.py and is located at the root of the Django project directory. It is a crucial file that determines how your Django application behaves and interacts with other components such as the database and other third-party services.
Now that you the reader are acquainted with what the settings file is, let us move on to exploring some of its features and components.
- Secret Key:
In the context of the Django framework, a secret key is a unique and random string of characters used for cryptographic signing and securing the session data, user authentication, and other sensitive information in a Django project. It is an essential security feature that helps protect your application against various forms of attacks such as CSRF, XSS (we shall explore these later in the paper), and others.
- Default Django-Apps:
Django as a framework provides a set of default applications that are included whenever a new project is created via the django-admin startproject command. These apps get included in the INSTALLED_APPS list within the settings.py file of the project.
Some of the default Django applications are as follows:
django.contrib.admin: This app provides the built-in Django admin interface, which allows you to manage your application's data through a web-based user interface. It provides an easy way to perform CRUD (Create, Read, Update, Delete) operations on your models and manage your site's users and groups.
django.contrib.auth: This app provides the authentication system for your Django application. It includes user authentication, password management, and permission handling.
django.contrib.contenttypes: This app provides a framework for content types, which allows you to create and manage different types of content in your application, such as blog posts, comments, or user profiles.
django.contrib.sessions: This app provides support for session management in your Django application. It allows you to store and retrieve user-specific data across different requests.
django.contrib.messages: This app provides support for messages framework in your Django application. It allows you to display messages to the user, such as success messages, error messages, or warning messages.
django.contrib.sites: This app provides support for managing multiple sites in a single Django project.
One question that comes as a follow-up is, there's already a decent amount of default applications that serve a lot of purposes for any project. Are there more such applications present? The answer to that is yes, there are. Django provides several other built-in apps that you can use based on your project's requirements. You can also install third-party apps or create your own custom apps to extend the functionality of your Django application.
We will now focus on areas that focus on features that enforce security offered by Django-framework.
In the context of the Django framework, middleware is a component that sits between the web server and the view and provides a way to perform processing on incoming requests or outgoing responses. Middleware can be used to modify or filter requests and responses, add headers, or perform authentication and security checks. Middleware operates on the principle of the chain of responsibility, where each middleware component can modify the request or response and pass it on to the next component in the chain. Middleware can be added to your Django project using the MIDDLEWARE setting in the settings file. Following are the different kinds of middleware in Django:
Process Request Middleware: This middleware runs at the beginning of the request processing cycle and can modify the incoming request or add information to it.
Authentication Middleware: This middleware is used for authenticating requests and checking permissions. It can be used to ensure that only authenticated users have access to certain views or to add user information to the request.
Middleware for Request and Response: This middleware runs after the view has been called and can modify the response or add information to it.
Exception Middleware: This middleware is used to catch and handle exceptions that occur during the request processing cycle. It can be used to log errors, send email notifications, or display custom error pages.
Security Middleware: This middleware is used to enforce security policies and prevent attacks such as cross-site scripting (XSS), cross-site request forgery (CSRF), or clickjacking. It can be used to set security headers, add CSRF protection tokens, or perform content security checks.
Now we shall explore a few security features which are offered as part of the Django-Framework architecture for developers.
This is a type of attack that tricks users into performing actions on a website without their consent. Django protects against CSRF attacks by adding a unique token to each form rendered on the site, which is then validated when the form is submitted. This token ensures that the form was submitted by the user who requested it and not by a malicious attacker.
This is a type of attack that injects malicious scripts into a website to steal user data or hijack user sessions. Django provides protection against XSS attacks by automatically escaping all output rendered in templates, ensuring that any user-generated content is safe to display.
This is a type of attack that tricks users into clicking on hidden or disguised elements on a website, leading them to perform actions they didn't intend to. Django provides protection against clickjacking by setting the X-Frame-Options header in HTTP responses, which prevents the site from being embedded within an iframe on another domain.
Perhaps one of the most crucial components in terms of allowing server interaction, the Web Services Gateway Interface (WSGI) is a standard interface between web servers and web applications or frameworks, including Django. It defines a common API for communication between servers and applications, allowing for flexibility in the choice of the web server and application server. In the context of Django, the WSGI interface is used to communicate with web servers such as Apache or Nginx. The WSGI application is responsible for handling incoming HTTP requests and returning responses to the web server.
The Django Framework includes a built-in WSGI application that can be used with any WSGI-compatible web server. When deploying a Django application to a production environment, the WSGI interface is typically used to communicate with the web server.
We now traverse into the next section of the Django Framework which helps in establishing our project database and allow working with it to serve the purpose of our application.
In the Django framework, the model file is a Python module that defines the structure and behavior of a database table. It is one of the three major components of the Model-View-Controller (MVC) design architecture used in Django. The model file is responsible for defining the fields of a database table, their types, and any relationships between tables. It also defines the behavior of the table, such as validation rules, default values, and custom methods. The model file is an essential component of the Django-ORM system (explored in this paper). The Django-ORM provides a high-level interface for managing data and interacting with the database.
From the perspective of working with databases in our project which uses the Django-Framework, 'on_delete' is a parameter that used for dictating the behavior of a foreign key when the referenced object is deleted. The option 'on_delete=CASCADE' is a fairly common option that is used, which specifies that when the referenced object is deleted, all objects which have that specific foreign key shall be deleted.
Consider the following code snippet which shows the usage of the 'on_delete' parameter while defining our database in models.
class Batch(models.Model): name = models.CharField(max_length = 50) class Review(models.Model): title = models.CharField(max_length = 30) entrant = models.ForeignKey(Batch, on_delete = models.CASCADE)
In this example, the Review model has a foreign key to the Batch model, and the on_delete parameter is set to CASCADE. This means that if a Batch object is deleted, all Review objects that have a foreign key pointing to that Batch will also be deleted.
Now we shall explore 2 more components which help add more depth and detail in defining our database for our project i.e. Fields and Validators. In the Django framework, fields and validators are used to define the structure and validation rules of the data stored in a database table. Fields represent the columns of the table, and validators are used to ensure that the data stored in those columns is valid.
Fields in Django can be of different types, such as CharField, IntegerField, DateField, and so on. Each field type has its own set of options that can be used to customize its behavior. For example, the CharField has an optional max_length parameter, which sets the maximum length of the field.
Validators, on the other hand, are functions that are used to validate the data stored in a field. They can be used to ensure that the data meets certain criteria, such as being within a certain range, or having a specific format.Django provides a number of built-in validators, such as MinValueValidator, MaxValueValidator, RegexValidator, etc.
Following code snippet will help in getting a better idea of Fields and Validators:
from django.db import models from django.core.validators import MinValueValidator, MaxValueValidator class Shop(models.Model): toolName = models.CharField(max_length=100) toolDesigner = models.CharField(max_length=50) production_date = models.DateField() toolPrice = models.DecimalField(max_digits=5, decimal_places=2, validators=[ MinValueValidator(0.01), MaxValueValidator(999.99) ])
In this code snippet, the Shop model has fields for different tools available (toolName), the company designing the tool (toolDesigner), production_date, and its price. The price field is a DecimalField, which has a maximum of 5 digits, with 2 digits after the decimal point. It also has two validators, MinValueValidator and MaxValueValidator, which ensure that the price is between 0.01 and 999.99.
Now this might be seem a bit strange to introduce this small section but no harm no foul in gaining a bit more conceptual clarity.
A class in python is a template for creating objects with a set of attributes and methods. A class can be thought of as a user-defined data type that encapsulates data and functionality.
A module on the other hand s a file containing Python definitions and statements. It can be thought of as a collection of related functions, classes, and variables that can be used in other Python programs. A module can be imported into a Python program using the import statement.
The final section of this paper will be dedicated to understanding the Object-Relational-Mapping (ORM) technique and few concepts related to transactions in Database Management Systems.
In software engineering, ORM is a technique used in software engineering to convert data between incompatible type systems in object-oriented programming languages and relational databases.
In the context of Django Framework, the ORM system provides a high-level API for querying and manipulating data in a relational database. Instead of writing SQL queries directly, developers can use Django's ORM to define models that represent the data in their application and then use a Pythonic syntax to perform database operations. Django's ORM allows developers to define database tables as Python classes, which can be used to create, retrieve, update, and delete data in the database. Each model represents a database table, and each attribute of the model corresponds to a column in the table.
The following section acquaints the reader with using ORM queries in the Django shell and turning the ORM queries to SQL in the Django Shell:
The way to access the Django shell is simply with the use of the command
python manage.py shell
Once activated, developers can use Django's ORM to work with the database. Consider the following model saving player info for a tournament with fields defined as follows:
class Player(models.Model): name = models.CharField(max_length=50) country = models.CharField(max_length=30) age = models.IntegerField()
If we wish to retrieve all players registered for the tournament, we can simply do the following:
from <appname>.models import Player players = Player.objects.all() for currentPlayer in players: print(currentPlayer.name, currentPlayer.age, currentPlayer.country)
To search for players who are above a certain age group we can simply do:
adultPlayers = Players.objects.filter(age__gte = 21)
Methods such as get(), exclude(), order_by(), and count() which are provided by Django's ORM can add more tools in performing even more queries.
The Django shell interface allows us to use the query attribute of a QuerySet object to get the corresponding SQL query that will be executed by the ORM. Following code snippets will help show how to do so:
Let's use our previous model of players once again.
To retrieve all players once again we will simply do as follows:
from <appname>.models import Player players = Player.objects.all() player_query = players.query print(player_query)
Our previous age group filter can be converted into a query like so:
adult_query = adultPlayers.query print(adult_query)
We shall now explore some concepts which draw a lot of relation with some important SQL concepts.
Aggregations in general are operations that perform calculations on a set of database records and return a single value. These operations are typically used to summarize or analyze data in the database. Django's ORM provides several aggregation functions that can be used to perform aggregations on QuerySets. These functions are called on a QuerySet object and return a single value.
Commonly used aggregation functions included in Django's ORM include Count, Sum, Avg, Min, Max which all as you can expect actually mean what they mean in the literal sense.
In Django's ORM, annotation is a feature that allows us to add extra information to each object in a QuerySet. This extra information can either be the result of a database aggregation, a related object, or a calculated value based on other fields in the model. Following is a snippet of a general annotation operation on a QuerySet of delivery data saved within a projects database:
runsData = deliveryData.objects.filter(match_id__season=2016).values('bowling_team').annotate(extra_runs=Sum('extra_runs'))
Perhaps a more commonly missed out file, yet maybe the most important file in a Django project structure (besides the settings file) is the migration file. The migration file is a script that represents a set of changes to the database schema. Migration files are used to apply and track changes to the database schema as your Django application evolves over time.
When a change is made to your models, such as adding a new field or changing the data type of an existing field,a new migration file needs to be created to represent those changes. The migration file contains instructions on how the database schema needs to be updated to match the new model definition.
Migration files employ the makemigrations management command, which analyzes our models and generates the appropriate migration files.
The need and necessity for migration file come due to the fact that it is needed to keep the database schema in sync with our models and ensure that our application can function correctly. Without migrations, changes to our models could cause errors or data loss when running our application or performing database operations.
Perhaps a very important concept related to maintaining and creating databases will be discussed in the following section.
The following content references my previous paper on SQL, the link for which is provided in the references section. A brief summary of SQL Transactions and Atomic transactions requires an understanding of transactions in the first place.
A transaction as we define it in the context of databases and management systems is an operation that has definite completion states i.e. complete or incomplete. To go more into detail, Transactions can be comprised of either a single reading, writing, deletion, or updation process or a combination of either of these processes. The simplest example that one can easily relate to is that of a banking withdrawal transaction via an ATM.
An Atomic Transaction is a transaction that has the property Atomicity meaning it is a transaction that ensures that each statement that constitutes a transaction is treated like an individual unit. The statement is either going to end up being executed completely or the entirety of it isn't even bothered with in the first place.
In conclusion, I hope this review paper has provided you with a good understanding of the key concepts and features of the Django Framework. We have covered a wide range of topics, from the basics of setting up a Django project to advanced topics such as ORM queries and web services.
Whether you are new to Django or a seasoned developer, there is something in this review for everyone. By the end of this review, I hope you are satisfied with the contents and feel more confident in your ability to work with the Django Framework.