0% found this document useful (0 votes)
110 views186 pages

FSD Compiled Notes

Uploaded by

Rutuja K
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
110 views186 pages

FSD Compiled Notes

Uploaded by

Rutuja K
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 186

Full Stack Development 21CS62

Module-1: MVC based Web Designing


Introduction to Django
What is a Web Framework?
Django is a powerful web development framework that aims to simplify the process of building
web applications by providing a set of tools, libraries, and conventions. At its core, Django is
designed to save developers time and make web development a more enjoyable experience.
Web development involves creating dynamic and interactive websites or web applications that
can perform various tasks, such as processing user input, interacting with databases, and
generating dynamic content. Without a framework like Django, developers would need to write
all the code from scratch, including handling HTTP requests, managing databases, and
generating HTML content.
Django eliminates much of this manual work by providing a structured approach to web
development. It follows the model-view-template (MVT) architectural pattern, which divides
the application into three main components:
1. Model: Represents the data structure of the application and interacts with the database.
Django provides an object-relational mapping (ORM) system that allows developers to
define models as Python classes, which are then translated into database tables.
2. View: Handles the logic of the application and serves as the bridge between the model
and the template. Views receive HTTP requests, process data from the model, and return
responses to the client.
3. Template: Defines the presentation layer of the application and generates HTML
content to be displayed to the user. Templates are written in Django's template language,
which allows for the dynamic rendering of data.
Django also includes a range of built-in features and utilities to streamline common web
development tasks, such as:
1. Authentication and authorization: Django provides robust authentication and
authorization mechanisms, allowing developers to secure their applications and manage
user accounts easily.
2. URL routing: Django's URL routing system maps URL patterns to view functions,
making it easy to define the structure of a web application and handle different types of
requests.
3. Admin interface: Django's admin interface offers a powerful and customizable
interface for managing application data, including built-in CRUD (create, read, update,
delete) operations.

1
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

4. Form handling: Django simplifies form handling by providing form classes that can
automatically validate user input, handle form submission, and generate HTML forms.
5. Security features: Django includes built-in security features to protect against common
web vulnerabilities, such as cross-site scripting (XSS), cross-site request forgery
(CSRF), and SQL injection.
Sample Python CGI script that displays the ten most recently published books from a
database
import MySQLdb
print "Content-Type: text/html\n"
print "<html><head><title>Books</title></head>"
print "<body>"
print "<h1>Books</h1>"
print "<ul>"
connection = MySQLdb.connect(user='me', passwd='letmein', db='my_db')
cursor = connection.cursor()
cursor.execute("SELECT name FROM books ORDER BY pub_date DESC LIMIT 10")
for row in cursor.fetchall():

print "<li>%s</li>" % row[0]


print "</ul>"
print "</body></html>"
connection.close()
This Python CGI script showcases a basic approach to generating dynamic HTML content by
retrieving data from a MySQL database. It begins with specifying the Python interpreter to use,
imports the MySQLdb module for database interaction, and prints the necessary HTTP headers
to specify the content type as HTML. The script then constructs an HTML document,
establishes a connection to the MySQL database, executes an SQL query to retrieve the ten
most recently published books, and iterates over the results to display them as an unordered list
in the HTML document. Finally, it closes the HTML document and the database connection.
While this approach is simple and straightforward, it lacks scalability and maintainability for
larger or more complex web applications. In such cases, utilizing a web framework like Django
would offer a more organized, efficient, and secure solution for web development tasks.

2
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

The MVC Design Pattern


from django.db import models
class Book(models.Model):
name = models.CharField(max_length=50)
pub_date = models.DateField()
# views.py (the business logic)
from django.shortcuts import render_to_response
from models import Book

def latest_books(request):
book_list = Book.objects.order_by('-pub_date')[:10]
return render_to_response('latest_books.html', {'book_list': book_list})
# urls.py (the URL configuration)
from django.conf.urls.defaults import *
import views
urlpatterns = patterns('',
(r'^latest/$', views.latest_books),
)
# latest_books.html (the template)
<html><head><title>Books</title></head>
<body>
<h1>Books</h1>
<ul>

{% for book in book_list %}


<li>{{ book.name }}</li>
{% endfor %}
</ul>
</body></html>

3
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

This Django example demonstrates the Model-View-Controller (MVC) design pattern, which
separates concerns within a web application into distinct components:
1. models.py: Defines the database structure using Django's ORM (Object-Relational
Mapping). The Book class represents a table in the database, and developers can
interact with it using simple Python code.
2. views.py: Contains the business logic of the application. The latest_books() function
retrieves the latest ten books from the database and passes them to the template for
rendering.
3. urls.py: Maps URL patterns to view functions. In this example, the /latest/ URL is
routed to the latest_books() view function.
4. latest_books.html: An HTML template that defines the structure and content of the
page. It uses Django's template language to iterate over the list of books and display
them as list items.
The MVC pattern separates the concerns of data management (model), business logic
(controller), and presentation (view). This separation allows for loose coupling between
components, enabling independent development and maintenance:

 Developers can modify the database structure or data access logic without affecting
the presentation layer.
 Designers can update the HTML templates without altering the underlying
application logic.
 Changes to URL routing can be made without impacting the functionality of
individual views.
 Database administrators can manage database schema changes centrally, reducing
the risk of errors.

Django's History
Django originated from the real-world needs of a web development team at the Lawrence
Journal-World newspaper in Lawrence, Kansas, USA, back in 2003. Adrian Holovaty and
Simon Willison, along with Jacob Kaplan-Moss later on, developed Django to address the
challenges of building and maintaining web applications under tight journalism deadlines. The
team needed a framework that could streamline development and meet the demands of rapidly
changing requirements.
Driven by necessity, Django was born out of the team's efforts to create a time-saving web
development framework. After refining the framework to power most of the Lawrence Journal-
World's sites, the team decided to release it as open-source software in July 2005. They named
it Django, after the jazz guitarist Django Reinhardt.

4
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

Over the years, Django has grown into a mature and widely adopted framework with a large
and active community of users and contributors worldwide. Adrian Holovaty and Jacob
Kaplan-Moss, two of the original developers, continue to provide guidance for the framework's
development, but it is now a collaborative effort involving many contributors.
Django's roots in the news environment have influenced its feature set, making it particularly
well-suited for content-driven sites like news portals and e-commerce platforms. However,
Django is versatile and can be used to build a wide range of web applications.
The culture of Django's open-source community is shaped by its origins in real-world
development. The framework is focused on solving practical web development problems faced
by its developers, which drives continuous improvement. Django's maintainers are motivated
by their own desire to save time, create maintainable applications, and enjoy their work, leading
to a framework that is constantly evolving and improving.

Views and URLconfs


A URLconf is like a table of contents for a Django-powered Web site. Basically, it’s a mapping
between URLs and the view functions that should be called for those URLs.

First View
Creating your first view in Django is straightforward. Start by creating an empty file named
views.py within the mysite directory. While Django doesn't enforce the name views.py, it's a
convention that helps other developers understand the purpose of the file.
from django. http import HttpResponse
def hello(request):
return HttpResponse ("Hello world")
Importing HttpResponse: The HttpResponse class is imported from the django. http module.
This class represents an HTTP response.
Defining the View Function: A function named hello is defined. This function represents the
view. It takes one parameter, request, which is an instance of django.http.HttpRequest. This
parameter contains information about the current HTTP request.

Returning an HttpResponse: The hello function returns an instance of HttpResponse with the
text "Hello world". This response will be sent back to the client's browser when the view is
accessed.
The name of the view function (hello in this case) doesn't matter to Django's core functionality.
Django relies on URL patterns to route requests to views, as we'll see in the next section. The
view function could have any name as long as it follows the pattern of taking an HttpRequest
object as its first parameter and returning an HttpResponse object. Django view is simply a
Python function that takes an HttpRequest object as its first parameter and returns an

5
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

HttpResponse object. This pattern allows Django to handle HTTP requests and generate
appropriate responses dynamically.

Your First URLconf


To activate your "Hello world" view in Django, you need to define a URLconf, which maps
URLs to view functions. Think of it as a table of contents for your website, specifying which
view function should be called for each URL.
When you created your Django project using django-admin.py startproject, a default URLconf
was generated for you in the urls.py file within your project directory (mysite in this case).
from django.conf.urls.defaults import *
urlpatterns = patterns ('',

# Define URL patterns here


)
1. Importing URL Functions: The django.conf.urls.defaults module is imported, which
provides functions for defining URL patterns.
2. Defining URL Patterns: The urlpatterns variable is set using the patterns () function.
This function takes a series of URL patterns as arguments. In the default case, it's an
empty list.
To hook your "Hello world" view to a specific URL, you need to add a URL pattern to this list.
from django.conf.urls.defaults import *
from views import hello
urlpatterns = patterns ('',
# Map the URL /hello/ to the hello view function
(r'^hello/$', hello),
)
In this modified urls.py file:

 We import the hello view function from the views module.


 We add a URL pattern (r'^hello/$', hello) to the urlpatterns list. This pattern specifies
that requests to the URL /hello/ should be handled by the hello view function.

6
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

With this setup, when a user visits the URL /hello/ in their browser, Django will call the hello
view function, which will return an HTTP response with the text "Hello world". This response
will then be displayed in the user's browser.
By defining URL patterns in urls.py, you're telling Django how to route incoming requests to
the appropriate view functions, effectively mapping URLs to code handlers.

To integrate the "hello" view into the URLconf, you can follow these steps:

 Import the hello view function from the mysite.views module.


 Add a tuple to the urlpatterns list, mapping the URL pattern '^hello/$' to the hello view
function.
from django.conf.urls.defaults import *
from mysite.views import hello
urlpatterns = patterns('',
('^hello/$', hello),
)

With these changes:


1. The hello view function is imported from the mysite.views module.
2. A tuple ('^hello/$', hello) is added to the urlpatterns list. This tuple specifies that
requests to the URL /hello/ should be handled by the hello view function.
3. By making these modifications, you've successfully integrated the "hello" view into
your URLconf. Now, when a user visits the URL /hello/ in their browser, Django will
call the hello view function, which will return an HTTP response with the text "Hello
world".

A Quick Note About 404 Errors


When you request a URL that is not defined in your Django URLconf, such as
http://127.0.0.1:8000/goodbye/ or http://127.0.0.1:8000/hello/subdirectory/, Django will
return a "Page not found" error message. This error page provides information about which
URLconf Django used and every pattern in that URLconf, helping you identify why the
requested URL resulted in a 404 error.
Here's how to interpret this error page:
 URLconf Used: Django displays the URLconf it used to handle the request.
 URL Patterns: The error page lists all the URL patterns defined in the URLconf.

7
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

 Error Message: The "Page not found" message indicates that the requested URL does
not match any of the defined URL patterns.
This error page is useful for debugging during development, but it contains sensitive
information that you wouldn't want to expose to the public on a production site. Therefore,
Django only displays this error page when your project is in debug mode. Debug mode is
enabled by default when you create a new Django project. Later on, you'll learn how to
deactivate debug mode for a production deployment.

A Quick Note About the Site Root


When you visit the site root, http://127.0.0.1:8000/, Django doesn't automatically assign any
behavior to this URL. Therefore, if you attempt to access the site root without defining a
corresponding URL pattern in your URLconf, you'll receive a 404 error message.
To assign a view to the site root, you need to specify a URL pattern that matches an empty
string. This might seem a bit counterintuitive, but it's how you tell Django to handle requests
to the site root. Here's how you can implement this in your urls.py file:
from django.conf.urls.defaults import *
from mysite.views import hello, my_homepage_view
urlpatterns = patterns('',
# Assign my_homepage_view to the site root
('^$', my_homepage_view),
# Other URL patterns...)
In this example:

 The URL pattern '^$' matches an empty string, representing the site root.
 The my_homepage_view function is assigned to handle requests to the site root.
 You can add other URL patterns as needed.
By defining a URL pattern for the site root, you can specify which view should be rendered
when users visit your website's homepage. This allows you to customize the behaviour of the
site root and provide users with a tailored experience.

How Django Processes a Request?


1. Settings File: Django starts by looking for a file named settings.py in the same
directory as manage.py when you run python manage.py runserver. This file
contains configuration settings for your Django project, including the
ROOT_URLCONF setting.

8
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

2. ROOT_URLCONF: The ROOT_URLCONF setting specifies the Python module


that Django should use as the URLconf for your website. This module contains the URL
patterns for your application.
3. URLconf Processing: When a request is made to a specific URL, such as /hello/,
Django loads the URLconf specified by the ROOT_URLCONF setting. It then checks
each URL pattern in the URLconf sequentially, comparing the requested URL with each
pattern until it finds a match.
4. Matching URL Patterns: Django compares the requested URL with each URL pattern
in the URLconf. When it finds a pattern that matches the requested URL, Django
proceeds to the next step.
5. Calling View Function: Once Django finds a URL pattern that matches the requested
URL, it calls the view function associated with that pattern. The view function receives
an HttpRequest object as its first parameter.
6. Generating Response: The view function processes the request and returns an
HttpResponse object. This response can include HTML content, redirections, or any
other HTTP response. Django then converts the HttpResponse object into a proper
HTTP response with the appropriate headers and body.

Your Second View: Dynamic Content


For the second view in Django, we're tasked with creating a more dynamic web page that
displays the current date and time. Unlike the static "Hello world" view, this one will leverage
Python's datetime module to calculate and display real-time data.

To achieve this, we'll start by importing the datetime module in Python. Then, we'll use the
datetime.now() function to retrieve the current date and time. This information will be stored
in a variable, let's say 'now'. Here's a snippet demonstrating how to use it:
import datetime
now = datetime.datetime.now()
print(now)
Running this code will output the current date and time in the format 'YYYY-MM-DD
HH:MM:SS.microseconds'. This functionality is independent of Django and is purely Python
code.
In Django, we'll integrate this functionality into a view. The view will calculate the current date
and time using the datetime module and return an HttpResponse containing this value. This
will result in a dynamic web page that updates with the current date and time every time it's
accessed.

9
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

from django.http import HttpResponse

import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
In this example, the current_datetime view function is defined within views.py. It calculates
the current date and time using datetime.datetime.now() and formats it into an HTML string.
This string is then embedded into the HttpResponse object and returned to the client.
1. Importing datetime Module:
 An import statement import datetime was added to the top of the module,
allowing us to use Python's datetime module to calculate dates and times.
2. Defining the current_datetime Function:
 The current_datetime function is defined to calculate the current date and time
as a datetime object and store it as the local variable now.
3. Constructing HTML Response:
 The second line of code within the view constructs an HTML response using
Python's string formatting capability. The %s placeholder within the string is
replaced with the value of the variable now, resulting in an HTML string
containing the current date and time.
4. Returning HttpResponse Object:
 Finally, the view returns an HttpResponse object containing the generated
HTML response, similar to the hello view.
Next, after adding the current_datetime function to views.py, we need to update the URL
configuration in urls.py to map the URL /time/ to the current_datetime view:
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime
urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
)

10
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

Two changes were made to urls.py:

1. First, we imported the current_datetime function at the top.


2. Second, we added a URL pattern mapping the URL /time/ to the current_datetime
view.
With the view implemented and the URL configuration updated, we can start the
Django development server (runserver) and visit http://127.0.0.1:8000/time/ in the
browser to see the current date and time displayed on the web page.

URLconfs and Loose Coupling:


One of the fundamental principles underlying Django, including its URLconfs, is the
concept of loose coupling. This software development approach emphasizes the importance
of making components interchangeable, such that changes made to one component have
minimal or no impact on others.
In the context of Django's URLconfs, the definitions of URLs and the view functions they
call are loosely coupled. This means that the decision regarding the URL structure for a
particular function and the implementation of that function reside in separate places. As a
result, modifying one aspect does not necessitate changes to the other, enhancing flexibility
and maintainability.

URLconfs and views exemplify loose coupling in action within Django. Throughout this
book, we will continue to highlight examples of this important philosophy, showcasing how
Django's design promotes modularity, flexibility, and ease of maintenance.

Pretty URLs and Wildcard URL Patterns:


In Django, there's a strong emphasis on using clean and user-friendly URLs, often referred
to as "pretty URLs." Instead of using query string parameters like other platforms, Django
encourages the use of structured URLs that convey meaning and are easy to understand.
To handle arbitrary hour offsets in our application, we utilize wildcard URL patterns. These
patterns are defined using regular expressions and can match various URL structures. By
setting a limit on the maximum allowed offset, we ensure that our application remains
robust and user-friendly.
With this approach, Django makes it straightforward to implement elegant and readable
URLs, enhancing the overall quality of our web application and improving the user
experience.
urlpatterns = patterns('',
# ...

11
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

(r'^time/plus/\d+/$', hours_ahead),

# ...
)
(r'^time/plus/\d{1,2}/$', hours_ahead)

Coding Order
Here’s the view code:
from django.http import Http404, HttpResponse
import datetime
def hours_ahead(request, offset):
try:
offset = int(offset)
except ValueError:
raise Http404()
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
Explanation:->

1. It returns HttpResponse(html)
The function takes two parameters: request, which is an HttpRequest object, and
offset, which represents the hour offset captured from the URL.

2. The offset parameter holds the string captured by the URL pattern's parentheses. For
example, if the URL is /time/plus/3/, offset would be the string '3'.
3. The first action within the function is to convert the offset string to an integer using the
int() function. This ensures that we have a numeric value to work with.
4. It's worth noting that Python may raise a ValueError if the offset string cannot be
converted to an integer, such as when it contains non-numeric characters like 'foo'. In
such cases, we raise a Http404 exception, resulting in a "Page not found" error.
5. Next, we calculate the current date and time using datetime.datetime.now() and add
the specified number of hours to it. This is achieved by creating a datetime.timedelta
object representing the time duration and adding it to the current datetime object.
6. The HTML output of the view is constructed using Python's format-string capability,
incorporating both the hour offset (offset) and the calculated datetime (dt).

12
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

7. Finally, an HttpResponse containing the generated HTML is returned to the client.

Testing the Functionality:


To verify the functionality of the hours_ahead view function, we can visit different URLs, such
as /time/plus/3/, /time/plus/5/, and /time/plus/24/, to see the time offset by the specified number
of hours.
Additionally, attempting to access URLs with invalid hour designations, such as
/time/plus/100/ (which exceeds the maximum allowed offset), or /time/plus/ (without
specifying an hour), should result in Django displaying a "Page not found" error, maintaining
robust input validation.

Django’s Pretty Error Pages:


When developing web applications with Django, encountering errors is inevitable. However,
Django provides elegant error pages that not only inform you about the error but also offer
valuable insights for debugging and troubleshooting.
def hours_ahead(request, offset):
# try:
# offset = int(offset)
# except ValueError:
# raise Http404()
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)

return HttpResponse(html)
Now, if we visit a URL like /time/plus/3/, we'll encounter an error page displaying a
TypeError message, indicating "unsupported type for timedelta hours component: unicode".
What's happening here? The datetime.timedelta function expects the hours parameter to be
an integer, but since we've commented out the code that converts offset to an integer, offset
remains a string. Consequently, datetime.timedelta raises a TypeError.
Analyse Django's error page and its various components:
 At the top, crucial information about the exception is provided, including the type of
exception, parameters, file name, and line number.
 Beneath the key information, Django presents the full Python traceback, illustrating the
sequence of function calls and the source code involved.

13
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62

 Clicking on any line of source code expands to show contextual information, displaying
lines before and after the erroneous line.
 The "Local vars" section displays a table of local variables and their values at the point
of the exception, aiding in debugging.
 You can switch to a copy-and-paste view for easy sharing of the traceback with others.
 The "Share this traceback on a public Web site" button allows you to post the traceback
to a public website, generating a shareable URL for collaboration.
 The "Request information" section provides details about the incoming web request,
such as GET and POST data, cookies, and CGI headers.
 Finally, the "Settings" section lists all Django settings for the current installation,
offering insights into the project's configuration.
-------------------------------------END OF MODULE 1----------------------------------------

14
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Django Templates
and Models
Module 2 – Chapter 1
Table of contents

01 Django Templates

02 Django Models
01
Django Templates
Template System Basics
• A Django template is a string of text that is intended to
separate the presentation of a document from its data.
• A template defines placeholders and various bits of basic logic
(i.e., template tags) that regulate how the document should be
displayed.
• Usually, templates are used for producing HTML, but Django
templates are equally capable of generating any text-based
format.
How to use templates
To use the template system in Python code, just follow these two
steps:
1. Create a Template object by providing the raw template code as
a string. Django also offers a way to create Template objects by
designating the path to a template file on the filesystem.
2. Call the render() method of the Template object with a given set
of variables (i.e., the context). This returns a fully rendered
template as a string, with all of the variables and block tags
evaluated according to the context.
1. Creating template objects
• The easiest way to create a Template object is to instantiate it directly.
• The Template class lives in the django.template module, and the
constructor takes one argument, the raw template code.
• When using Django, we need to tell Django which settings to use.
Interactively, this is typically done using python manage.py shell.
• When you create a Template object, the template system compiles the
raw template code into an internal, optimized form, ready for
rendering.
• But if your template code includes any syntax errors, the call to
Template() will cause a TemplateSyntaxError exception:
Creating template objects
The system raises a TemplateSyntaxError
exception for any of the following cases:
1. Invalid block tags
2. Invalid arguments to valid block tags
3. Invalid filters
4. Invalid arguments to valid filters
5. Invalid template syntax
6. Unclosed block tags (for block tags
that require closing tags)
2. Rendering a template
• Once you have a Template object, you can pass it data by giving it a context.
• A context is simply a set of variables and their associated values.
• A template uses this to populate its variable tags and evaluate its block tags.
• A context is represented in Django by the Context class, which lives in the
django.template module.
• Its constructor takes one optional argument: a dictionary mapping variable
names to variable values.
• Call the Template object’s render() method with the context to “fill” the
template:
2. Rendering a template
Dictionaries and context
• A Python dictionary is a mapping between known keys and
variable values.
• A Context is similar to a dictionary, but a Context provides
additional functionality
• Variable names must begin with a letter (A-Z or a-z) and
may contain digits, underscores, and dots.
• Variable names are case sensitive.
Multiple context same template
• Once you have a Template object, you can render multiple contexts through it.

• Whenever you’re using the same template source to render multiple contexts like this, it’s
more efficient to create the Template object once, and then call render() on it multiple times:
Context variable look up
The template system elegantly handles more complex data structures, such as lists, dictionaries, and
custom objects.
The key to traversing complex data structures in Django templates is the dot character (.).
1. Use a dot to access dictionary keys, attributes, indices, or methods of an object.
2. dots also allow access of object attributes.
For example, a Python datetime.date object has year, month, and day attributes, and you can use a dot
to access those attributes in a Django template:
Context variable look up
3. Dots are also used to call methods on objects. For example, each
Python string has the methods upper() and isdigit(), and you can call
those in Django templates using the same dot syntax:
Context variable look up
4. dots are also used to access list indices.

Negative list indices are not allowed. For example, the template
variable {{ items.-1 }} would cause a TemplateSyntaxError.
Context variable look up
Dot lookups can be nested multiple levels deep
Basic Template Tags and Filters
TAGS:
1. IF ELSE
The {% if %} tag evaluates a variable, and if that variable is “true”,
the system will display everything between {% if %} and {% endif %}.

{% if variable %}
// statements
{% else %}
// statements
{% endif %}
Basic Template Tags and Filters
• The {% if %} tag accepts and, or, or not for testing multiple
variables, or to negate a given variable.
• {% if %} tags don’t allow and and or clauses within the same tag,
because the order of logic would be ambiguous.
• The use of parentheses for controlling order of operations is not
supported.
• There is no {% elif %} tag. Use nested {% if %} tags to accomplish
the same thing:
• Make sure to close each {% if %} with an {% endif %}. Otherwise,
Django will throw a TemplateSyntaxError.
Basic Template Tags and Filters
2. for
• The {% for %} tag allows you to loop over each item in a sequence.
• As in Python’s for statement, the syntax is for X in Y, where Y is the
sequence to loop over and X is the name of the variable to use for a
particular cycle of the loop.
• Each time through the loop, the template system will render
everything between {% for %} and {% endfor %}.
• Syntax:
for
Keywords :
1. reversed: reverses the list items
Loop variables
1. forloop.counter
2. forloop.counter0
3. forloop.revcounter
4. forloop.revcounter0
5. forloop.first
6. forloop.last
7. forloop.parentloop
for
forloop.counter:

• set to an integer representing the number of times the loop has


been entered.
• This is one-indexed, so the first time through the loop,
forloop.counter will be set to 1.
Eg:
{% for i in name%}
<p> {{forloop.counter}}:{{i}}</p>
{% endfor%}
Output: 1:a
2:b
3:c
for
forloop.counter0:

• like forloop.counter, except it’s zero-indexed. Its value will be set


to 0 the first time through the loop.
Example:
{% for i in name%}
<p> {{forloop.counter0}}:{{i}}</p>
{% endfor%}
Output: 0:a
1:b
2:c
for
forloop.revcounter:
• Always set to an integer representing the number of remaining
items in the loop.
• The first time through the loop, forloop.revcounter will be set to the
total number of items in the sequence you’re traversing.
• The last time through the loop, forloop.revcounter will be set to 1.
Example:
{% for i in name%}
<p> {{forloop.revcounter}}:{{i}}</p>
{% endfor%}
for
forloop.revcounter0:
• like forloop.revcounter, except it’s zero-indexed.
• The first time through the loop, forloop.revcounter0 will be set to
the number of elements in the sequence minus 1.
• The last time through the loop, it will be set to 0.
Example:
{% for i in name%}
<p> {{forloop.revcounter0}}:{{i}}</p>
{% endfor%}
for
forloop.first:
set a Boolean value True for the first occurrence and the rest of the
iterations will be set to False. {% for i in name%}
Example:
<p> {{forloop.first}}:{{i}}</p>
{% endfor%}
Output: True:a
False:b
False:c
for
forloop.last:
set a Boolean value True for the last iteration and the rest of the
iterations set to False.
Example:
<p> {{forloop.last}}:{{i}}</p>
{% endfor%}
Output: False:a
False:b
True:c
Comments
• Just as in HTML or in a programming language such as Python,
the Django template language allows for comments. To designate a
comment, use{# #}:
{# This is a comment #}
The comment will not be output when the template is rendered.
• A comment cannot span multiple lines. This limitation improves
template parsing performance.
This is a {# this is not
a comment #}
test.
Filters
• Django Template Engine provides filters which are used to
transform the values of variables and tag arguments.
• Template filters are simple ways of altering the value of variables
before they’re displayed.
• Tags can’t modify value of a variable whereas filters can be used
for incrementing value of a variable or modifying it to one’s own
need.
• Filters look like this:
Syntax
{{ variable_name | filter_name }}
Filters
• Filters can be “chained.” The output of one filter is applied to the
next. {{ text|escape|linebreaks }} is a common idiom for escaping
text contents, then converting line breaks to <p> tags.

Example
{{ value | length }}
If value is [‘a’, ‘b’, ‘c’, ‘d’], the output will be 4.
Filters
Some filters take arguments. A filter argument looks like this:
{{ bio|truncatewords:"30" }}
This displays the first 30 words of the bio variable. Filter arguments
are always in double quotes.
Filters
add
• It is used to add an argument to the value. Example
{{ value | add:"2" }}
• If value is 4, then the output will be 6. This filter is used
to increment a variable in django Templates.
addslashes
• It is used to add slashes before quotes. Useful for escaping strings
in CSV. Example
{{ value | addslashes }}
If value is “I’m Jai”, the output will be “I\’m Jai”
Filters
date
It is used to format a date according to the given format. Example
{{ value | date:"D d M Y" }}
If value is a datetime object
(e.g., the result of datetime.datetime.now()), the output will be the
string ‘Thu 06 Feb 2020’.
Filters
escape
It is used to escape a string’s HTML. Specifically, it makes these
replacements:
Philosophies
• Business logic should be separated from presentation logic

• Syntax should be decoupled from HTML/XML

• Designers are assumed to be comfortable with HTML code

• Designers are assumed not to be Python programmers

• The goal is not to invent a programming language.


Limitations
A template cannot Set a variable or change the value of a
variable.
• It’s possible to write custom template tags that accomplish these
goals, but the stock Django template tags do not allow it.
A template cannot call raw Python code.
• There’s no way to “drop into Python mode” or use raw Python
constructs. Again, it’s possible to write custom template tags to do
this, but the stock Django template tags don’t allow it.
Using Templates in Views
Using Templates in Views
Template Loading
Django provides a convenient and powerful API for loading templates
from disk, with the goal of removing redundancy both in your
template-loading calls and in your templates themselves.
The place to do this is in your settings file, settings.py
EMPLATE_DIRS = ( '/home/django/mysite/templates', )
or
TEMPLATE_DIRS = [ '/home/django/mysite/templates' ]
Template Loading
Django provides a convenient and powerful API for loading templates
from disk, with the goal of removing redundancy both in your
template-loading calls and in your templates themselves.
The place to do this is in your settings file, settings.py
EMPLATE_DIRS = ( '/home/django/mysite/templates', )
or
TEMPLATE_DIRS = [ '/home/django/mysite/templates' ]
Template Loading
There are a few things to note:
• You can specify any directory you want, as long as the directory
and templates within that directory are readable by the user
account under which your Web server runs.
• If you can’t think of an appropriate place to put your templates,
we recommend creating a templates directory within your Django
project.
• Don’t forget the comma at the end of the template directory string!
Template Loading
There are a few things to note:
• You can specify any directory you want, as long as the directory
and templates within that directory are readable by the user
account under which your Web server runs.
• If you can’t think of an appropriate place to put your templates,
we recommend creating a templates directory within your Django
project.
• Don’t forget the comma at the end of the template directory string!
Template Loading
With TEMPLATE_DIRS set, the next step is to change the view code
to use Django’s template- loading functionality rather than hard-
coding the template paths. Returning to our current_ datetime view,
let’s change it like so:
Template Loading
With TEMPLATE_DIRS set, the next step is to change the view code
to use Django’s template- loading functionality rather than hard-
coding the template paths. Returning to our current_ datetime view,
let’s change it like so:
render_to_response()
Because it’s such a common idiom to load a template, fill a Context,
and return an HttpResponse object with the result of the rendered
template,
Django provides a shortcut that lets you do those things in one line
of code.
This shortcut is a function called render_to_response(), which lives in
the module django.shortcuts.
Most of the time, you’ll be using render_to_response() rather than
loading templates and creating Context and HttpResponse objects
manually.
render_to_response()
The locals() Trick
Subdirectories in get_template()
It can get unwieldy to store all of your templates in a single directory.
You might like to store templates in subdirectories of your template
directory, and that’s fine.
In fact, we recommend doing so; some more advanced Django features
expect this template layout as a default convention.
The include Template Tag

The include tag allows us to include the contents of a template

inside another template. Here is the syntax of the include tag:

{% include template_name %}
Template Inheritance
extends tag in Templates

extends tag is used for inheritance of templates in django.

One needs to repeat the same code again and again.

Using extends we can inherit templates as well as variables.

{% extends 'template_name.html' %}
Full Stack Development 21CS62

MODULE 2
MODELS
The notes are prepared from the prescribed textbook

In modern web applications, arbitrary logic frequently involves database interactions.


Typically, a database-driven website connects to a database server, retrieves data, and
displays it on a web page. These sites may also allow visitors to add data to the database.
Complex websites often combine both functionalities.

For instance, Amazon.com is a prime example of a database-driven site, where each product
page is a query into Amazon's product database displayed as HTML, and customer reviews
are inserted into a reviews database.

Django is particularly well-suited for building database-driven websites due to its powerful
tools for performing database queries using Python. Django's functionality for database
management is designed to be both easy to use and highly effective, streamlining the
development of such web applications.

The "Dumb" Way to Do Database Queries in Views


This method involves using an existing Python library to execute an SQL query and process
the results.
from django.shortcuts import render_to_response
import MySQLdb
def book_list(request):
db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
cursor = db.cursor()
cursor.execute('SELECT name FROM books ORDER BY name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})

While this approach works, it has several significant drawbacks:


1. Hard-Coded Database Connection Parameters: The connection parameters (user,
password, database name, host) are hard-coded into the view. Ideally, these should be
stored in Django's configuration settings.

1
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

2. Boilerplate Code: This method requires writing repetitive boilerplate code for
creating a connection, executing a query, and closing the connection. Ideally, we
should only need to specify the desired results.
3. Database-Specific Code: This approach ties the code to a specific database (MySQL
in this case). Switching to a different database (e.g., PostgreSQL) would necessitate
using a different adapter, changing connection parameters, and possibly rewriting
SQL statements. Ideally, the database should be abstracted to allow for easy
switching.

Django’s Database API

Django’s database layer addresses these issues, making database interactions simpler and
more abstract.

from django.shortcuts import render_to_response

from mysite.books.models import Book

def book_list(request):

books = Book.objects.order_by('name')

return render_to_response('book_list.html', {'books': books})

This version avoids hard-coded parameters, boilerplate code, and database-specific


dependencies by leveraging Django's ORM (Object-Relational Mapping) system.

Django's Interpretation of MVC and MTV

Django follows the Model-View-Controller (MVC) pattern closely enough to be considered


an MVC framework, but it has its own interpretation that shifts some roles. Here's how
Django breaks down the MVC components:

Model (M): The data-access portion, managed by Django’s database layer. This handles
everything related to data: access, validation, behaviors, and relationships.

View (V): Handles selecting which data to display and how to display it, managed through
views and templates.

Controller (C): Delegates to a view based on user input, managed by Django's framework
via URLconf, which maps URLs to appropriate Python functions (views).

Django's MTV Pattern


Due to its unique handling of MVC, Django is often described as an MTV (Model-Template-
View) framework:

2
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Model (M): The data access layer, containing all data-related logic including access
methods, validation, behaviors, and relationships.
 Template (T): The presentation layer, deciding how data should be displayed on web
pages or other documents.
 View (V): The business logic layer, bridging models and templates. Views contain the
logic to access models and delegate data to appropriate templates for rendering.
MVC vs. MTV in Django
In traditional MVC frameworks like Ruby on Rails, the controller decides which data to
present, and the view defines how this data looks. In Django:
 The view (in MTV) or "controller" (in MVC) handles both selecting the data and
delegating it to templates for display.
 The template in Django's MTV is akin to the "view" in MVC, focusing strictly on
presentation.
Configuring the Database in Django
Set Up Your Database Server:
 Ensure that your database server is set up, activated, and that a database has been
created within it (e.g., using a CREATE DATABASE statement).
 If you’re using SQLite, this step is not required as SQLite uses standalone files on the
filesystem to store data.
Edit the Django Settings File:
 Open the settings.py file in your Django project directory. This file contains all the
configuration settings for your Django project, including database settings.
Database Configuration Settings:
 Look for the following settings in settings.py and update them with your database
configuration:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.<database_backend>', # E.g., 'sqlite3', 'postgresql',
'mysql', 'oracle'
'NAME': '<database_name>', # Database name or path to database file
if using SQLite
'USER': '<database_user>', # Database user (not required for SQLite)
'PASSWORD': '<database_password>', # Database password (not required
for SQLite)

3
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

'HOST': '<database_host>', # Set to empty string for localhost, not


required for SQLite
'PORT': '<database_port>', # Set to empty string for default, not
required for SQLite
}}
To ensure proper functionality, we'll need to download and install the appropriate database
adapter for your chosen backend. Each adapter is freely available online; follow the links
provided in the "Required Adapter" column in the documentation. If we're using Linux, our
distribution's package management system may offer convenient packages, such as python-
postgresql or python-psycopg.
Here's a breakdown of the database settings and their implications:
 DATABASE_ENGINE: This setting specifies the database backend to use. For
PostgreSQL, you'd use 'postgresql_psycopg2'.
 DATABASE_NAME: Specifies the name of your database. For SQLite, you'd
provide the full filesystem path to the database file.
 DATABASE_USER: Specifies the username for connecting to the database. For
SQLite, leave this blank.
 DATABASE_PASSWORD: Specifies the password for connecting to the database.
For SQLite or empty passwords, leave this blank.
 DATABASE_HOST: Specifies the host for connecting to the database. For localhost
connections, leave this blank. For MySQL Unix socket connections, you can specify
the socket path.
Once we 've configured these settings in your settings.py file, it's advisable to test your
configuration. Run python manage.py shell from your project directory to activate the Python
interpreter with the correct Django settings.
In the shell, execute the following commands to test the database configuration:
from django.db import connection
cursor = connection.cursor()
Database Configuration Error Messages
1. Error: You haven’t set the DATABASE_ENGINE setting yet.
Solution: Set the DATABASE_ENGINE setting in your settings.py file to a valid
database engine. For example, for PostgreSQL, you would use 'postgresql_psycopg2'.
2. Error: Environment variable DJANGO_SETTINGS_MODULE is undefined.
Solution: Instead of running python, run python manage.py shell to ensure that
Django's settings are properly activated.

4
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

3. Error loading _____ module: No module named _____.


Solution: You haven't installed the appropriate database-specific adapter (e.g.,
psycopg2 for PostgreSQL or MySQLdb for MySQL). Install the required adapter
using pip or your package manager.
4. _____ isn’t an available database back-end.
Solution: Check your DATABASE_ENGINE setting in settings.py for typos or
incorrect values. Ensure it matches one of the valid engine settings described earlier.
5. Database _____ does not exist.
Solution: Either change the DATABASE_NAME setting in settings.py to point to an
existing database or execute the appropriate CREATE DATABASE statement in your
database management tool to create it.
6. Role _____ does not exist.
Solution: Update the DATABASE_USER setting in settings.py to point to an existing
database user or create the user in your database management tool.
7. Could not connect to server.
Solution: Ensure that DATABASE_HOST and DATABASE_PORT settings in
settings.py are correctly configured to point to your database server. Also, verify that
your database server is running and accessible from your Django application
environment.
Project:
 A project is an instance of a certain set of Django apps, along with the configuration
for those apps.
 At its core, a project provides a settings file defining database connection information,
installed apps, template directories, etc.
 Projects are where you configure and manage your overall Django application.
App:
 An app is a portable set of Django functionality packaged together in a single Python
package.
 Typically, an app includes models, views, and other functionalities.
 Django comes with several built-in apps like a commenting system and an admin
interface.
 Apps are designed to be reusable across multiple projects, promoting modularity and
code reusability.

5
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Reasons for Defining Models in Python:


1. Introspection Overhead: Introspecting the database at runtime introduces overhead
and is imperfect. Explicitly describing the data in Python reduces this overhead.
2. Consistency and Productivity: Writing Python code is more enjoyable and
productive than switching between SQL and Python. Keeping data models in Python
enhances productivity and maintains a single programming environment.
3. Version Control: Storing data models as code makes it easier to manage changes and
keep track of revisions using version control systems.
4. Higher-level Data Types: Django models offer higher-level data types (e.g., for email
addresses or URLs) that SQL might lack, enhancing productivity and code reusability.
5. Cross-Platform Compatibility: Distributing a Python module that describes data
layouts is more pragmatic than distributing separate sets of SQL statements for
different database platforms.
Drawback and Strategies:
 A drawback of defining models in Python is the possibility of code getting out of sync
with the actual database schema.
 When making changes to Django models, corresponding changes must be made in the
database to maintain consistency.
 Strategies for handling this problem include using Django's migration system to
manage changes to database schema and employing version control to track
modifications to models and database schema.
Your First Model
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()

class Author(models.Model):

6
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()

class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
Summary:
 Each Django model corresponds to a SQL table.
 Model attributes translate to table columns, with field types mapped to appropriate
SQL column types.
 Relationships between models are managed via foreign keys and many-to-many join
tables.
 Django handles the creation and management of these relationships automatically,
providing a high-level API to interact with the data.
CREATE TABLE "books_publisher" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,
"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
);
Automatic Table Creation with Django
As mentioned, Django can generate the SQL CREATE TABLE statements automatically
based on the models defined in your models.py file. This is achieved using Django’s
migrations system. When you define your models, Django’s migration system can generate
the SQL commands required to create the corresponding tables in your database.

7
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

The Many-to-Many Relationship


In the provided models, the Book model has a ManyToManyField for authors. This indicates
a many-to-many relationship between books and authors, which Django handles by creating
an additional join table.

Points to Note
 Table Names: Table names are generated by combining the app name (books) and the
model’s name (in lowercase). For example, the Publisher model becomes
books_publisher.
 Primary Keys: Django automatically adds an id field as the primary key for each
table unless explicitly overridden.
 Foreign Keys: Foreign key fields have _id appended to their names. For example, the
publisher field in the Book model becomes publisher_id in the database.
 References: Foreign key relationships are explicitly defined using the REFERENCES
statement, ensuring referential integrity.
 Database-Specific SQL: The SQL generated by Django is tailored to the specific
database backend being used (e.g., PostgreSQL, MySQL, SQLite). This ensures
compatibility and correct syntax for the target database.
BEGIN;
CREATE TABLE "books_publisher" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,
"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
);
CREATE TABLE "books_author" (
"id" serial NOT NULL PRIMARY KEY,

8
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

"first_name" varchar(30) NOT NULL,


"last_name" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL
);
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id")
DEFERRABLE INITIALLY DEFERRED,
"publication_date" date NOT NULL
);
CREATE TABLE "books_book_authors" (
"id" serial NOT NULL PRIMARY KEY,
"book_id" integer NOT NULL REFERENCES "books_book" ("id")
DEFERRABLE INITIALLY DEFERRED,
"author_id" integer NOT NULL REFERENCES "books_author" ("id")
DEFERRABLE INITIALLY DEFERRED,
UNIQUE ("book_id", "author_id")
);
CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id");
COMMIT;
Basic Data Access
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',

9
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

... website='http://www.oreilly.com/')
>>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
Explanation:
Model Import: from books.models import Publisher imports the model so you can interact
with the corresponding database table.
Instantiation and Saving: p1.save() saves the instantiated object to the database.
Model Manager: Publisher.objects is the default manager that provides methods to query the
database.
Deferred Saving: Objects are not saved to the database until the save() method is called.
Immediate Creation and Saving: Publisher.objects.create() creates and saves the object in
one go.
These few lines of code accomplish quite a bit. Here are the highlights:
• First, import the Publisher model class. This lets you interact with the database table that
contains publishers.
• Create a Publisher object by instantiating it with values for each field: name, address, and so
on.
• To save the object to the database, call its save() method. Behind the scenes, Django
executes an SQL INSERT statement here.
• To retrieve publishers from the database, use the attribute Publisher.objects, which you can
think of as a set of all publishers. Fetch a list of all Publisher objects in the database with the
statement Publisher.objects.all(). Behind the scenes, Django executes an SQL SELECT
statement here.
Or using the create() method:
>>> from books.models import Publisher
>>> p1 = Publisher.objects.create(name='Apress', address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')
>>> p2 = Publisher.objects.create(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',

10
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

... website='http://www.oreilly.com/')
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
Adding Model String Representations in Django
When you work with Django models and query the database, the default string representation
of objects may not be very informative. By defining a __str__() method for each model, you
can customize the string representation of your objects, making it easier to identify them
when printed or logged.
Modifying the Models with __str__() Methods
Here’s how you can add the __str__() method to your Publisher, Author, and Book models:
1. Publisher Model: Return the name of the publisher.
2. Author Model: Return the full name of the author.
3. Book Model: Return the title of the book
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()

def __str__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()

11
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

def __str__(self):
return f'{self.first_name} {self.last_name}'
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
publication_date = models.DateField()
def __str__(self):
return self.title
What Are Unicode Objects?
In Python, a Unicode object is essentially a string that can represent a vast array of characters
from different languages and symbols, not limited to the standard ASCII characters. This
capability allows Unicode strings to handle international characters and special symbols
seamlessly.
Normal Python Strings and Encodings
 Encoded Strings: In Python, standard strings (before Python 3) are encoded, meaning
they are stored using a specific encoding format such as ASCII, ISO-8859-1, or UTF-
8.
 Encoding Issues: When dealing with characters outside the standard ASCII range
(e.g., accented letters, non-Latin characters), it's crucial to know the encoding used.
Mixing different encodings without proper handling can result in garbled text,
commonly seen as “??? ??????” in web pages or emails.
Unicode Strings
 No Encoding: Unicode objects use a consistent and universal set of characters,
known as Unicode, which eliminates the encoding issues found with standard strings.
 Mixing and Matching: Since Unicode strings don’t rely on a specific encoding, you
can combine them without worrying about encoding mismatches.
Unicode in Django
 Consistency: Django uses Unicode objects throughout its framework. This means
model objects, views, and templates all handle data as Unicode, ensuring consistent
and correct handling of text.
 Simplicity: As a developer, you generally don’t need to manage or convert encodings
manually when using Django. The framework takes care of these details, allowing
you to focus on the logic and functionality of your application.

12
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

When you create an instance of a model class in Django, like p = Publisher(name='Apress',


...), Django doesn't immediately insert that data into the database. Instead, it holds onto it
until you explicitly call the save() method on that instance. This deferred insertion is an
efficient way to batch up database writes.
When you call save() for the first time on an instance that hasn't been saved yet, Django
translates this into an SQL INSERT statement, adding a new row to the database table. As
you correctly mentioned, because the primary key id is auto-incrementing, Django calculates
the next available ID for this record and assigns it to the id attribute of the instance.
Here's a quick summary:
Instantiation: Creating an instance of a model class does not affect the database.
Insertion: Calling the save() method on a new instance inserts the data into the database.
Primary Key Assignment: If the model uses an auto-incrementing primary key, Django
assigns the next available ID to the instance's id attribute.
Subsequent Saves: If you call save() again on an already saved instance, Django updates the
existing record in the database rather than creating a new one.
Here’s how to insert a new Publisher record:
# Instantiate the model class with keyword arguments
p = Publisher(
name='Apress',
address='2855 Telegraph Ave.',
city='Berkeley',
state_province='CA',
country='U.S.A.',
website='http://www.apress.com/'
)
# Save the instance to the database
p.save()
Auto-incrementing Primary Key
When the save() method is called for the first time, Django assigns an auto-incrementing
primary key value to the id attribute of the instance:
>>> p.id
52 # this value will differ based on your own data

13
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Updating Data
To update a record in the database:
1. Modify the Fields: Change the values of the fields you want to update.
2. Save the Instance: Call the save() method again to update the record in the database.
# Change the name attribute
p.name = 'Apress Publishing'
# Save the instance to update the record in the database
p.save()
Complete Update: When calling save(), Django updates all the fields of the record, not just
the ones that have changed. This can be a performance consideration if you are updating
multiple fields unnecessarily.
Potential Race Conditions: If multiple processes are trying to update the same record
simultaneously, it can cause race conditions. To update only the changed fields and
potentially avoid such conditions, you can use the update() method on a query set,
Selecting Objects
Knowing how to create and update database records is essential, but chances are that the Web
applications you’ll build will be doing more querying of existing objects than creating new
ones. You’ve already seen a way to retrieve every record for a given model:
>>> Publisher.objects.all()
[<Publisher: Apress>, <Publisher: O'Reilly>]
This roughly translates to this SQL:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher;
Model Definition: We start with the model we want to query, which in this case is Publisher.
This is the Python class representing the database table.
Manager (objects): The .objects attribute is a manager. Managers handle all table-level
operations, including data lookup. Every model automatically gets an objects manager.
Method (all()): We call the .all() method on the manager. This method retrieves all rows from
the database table associated with the model. It returns a QuerySet, which represents the
result set of the query.
Filtering Data

14
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Filtering data in Django involves using the filter() method on a model's manager. This
method allows you to narrow down querysets based on specific conditions, which are
translated into SQL WHERE clauses.
You can pass multiple arguments to filter() for more refined queries, and Django provides
various lookup types to perform different types of filtering, such as exact matches, substring
searches, case-insensitive searches, and more.
The double underscore (__) syntax indicates special operations or lookups, and Django
supports a wide range of lookup types to cater to diverse filtering requirements.
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
You can pass multiple arguments into filter() to narrow down things further:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]
Retrieving Single Objects
The previous filter() examples all returned a QuerySet, which you can treat like a list.
Sometimes it’s more convenient to fetch only a single object instead of a list. That’s what the
get()
method is for:
>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>
Instead of a list (rather, QuerySet), only a single object is returned. Because of that, a query
resulting in multiple objects will cause an exception
>>> Publisher.objects.get(country="U.S.A.")
Traceback (most recent call last):
...
MultipleObjectsReturned: get() returned more than one Publisher --
it returned 2! Lookup parameters were {'country': 'U.S.A.'}
A query that returns no objects also causes an exception:
>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
...

15
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

DoesNotExist: Publisher matching query does not exist.


The DoesNotExist exception is an attribute of the model’s class: Publisher.DoesNotExist.
In your applications, you’ll want to trap these exceptions, like this:
try:
p = Publisher.objects.get(name='Apress')
except Publisher.DoesNotExist:
print "Apress isn't in the database yet."
else:
print "Apress is in the database."

Ordering Data
In Django, when retrieving data from the database, the order in which the objects are returned
might appear random unless specified otherwise. To order the results based on a specific
field, you can use the order_by() method.
Publisher.objects.order_by("name")
The result would be a list of Publisher objects sorted by their names.
You can order by any field in the model, such as address or state_province, by passing the
field name as an argument to order_by().
Publisher.objects.order_by("address")
Publisher.objects.order_by("state_province")
In Django, you can order query results by multiple fields using the order_by() method with
multiple arguments.
Publisher.objects.order_by("state_province", "address")
This will first order the results by the state_province field and then by the address field,
allowing for disambiguation in cases where the state_province values are the same.
Additionally, you can specify reverse ordering by prefixing the field name with a minus sign
(-):
Publisher.objects.order_by("-name")
To avoid repetitive use of order_by() for common ordering scenarios, you can specify a
default ordering in the model's Meta class.
class Publisher(models.Model):
name = models.CharField(max_length=30)

16
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

# Other fields...
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
class Publisher(models.Model):
name = models.CharField(max_length=30)
# Other fields...

def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
Chaining Lookups
Chaining lookups in Django allows you to combine multiple operations like filtering and
ordering in a single query
Publisher.objects.filter(country="U.S.A.").order_by("-name")

This code filters Publisher objects by country "U.S.A." and then orders the results by the
name field in descending order.

The resulting SQL query includes both a WHERE clause to filter the data and an ORDER BY
clause to specify the ordering:

SELECT id, name, address, city, state_province, country, website


FROM books_publisher
WHERE country = 'U.S.A'
ORDER BY name DESC;
Slicing
Slicing data in Django allows you to retrieve a fixed number of rows or a specific subset of
data from the database.
Publisher.objects.order_by('name')[0]
This corresponds to the following SQL query:

17
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

SELECT id, name, address, city, state_province, country, website


FROM books_publisher
ORDER BY name
LIMIT 1;
Similarly, to retrieve a specific subset of data, such as the first two publishers:
Publisher.objects.order_by('name')[0:2]
Which translates to:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;
Updating Multiple Objects in One Statement
We pointed out in the “Inserting and Updating Data” section that the model save() method
updates all columns in a row. Depending on your application, you might want to update only
a subset of columns.
For example, suppose that you want to update the Apress Publisher to change the name
from 'Apress' to 'Apress Publishing'. Using save(), it would look something like this:
>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()
Deleting Objects
To delete objects in Django, you can use the delete() method.
Deleting a single object:
p = Publisher.objects.get(name="O'Reilly")
p.delete()
Deleting objects in bulk using a filter:
Publisher.objects.filter(country='USA').delete()
Deleting all objects:Publisher.objects.all().delete()
----------------------------------------------END OF MODULE 2---------------------------------------

18
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

MODULE 3 The Django Admin Site


The django.contrib packages in Django are a collection of optional but widely-used
components that extend the core framework's functionality. They provide built-in solutions
for common web development needs, similar to how the Python standard library offers
essential modules for Python development. Here's an overview of some key packages within
django.contrib:
1. Admin Interface (django.contrib.admin):
o This package provides a powerful and customizable administrative interface
for managing the site's content. It allows developers to quickly create a
backend interface for managing data models without writing much code.
2. Authentication System (django.contrib.auth):
o It includes features for user authentication, permission handling, and user
group management. This package is essential for implementing user login,
logout, and registration functionalities in a Django application.
3. Sessions (django.contrib.sessions):
o This package enables session management, allowing developers to store and
retrieve arbitrary data on a per-site-visitor basis. Sessions are useful for
maintaining user-specific data across multiple requests.
4. Messages Framework (django.contrib.messages):
o It provides a way to store messages (like error or success notifications) that
can be displayed to users. These messages are typically used for giving
feedback after form submissions or other user actions.
5. Static Files Handling (django.contrib.staticfiles):
o This package helps manage and serve static files, such as CSS, JavaScript, and
images. It simplifies the process of collecting and deploying static assets in a
Django project.
6. Content Types (django.contrib.contenttypes):
o It allows for the creation of generic relationships between models, making it
possible to create models that can reference any other model.
7. Redirects (django.contrib.redirects):
o This package allows you to manage redirects from within the Django admin. It
is useful for setting up URL redirections when URLs change.
8. Sitemaps (django.contrib.sitemaps):
o It provides tools to generate sitemaps, which are XML files that inform search
engines about the pages on your site available for crawling.

19
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

9. Syndication Feeds (django.contrib.syndication):


o This package offers a framework for generating RSS and Atom feeds for your
site's content.
10. Flat Pages (django.contrib.flatpages):
o It allows you to manage simple static pages (like 'About Us' or 'Contact'
pages) through the admin interface.
11. Sites Framework (django.contrib.sites):
o This package is useful for projects with multiple sites, allowing you to manage
site-specific data.
12. Comments (django.contrib.comments):
o Although deprecated and removed in Django 1.8, this package previously
provided a commenting system for Django applications.
Activating the Admin Interface
The Django admin site is entirely optional, because only certain types of sites need this
functionality. That means you’ll need to take a few steps to activate it in your project.
First, make a few changes to your settings file:
1. Add 'django.contrib.admin' to the INSTALLED_APPS setting. (The order of
INSTALLED_
APPS doesn’t matter, but we like to keep things alphabetical so it’s easy for a human
to read.)
2. Make sure INSTALLED_APPS contains
'django.contrib.auth', 'django.contrib.
contenttypes', and 'django.contrib.sessions'. The Django admin site requires
these three packages. (If you’re following along with our ongoing mysite project, note
that we commented out these three INSTALLED_APPS entries in Chapter 5. Uncomment
them now.)
3. Make sure MIDDLEWARE_CLASSES contains
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', and 'django.contrib.
auth.middleware.AuthenticationMiddleware'.
# Include these import statements...

20
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

from django.contrib import admin admin.autodiscover()


# And include this URLpattern...
urlpatterns = patterns('', # ...
(r'^admin/', include(admin.site.urls)),
# ... )
Log in with the username and password you set up when you added your superuser. If
you’re unable to log in, make sure you’ve actually created a superuser—try running
python manage.py createsuperuser.
Once you’re logged in, the first thing you’ll see will be the admin home page (Figure 6-2).
This page lists all the available types of data that can be edited on the admin site. At this
point,
because we haven’t activated any of our own models yet, the list is sparse: it includes only
Groups and Users, which are the two default admin-editable models.
To recap, activating the Django admin interface involves:
1. Adding the necessary apps (django.contrib.admin, django.contrib.auth,
django.contrib.contenttypes, and django.contrib.sessions) to INSTALLED_APPS.
2. Ensuring the required middleware (CommonMiddleware, SessionMiddleware, and
AuthenticationMiddleware) is in MIDDLEWARE_CLASSES.
3. Including the admin URL pattern in your urls.py.
4. Creating a superuser account.
5. Applying database migrations.
6. Starting the development server and accessing the admin interface.
Adding Your Models to the Admin Site
1. Creating an admin.py file in your application directory.
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
admin.site.register(Publisher)
admin.site.register(Author)
admin.site.register(Book)

21
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

2. Access the Admin Interface


After making these changes, you can access the Django admin site by navigating to
http://127.0.0.1:8000/admin/ in your web browser.
python manage.py runserver

Adding and Managing Records

You can now add, change, and delete records for each of these models. The admin interface
provides an intuitive form for each model, handling foreign keys and many-to-many
relationships seamlessly.

class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
publication_date = models.DateField()

def __unicode__(self):
return self.title
 Restarting the development server to apply changes.
 Navigating to the admin interface and managing your data through the provided forms.
How the Admin Site Works?
The Django admin site operates through a series of steps and mechanisms designed to
streamline the process of managing application data through a web interface.
1. URLconf and Admin Site Initialization
When Django starts up, it loads the URLconf from urls.py. One of the important statements
executed during this process is admin.autodiscover().
2. admin.autodiscover()
The admin.autodiscover() function plays a crucial role in setting up the admin interface:
 It iterates over each app listed in the INSTALLED_APPS setting.
 For each app, it checks for the existence of an admin.py file.
 If an admin.py file is found, it executes the code within that file.
3. admin.py in Each App
The admin.py file is where the admin site is configured for each app:

22
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Within admin.py, the admin.site.register() function is used to register models with the
admin site.
 Only models explicitly registered using admin.site.register() will have an edit/change
interface displayed in the admin site.
4. Built-in and Third-Party Admin Integration
Some Django applications, like django.contrib.auth, include their own admin.py file. This is
why models like Users and Groups appear in the admin interface without additional
configuration. Other django.contrib apps and many third-party Django apps also include their
own admin.py files to register their models with the admin site automatically.
Making Fields Optional
To make fields optional in a Django model, you need to specify the blank=True parameter for
those fields. This allows the field to be left blank in forms, including those in the Django
admin interface. Here’s a detailed explanation and steps to make a field optional:
Making a Field Optional
1. Model Definition: Adjust the model definition to include blank=True for the fields
you want to be optional. For example, consider the Author model:
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True)
Here, email is now optional because blank=True allows it to be blank in forms.
Understanding blank vs. null
 blank=True: This allows the field to be blank in forms. It is related to form validation
and has no effect on the database schema directly.
 null=True: This allows the database column to accept NULL values. It is related to the
database schema.
For text-based fields (CharField, TextField, etc.), it’s common to use blank=True without
null=True because an empty string ("") is often more appropriate than NULL.
Example with null=True and blank=True
If you want the field to accept NULL in the database and be optional in forms:
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)

23
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

email = models.EmailField(null=True, blank=True)


Handling Optional Date and Numeric Fields
When dealing with date and numeric fields, handling optional values involves understanding
the differences between blank, null, and how SQL handles NULL values. Here's a detailed
explanation of how to make these fields optional in Django models.
1. Understanding null and blank
 null=True: This allows the database column to store NULL values, indicating the
absence of a value.
 blank=True: This allows the field to be left blank in forms, affecting form validation
but not directly the database schema.
2. Optional Date Fields
For date fields to be optional, you should set both null=True and blank=True. This
combination allows the field to be NULL in the database and empty in forms.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
published_date = models.DateField(null=True, blank=True) # Optional date field
3. Optional Numeric Fields
For numeric fields like IntegerField, FloatField, etc., you should also use both null=True and
blank=True:
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
Customizing Field Labels in Django Admin
When using the Django admin site, field labels are generated automatically based on the
model field names. However, you might want to customize these labels for better readability
or clarity. This can be done using the verbose_name parameter. Here’s how to achieve that,
along with some examples and additional customizations for the admin interface.
Changing Field Labels with verbose_name
1. Basic Usage: To customize the label of a model field, use the verbose_name
parameter within the model definition. For example, to change the label of the email
field to "e-mail":

24
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail')
You can also pass verbose_name as a positional argument:
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField('e-mail', blank=True)
Note: For fields like ManyToManyField or ForeignKey, you cannot use verbose_name as a
positional argument because the first argument must be a model class.
2. Reloading the Server: After making changes to the model, reload the server to see
the updated labels on the admin site.
Customizing Admin Interface with ModelAdmin
In addition to model-level changes, you can customize the Django admin interface using
ModelAdmin classes. This allows for more detailed configuration of how models are
presented and managed in the admin site.
1. Creating a ModelAdmin Class: Define a ModelAdmin class to customize how a
model is displayed in the admin interface. For instance, to customize the change list
for the Author model to display first name, last name, and email:
from django.contrib import admin
from mysite.books.models import Author, Publisher, Book
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')

admin.site.register(Author, AuthorAdmin)
admin.site.register(Publisher)
admin.site.register(Book)
2. list_display Attribute: The list_display attribute specifies which fields to display on
the change list page. This example shows the first_name, last_name, and email fields.
These fields are sortable by clicking on the column headers.

25
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

3. Registering the ModelAdmin: Use admin.site.register() to register the model along


with its ModelAdmin class. This ensures that Django uses the specified
customizations for the admin interface.
Users, Groups, and Permissions in Django Admin
In Django, the admin interface uses a permissions system to control what different users can
access and perform within the admin site. This system is flexible and allows for a range of
permissions, from superuser access to very specific model-level permissions. Here’s an
overview of how it works and how you can manage users and permissions in the Django
admin.
User Model Fields and Flags
Each user in Django has a set of standard fields and Boolean flags that control their access
and permissions:
1. Standard Fields:
o username: The unique identifier for the user.
o password: The user's password.
o email: The user's email address.
o first_name and last_name: The user's real name.
2. Boolean Flags:
o Active (is_active): Determines if the user account is active. Inactive users
cannot log in.
o Staff (is_staff): Determines if the user can log in to the admin interface. Users
without this flag are treated as regular site users.
o Superuser (is_superuser): Grants the user full access to the admin interface,
bypassing all other permissions.
Permissions
Django's permissions system operates on three basic levels for each model:
1. Create: Allows the user to add new objects.
2. Edit: Allows the user to change existing objects.
3. Delete: Allows the user to delete objects.
These permissions can be assigned to users individually or through groups.
Managing Users and Permissions in the Admin Interface
1. Creating a User:
o Navigate to the Users section in the admin interface.

26
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o Add a new user by providing the required fields (username, password).


o Save the user and then edit to configure permissions and other details.
2. Assigning Permissions:
o On the user's detail page, you will find sections to assign permissions.
o You can assign specific permissions directly to the user or add the user to a
group that has the desired permissions.
3. Using Groups:
o Groups allow you to manage permissions for multiple users collectively.
o Create a group and assign the necessary permissions to the group.
o Add users to the group to grant them the permissions.
When to Use the Django Admin Interface
1. Data Entry by Nontechnical Users:
o Scenario: Nontechnical team members, such as reporters or content creators,
need to input and manage data.
o Example: A reporter needs to enter information for a special report. The
developer sets up models, and the reporter uses the admin interface to input
data while the developer works on the front-end views and templates.
o Benefit: The admin interface allows nontechnical users to enter and manage
data without requiring developer intervention, facilitating parallel work
between content creators and developers.
2. Inspecting Data Models:
o Scenario: Developers need to verify that data models are correctly defined.
o Example: After defining models, developers can enter dummy data through
the admin interface to identify potential issues or necessary adjustments in the
data structure.
o Benefit: Quickly reveals any modeling mistakes and allows for iterative
improvements.
3. Managing Acquired Data:
o Scenario: Applications that collect data from external sources need an easy
way to manage and inspect this data.
o Example: An app gathers data from web crawlers, and the admin interface
provides a straightforward way to review and edit this data.

27
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o Benefit: Acts as a convenient tool for data management, offering more user-
friendly functionality than a database’s command-line utility.
4. Quick and Dirty Data-Management Apps:
o Scenario: Developers need to build simple data-management applications for
internal use.
o Example: A lightweight app to track expenses or manage small datasets.
o Benefit: The admin interface provides a rapid development environment for
internal tools, akin to using a relational version of a spreadsheet.
When Not to Use the Django Admin Interface
1. Complex User Interfaces:
o Scenario: Applications require highly customized user interfaces or complex
workflows.
o Example: A customer-facing website that needs a tailored user experience
beyond basic CRUD operations.
o Limitation: The admin interface is designed for simplicity and might not
support intricate UI requirements or advanced interactions effectively.
2. Public-Facing Applications:
o Scenario: The application is intended for public use and requires a specific
look and feel.
o Example: An e-commerce website with a unique design and user flow.
o Limitation: The admin interface is meant for back-end data management and
doesn’t provide the flexibility needed for front-end customization and user
experience design.
3. Advanced Permissions and Access Control:
o Scenario: Applications need fine-grained, per-object permissions and access
control.
o Example: A content management system where different users have varying
levels of access to specific content items.
o Limitation: Django’s admin interface supports model-level permissions but
lacks built-in support for more complex, per-object access control without
significant customization.
4. Scalability Concerns:
o Scenario: Applications require high scalability and performance optimization.
o Example: A high-traffic website with extensive data management needs.

28
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o Limitation: The admin interface is not optimized for high-concurrency


scenarios and might not scale well for very large datasets or user bases.
Form Processing in Django
Django provides a robust framework for form handling and validation, making it easy to
create forms, process submissions, and handle custom validation. Here's an in-depth guide
covering form processing, creating feedback forms, form submissions, custom validation,
creating Model Forms, and managing URLConf.
1. Form Processing in Django
Form processing in Django involves three main steps:
 Displaying the form to the user.
 Validating user input.
 Processing valid data.
Django’s forms module simplifies these steps significantly.

2. Creating Feedback Forms


Step 1: Define a Form Class
First, you need to define a form class in forms.py:
from django import forms
class FeedbackForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
Step 2: Display the Form in a View
Next, create a view in views.py to display and process the form:
from django.shortcuts import render, redirect
from .forms import FeedbackForm

def feedback_view(request):
if request.method == 'POST':
form = FeedbackForm(request.POST)
if form.is_valid():

29
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

# Process the data in form.cleaned_data


name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# Redirect to a new URL or display a success message
return redirect('thank_you')
else:
form = FeedbackForm()
return render(request, 'feedback.html', {'form': form})
Step 3: Create a Template
Create a template feedback.html to render the form:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
3. Form Submissions
When the form is submitted, Django processes the form data. The POST request data is
available in request.POST. If the form is valid, you can access the cleaned data through
form.cleaned_data.

4. Custom Validation
You can add custom validation to your forms by defining clean_<fieldname> methods in your
form class:
class FeedbackForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)

def clean_email(self):

30
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

email = self.cleaned_data.get('email')
if "spam" in email:
raise forms.ValidationError("Invalid email address")
return email
For more complex validation, you can override the clean method to perform form-wide
validation:
class FeedbackForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)

def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get("name")
email = cleaned_data.get("email")
# Add custom validation logic here
if name and "spam" in email:
raise forms.ValidationError("Invalid combination of name and email")
5. Creating Model Forms
Model Forms provide a way to create forms that are directly tied to Django models,
simplifying the process of creating forms for model instances.

Step 1: Define a Model


In models.py:

from django.db import models


class Feedback(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
message = models.TextField()

31
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Step 2: Create a ModelForm


In forms.py:
from django import forms
from .models import Feedback

class FeedbackModelForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ['name', 'email', 'message']
Step 3: Use the ModelForm in a View
In views.py:
from django.shortcuts import render, redirect
from .forms import FeedbackModelForm

def feedback_view(request):
if request.method == 'POST':
form = FeedbackModelForm(request.POST)
if form.is_valid():
form.save()
return redirect('thank_you')
else:
form = FeedbackModelForm()
return render(request, 'feedback.html', {'form': form})
6. URLConf Tips and Tricks
Django’s URL configuration (URLconf) system is very flexible and allows you to organize
your URL patterns effectively.

Including Other URLConfs


For large projects, you might want to split your URL configurations into multiple files. You
can include URLConfs from other apps or modules using the include function.

32
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Example
Here’s how you can include URLs from an app called myapp in your project’s main urls.py:
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include('myapp.urls')),
]
In myapp/urls.py:
from django.urls import path
from . import views

urlpatterns = [
path('feedback/', views.feedback_view, name='feedback'),
path('thank-you/', views.thank_you_view, name='thank_you'),
]
Named URL Patterns
Using named URL patterns allows you to reference URLs by name instead of hardcoding
them, making your code more maintainable.

Example
In urls.py:
from django.urls import path
from . import views

urlpatterns = [
path('feedback/', views.feedback_view, name='feedback'),
path('thank-you/', views.thank_you_view, name='thank_you'),

33
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

]
In your views or templates, you can use the name to refer to the URL:
from django.urls import reverse
from django.shortcuts import redirect

def feedback_view(request):
# After form processing
return redirect('thank_you')
# In templates
<a href="{% url 'feedback' %}">Leave Feedback</a>
Drive Link for the verified Ppt’s:-
https://drive.google.com/drive/folders/1ydqApp8wRTH8HIxb45IuGnS-
pVufJpqE?usp=drive_link
---------------------------------END OF 2ND IA TEST PORTIONS!!!!!!-------------------------------

34
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Form Processing in Django


The Django framework provides powerful tools for handling HTTP requests and responses.
Here's an explanation of the provided Django tutorial excerpt and how it relates to building
interactive web applications:
HttpRequest Object in Django
1. Overview of HttpRequest: In Django, when a view function is called, it receives an
HttpRequest object as its first parameter. This object encapsulates all the details of the
current HTTP request made by the client (browser or other user agent).
2. Accessing Request Attributes:
o request.path: Represents the path part of the URL requested, without the
domain name but including the leading slash (/hello/ in the example).
o request.get_host(): Returns the host (or domain) of the request, such as
127.0.0.1:8000 or www.example.com.
o request.get_full_path(): Provides the complete path of the request including
any query parameters, like /hello/?print=true.
o request.is_secure(): Indicates whether the request was made via HTTPS
(True) or HTTP (False).
3. Using HttpRequest Attributes:
o It's recommended to use these attributes dynamically in views rather than
hard-coding URLs. This approach enhances code flexibility and reusability
across different parts of your application.
4. Handling request.META:
o request.META is a Python dictionary containing metadata about the request,
including HTTP headers like HTTP_REFERER, HTTP_USER_AGENT, and
REMOTE_ADDR (the client's IP address).
o These headers can vary depending on the client's browser and the
configuration of your web server.
5. Best Practices for Handling request.META:
o To safely access headers from request.META, it's recommended to use
techniques that handle potential KeyError exceptions gracefully.
o Examples include using a try/except block or the get() method on dictionaries
to provide default values when a key doesn't exist.
6. Example Views: Displaying request.META: The tutorial suggests creating a view
(display_meta) that lists all available headers in a table format using HTML. This
helps developers understand what metadata is available for each request.

29
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

7. Using Django Templates:


o To enhance maintainability, the tutorial encourages converting the
display_meta view to use Django's template system instead of manually
constructing HTML. This involves creating a template file
(meta_display.html) that renders the metadata dynamically.
Create a Template for Displaying Meta Data:
First, create a template file (e.g., meta_display.html) in your templates directory (templates/)
that will render the meta data in a table format.
<!-- meta_display.html -->
<table>
{% for k, v in meta_data %}
<tr>
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
{% endfor %}
</table>
Update Your View to Use the Template:
Modify your Django view to render this template instead of constructing HTML manually.
from django.shortcuts import render
def display_meta(request):
meta_data = request.META.items()
sorted_meta_data = sorted(meta_data)
return render(request, 'meta_display.html', {'meta_data': sorted_meta_data, 'path':
request.path})
In this updated view:
 request.META.items() retrieves all HTTP headers.
 sorted(meta_data) sorts the headers alphabetically.
 render() is used to render the meta_display.html template, passing meta_data and path
to it.
 Include Other HttpRequest Methods:
 If you want to include request.path and other HttpRequest methods as mentioned, you
can pass them similarly to the template:

30
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

def display_meta(request):
meta_data = request.META.items()
sorted_meta_data = sorted(meta_data)
path = request.path
host = request.get_host()
full_path = request.get_full_path()
is_secure = request.is_secure()

return render(request, 'meta_display.html', {


'meta_data': sorted_meta_data,
'path': path,
'host': host,
'full_path': full_path,
'is_secure': is_secure
})
Information About Submitted Data
request.GET and request.POST are attributes of the HttpRequest object that provide access to
user-submitted data:
1. request.GET
 Usage: Contains data submitted to the server as part of the URL query parameters
(?key1=value1&key2=value2).
 Source: Typically comes from <form> submissions where the form uses the GET
method.
 Behavior: Similar to a standard Python dictionary with methods like get(), keys(), and
values().
 Iteration: You can iterate over its keys using a loop like for key in request.GET.
2. request.POST
 Usage: Contains data submitted to the server via HTTP POST requests, typically from
HTML <form> submissions.
 Source: Form data is sent in the body of the HTTP request.

31
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Behavior: Also behaves like a dictionary with methods such as get(), keys(), and
values().
 Iteration: Keys can be iterated over similarly with for key in request.POST.
Dictionary-Like Objects
 Definition: While request.GET and request.POST behave like dictionaries, they aren't
standard Python dictionaries under the hood.
 Additional Methods: These objects offer additional methods beyond standard
dictionaries, specific to handling HTTP request data in Django.
Example Usage
Here's a simplified example of how you might handle data submitted via GET and POST
requests in a Django view:
from django.shortcuts import render
from django.http import HttpResponse
def process_form(request):
if request.method == 'POST':
# Handle POST data
username = request.POST.get('username', '')
password = request.POST.get('password', '')
# Process the data or perform validation
return HttpResponse(f'POST request received. Username: {username}, Password:
{password}')
elif request.method == 'GET':
# Handle GET data
search_query = request.GET.get('q', '')
# Perform search or display results based on the query
return HttpResponse(f'GET request received. Search query: {search_query}')
return HttpResponse('No data received.')
A Simple Form-Handling Example
1. Setting Up the Search Form
View Function (search_form in views.py):
from django.shortcuts import render_to_response

32
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

def search_form(request):
return render_to_response('search_form.html')
 This function renders the search_form.html template when accessed. It's responsible
for displaying the HTML form where users can input their search query.
Template (search_form.html):
<html>
<head>
<title>Search</title>
</head>
<body>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
 This HTML template defines a simple form with a text input field (<input type="text"
name="q">) and a submit button (<input type="submit" value="Search">). The form's
action attribute points to /search/, indicating where the form data will be sent upon
submission using the GET method.
2. Handling Form Submission
URL Configuration (urls.py):
from mysite.books import views

urlpatterns = [
# other patterns
path('search-form/', views.search_form),
path('search/', views.search),
# other patterns
]

33
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Here, URLs are mapped to corresponding view functions (search_form and search).
When a user submits the form (/search-form/), it renders the search_form view to
display the form. Upon submission, the form data is sent to /search/, which is handled
by the search view.
View Function (search in views.py):
from django.http import HttpResponse
def search(request):
if 'q' in request.GET:
message = 'You searched for: %r' % request.GET['q']
else:
message = 'You submitted an empty form.'
return HttpResponse(message)
 The search view function processes the search query submitted via the form. It checks
if the key 'q' exists in request.GET. If it does, it retrieves the value (the search query).
If not, it indicates that an empty form was submitted.
 Form Submission: The form uses the GET method (method="get"), which appends
form data to the URL as query parameters (?q=search_query).
 Handling GET Data: In Django, GET data is accessed via request.GET, which
behaves like a dictionary. It allows you to access form input values (request.GET['q'])
or check for their existence ('q' in request.GET).
 Security Considerations: Always validate and sanitize user input to prevent security
vulnerabilities like SQL injection or cross-site scripting (XSS). Django's built-in
protections and best practices help in handling user input securely.
GET Method
 Purpose: Used for requests where the primary purpose is to retrieve data from the
server.
 Usage: Data is appended to the URL as query parameters
(?key1=value1&key2=value2).
 Safe: Generally considered safe and idempotent (repeated requests do not have
additional side effects).
 Example Use Cases:
o Search queries (e.g., search engines).
o Navigating through pages with parameters (e.g., pagination).

34
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

POST Method
 Purpose: Used for requests that may have side effects on the server, such as
modifying data or initiating actions.
 Usage: Data is sent in the body of the HTTP request.
 Security: More secure than GET because data is not visible in the URL and can
handle larger amounts of data.
 Example Use Cases:
o Submitting a form that updates a database (e.g., user registration, submitting
comments).
o Uploading files.
Differences in Practice
 Data Visibility: GET parameters are visible in the URL, which can be bookmarked,
shared, or cached. POST data is not visible in the URL.
 Caching: GET requests can be cached by browsers and intermediaries, while POST
requests are not cached by default.
 Security Considerations: POST is preferred for sensitive data as it provides a layer
of security by keeping data hidden from the URL and potentially from being stored in
browser history.
Example Implementation: Book Search Using GET
In the provided Django example, the GET method is used for searching books based on a
user's query:
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book

def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html', {'books': books, 'query': q})
else:
return HttpResponse('Please submit a search term.')

35
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Explanation:
o The view checks if the query parameter 'q' exists in request.GET and if it is
not empty (request.GET['q']).
o It performs a case-insensitive search (icontains) in the Book model's title field
for books matching the query.
o Results are passed to the search_results.html template along with the search
query (query).
Template Example: search_results.html
The template renders the search results based on the books found:
<p>You searched for: <strong>{{ query }}</strong></p>
{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
<ul>
{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>No books matched your search criteria.</p>
{% endif %}
 Explanation:
o Displays the search query ({{ query }}) to inform the user what they searched
for.
o Conditionally displays the number of books found and lists them using a loop
({% for book in books %}).
o Uses the pluralize filter to handle singular and plural forms of the word "book"
based on the number of results.
Improving Our Simple Form-Handling Example
1. Initial Problem and Simple Solution
Originally, the search() view simply displayed a message when the search query was empty,
prompting users to use the browser's back button. This approach is not user-friendly. The

36
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

initial improvement was to render the search form again with an error message directly above
it.
Revised search() and search_form() Views
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book

def search_form(request):
return render_to_response('search_form.html')

def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html', {'books': books, 'query': q})
else:
return render_to_response('search_form.html', {'error': True})
2. Improvements Made
 Rendering Error Messages: Instead of displaying a generic message, the search()
view now renders search_form.html again if the search query is empty ('q' is not
provided or empty). It passes an error variable to indicate the presence of an error,
allowing the template to display an appropriate message.
 Template Update (search_form.html):
<html>
<head>
<title>Search</title>
</head>
<body>
{% if error %}
<p style="color: red;">Please submit a search term.</p>
{% endif %}

37
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

<form action="/search/" method="get">


<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
 Explanation:
o The template checks for the presence of the error variable. If true, it displays a
red error message asking the user to submit a search term.
o The form's action attribute remains action="/search/", ensuring the form
submits to the same URL handled by the search() view.
3. Final Refinements
 Removing Redundancy: Since the search_form() view only renders
search_form.html and the search() view handles both form display and result
rendering, the search_form() view and its associated URL pattern can be removed.
Updated search() View
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html', {'books': books, 'query': q})
return render_to_response('search_form.html', {'error': error})
 Explanation:
o The view checks for the presence and validity of the search query ('q').
o If 'q' is empty, it sets error to True and renders search_form.html with the error
message.

38
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o If 'q' has a value, it performs the search and renders search_results.html with
the results.
Updated search_form.html
<form action="" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
 Explanation:
o The action="" attribute in the form tag directs the form to submit to the current
URL (/search/), which is handled by the search() view.
o This eliminates the need to hard-code the URL and ensures flexibility if the
URL handling changes in the future.
Simple Validation
Initial Validation Approach
Initially, the search() view checked if the search query ('q') existed and wasn't empty. If it was
empty or exceeded 20 characters, it set an error flag to True and rendered the form again with
a generic error message.
Original search() View
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
elif len(q) > 20:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html', {'books': books, 'query': q})

return render_to_response('search_form.html', {'error': error})

39
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Original search_form.html
<html>
<head>
<title>Search</title>
</head>
<body>
{% if error %}
<p style="color: red;">
Please submit a search term 20 characters or shorter.
</p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
2. Improving Validation and Error Handling
To provide more specific error messages and handle multiple validation criteria, we should
refactor the search() view to use a list of error messages (errors) instead of a single Boolean
(error). This allows us to collect and display multiple error messages if necessary.
Updated search() View
def search(request):
errors = []
if 'q' in request.GET:
q = request.GET['q']
if not q:
errors.append('Enter a search term.')
elif len(q) > 20:
errors.append('Please enter at most 20 characters.')

40
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html', {'books': books, 'query': q})

return render_to_response('search_form.html', {'errors': errors})


Updated search_form.html
<html>
<head>
<title>Search</title>
</head>
<body>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
3. Explanation of Changes
 View Changes:
o Instead of a single error flag, the search() view now uses an errors list to
collect error messages.
o If the search query is empty or exceeds 20 characters, the respective error
message is appended to errors.

41
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o If valid, the view proceeds to query the database and render the results.
 Template Changes:
o search_form.html now checks for the presence of errors.
o If errors exist, it iterates over them and displays each in a list format
(<ul><li>).
By using a list of error messages (errors) instead of a Boolean (error), the application can
handle and display multiple validation issues simultaneously. This approach makes error
messages more specific and user-friendly, enhancing the overall user experience when
interacting with the form.
Making a Contact Form
Template: contact_form.html
First, let's define the template contact_form.html that includes fields for subject, email
(optional), and message. It also includes error handling to display validation errors.
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/contact/" method="post">
{% csrf_token %}
<p>Subject: <input type="text" name="subject" value="{{ subject }}"></p>
<p>Your e-mail (optional): <input type="text" name="email" value="{{ email }}"></p>

42
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

<p>Message: <textarea name="message" rows="10" cols="50">{{ message


}}</textarea></p>
<input type="submit" value="Submit">
</form>
</body>
</html>
2. View: contact()
Now, let's define the contact() view in views.py. This view will handle form submission,
validate the data, and process the email sending upon successful validation.
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render

def contact(request):
if request.method == 'POST':
subject = request.POST.get('subject', '')
email = request.POST.get('email', '')
message = request.POST.get('message', '')

errors = []

if not subject:
errors.append('Enter a subject.')
if not message:
errors.append('Enter a message.')
if email and '@' not in email:
errors.append('Enter a valid email address.')

if not errors:
# Send email

43
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

send_mail(
subject,
message,
email or '[email protected]',
['[email protected]'],
)
return HttpResponseRedirect('/contact/thanks/') # Redirect to a thank you page

# If there are errors, render the form with error messages


return render(request, 'contact_form.html', {'errors': errors, 'subject': subject, 'email':
email, 'message': message})

# If it's a GET request, just render the empty form


return render(request, 'contact_form.html')
3. Explanation
 Template (contact_form.html):
o Displays a form with fields for subject, email, and message.
o Checks for errors in the context and displays them in a list if they exist.
o Uses method="post" because the form submission has side effects (sends an
email).
o Uses {% csrf_token %} to prevent CSRF attacks.
 View (contact()):
o Handles both GET and POST requests.
o GET request: Renders the form initially.
o POST request: Processes form submission.
 Validates form data (subject and message are required, email must be
valid if provided).
 If valid, sends an email using send_mail() and redirects to a thank you
page.
 If invalid, re-renders the form with error messages and pre-filled data
(subject, email, message).

44
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

4. Improvements
 Error Handling: Provides specific error messages for each validation condition.
 Email Handling: Sends an email to the site owner upon successful form submission.
 CSRF Protection: Ensures security with {% csrf_token %} in the form.
Using Django Forms for Form Handling and Validation
Django provides a powerful form-handling mechanism that abstracts away much of the
complexity you've outlined. Here’s how you can refactor your contact() view to use Django
forms:
1. Define a Django Form
Create a form class in forms.py that represents your contact form fields and their validation
rules.
# forms.py
from django import forms

class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)

def clean_email(self):
email = self.cleaned_data.get('email')
if email and '@' not in email:
raise forms.ValidationError('Enter a valid email address.')
return email

2. Update the View to Use the Form


Modify your contact() view in views.py to use the new form class for processing and
validation.
# views.py
from django.shortcuts import render
from django.core.mail import send_mail

45
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

from django.http import HttpResponseRedirect


from .forms import ContactForm

def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
email = form.cleaned_data.get('email', '[email protected]')

send_mail(
subject,
message,
email,
['[email protected]'],
)
return HttpResponseRedirect('/contact/thanks/')
else:
form = ContactForm()

return render(request, 'contact_form.html', {'form': form})


3. Update the Template
Modify contact_form.html to use Django’s form rendering and error display capabilities.
<!-- contact_form.html -->
<html>
<head>
<title>Contact us</title>
</head>

46
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

<body>
<h1>Contact us</h1>
<form action="/contact/" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<p>{{ form.subject.label_tag }} {{ form.subject }}</p>
<p>{{ form.email.label_tag }} {{ form.email }}</p>
<p>{{ form.message.label_tag }} {{ form.message }}</p>
<input type="submit" value="Submit">
</form>
</body>
</html>
Explanation and Benefits
 Form Class (ContactForm):
o Defines fields (subject, email, message) with built-in validation.
o clean_email() method provides custom validation for the email field.
 View (contact()):
o Initializes ContactForm with request.POST data for POST requests.
o Validates form data using form.is_valid() which triggers all field and form-
level validation.
o If valid, extracts cleaned data and sends an email.
o If invalid, re-renders the form with error messages automatically displayed.
 Template (contact_form.html):
o Renders form fields using {{ form.field_name }} syntax, which automatically
handles HTML input generation and error display.
o Uses {{ form.non_field_errors }} to display form-level errors (if any).
Benefits of Using Django Forms
 Validation: Django forms provide built-in validation for common data types
(CharField, EmailField, etc.) and allow custom validation logic.
 Error Handling: Automatically displays errors next to form fields based on
validation rules defined in the form class.

47
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

 Security: Automatically handles CSRF protection with {% csrf_token %}.


 Simplicity: Reduces boilerplate code and minimizes chances of human error in form
handling logic.
Your First Form Class
Creating and Using Your First Form Class
In Django, forms are defined using the django.forms.Form class. Each form field is
represented by a field class (like CharField, EmailField, etc.) within the form class. Here's
how you can define and use a contact form using Django's forms framework:
1. Define the Form Class (forms.py)
# forms.py
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
In this example:
o CharField is used for the subject and message fields.
o EmailField is used for the email field, with required=False to make it optional.
o Textarea widget is used for the message field to render it as a multiline
textarea in HTML.
2. Displaying the Form in a Template (contact_form.html)
<!-- contact_form.html -->
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
<form action="/contact/" method="post">
{% csrf_token %}
{{ form.as_p }}

48
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

<input type="submit" value="Submit">


</form>
</body>
</html>
o {{ form.as_p }} renders each form field wrapped in <p> tags.
o csrf_token ensures CSRF protection, which is necessary for POST forms in
Django.
3. Handling Form Submission (views.py)
# views.py
from django.shortcuts import render, HttpResponseRedirect
from .forms import ContactForm
from django.core.mail import send_mail

def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
email = form.cleaned_data.get('email', '[email protected]')
send_mail(subject, message, email, ['[email protected]'])
return HttpResponseRedirect('/contact/thanks/')
else:
form = ContactForm()

return render(request, 'contact_form.html', {'form': form})


o POST Handling: If the form is submitted (request.method == 'POST'), create
a form instance with the submitted data (ContactForm(request.POST)).
o Validation: form.is_valid() checks if all fields pass validation rules defined in
the form class.

49
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o Sending Email: Uses send_mail to send an email based on the form data.
o GET Handling: If it's a GET request, create an empty form (ContactForm()).
Why Use Django Forms?
Django forms provide several benefits:
 Built-in Validation: Forms handle validation automatically based on field types and
rules.
 HTML Generation: Simplifies HTML form generation with automatic field
rendering and error handling.
 Security: Includes CSRF protection and safeguards against malicious input.
 Data Handling: Automatically cleans and prepares data for processing, improving
reliability and security.
Validation Process
Creating a Bound Form:

>>> f = ContactForm({'subject': 'Hello', 'e-mail': '[email protected]', 'message': 'Nice


site!'})
>>> f.is_bound
True
is_bound: Indicates whether the form has been bound with data (True in this case).
Validation Using is_valid():
>>> f.is_valid()
True
is_valid(): Checks if all fields in the form pass validation based on their specified rules
(True because all fields are valid).
Handling Optional Fields:
>>> f = ContactForm({'subject': 'Hello', 'message': 'Nice site!'})
>>> f.is_valid()
True
If a field marked as required=False is omitted, the form remains valid.
Invalid Data Handling:

50
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

>>> f = ContactForm({'subject': 'Hello'})


>>> f.is_valid()
False
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.is_valid()
False
Errors: When fields fail validation, errors are recorded in the errors attribute of the form
and each individual field.
>>> f['message'].errors
[u'This field is required.']
>>> f.errors
{'message': [u'This field is required.']}
Changing How Fields Are Rendered
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
e_mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)

1. Importing Django Forms:


o from django import forms: This imports Django's forms module, which
provides classes and utilities for creating and handling forms in Django
applications.
2. Defining the ContactForm class:
o class ContactForm(forms.Form):: This declares a new form class named
ContactForm, which inherits from Django's forms.Form class. This means
ContactForm will have all the functionalities provided by forms.Form.
3. Form Fields:
o subject = forms.CharField(): This defines a field named subject using
forms.CharField(). By default, this creates a text input field (<input
type="text">) in the HTML form.

51
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o e_mail = forms.EmailField(required=False): This defines a field named e_mail


using forms.EmailField(). This field expects a valid email address input. The
required=False parameter means that the field is not mandatory.
o message = forms.CharField(widget=forms.Textarea): This defines a field
named message using forms.CharField(), but it customizes its widget to
forms.Textarea. This changes the default presentation of the message field
from a simple text input to a larger textarea input (<textarea></textarea>),
suitable for multi-line text input like messages or comments.
4. Explanation:
o Django forms separate the definition of fields (subject, e_mail, message) from
how these fields are rendered in HTML (widget=forms.Textarea for message).
o The widget=forms.Textarea part specifically instructs Django to render the
message field as a <textarea> element in the HTML form, rather than the
default <input type="text">.
o This customization improves user experience by matching the input field type
to the expected content (multi-line text for message).
Setting Maximum Length
from django import forms

class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
e_mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
 subject = forms.CharField(max_length=100):
o This modification sets a maximum length (max_length=100) for the subject
field using forms.CharField(). Now, any input for the subject field in the form
will be limited to 100 characters.
 e_mail = forms.EmailField(required=False):
o This field remains an optional email field (forms.EmailField()) as before, with
required=False, meaning it's not mandatory.
 message = forms.CharField(widget=forms.Textarea):
o The message field continues to use a Textarea widget (forms.Textarea),
allowing for multi-line input.
o

52
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Setting Initial Values


To set an initial value for the subject field when rendering the form, you can use the initial
argument when initializing the form instance in your view:
from django.shortcuts import render
from django.core.mail import send_mail
from .forms import ContactForm # Assuming your form is in a forms.py file

def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('e_mail', '[email protected]'),
['[email protected]'],
)
return HttpResponseRedirect('/contact/thanks/')
else:
form = ContactForm(initial={'subject': 'I love your site!'})

return render(request, 'contact_form.html', {'form': form})


 form = ContactForm(initial={'subject': 'I love your site!'}):
o Here, when rendering the form initially (GET request), the subject field will be
prepopulated with the value "I love your site!". This provides a default value
to encourage users to start with a positive statement.
Note:
 Difference between initial data and bound data:
o Initial data (initial argument) is used for prepopulating form fields when
rendering the form initially. It does not bind the form to incoming POST data.

53
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

Binding happens when request.method == 'POST' and you pass request.POST


to ContactForm() to validate and process user-submitted data.
These enhancements improve both usability and validation in your Django form, ensuring
that the subject field has a reasonable character limit and providing a friendly initial value to
guide user input.
Adding Custom Validation Rules
Adding custom validation to Django forms allows you to enforce specific rules on user input
beyond standard field requirements. By leveraging clean_<field_name>() methods, you can
validate data according to application-specific criteria and provide meaningful feedback to
users when validation fails. This approach enhances the usability and reliability of your
Django forms in handling user-submitted data.
How Django Handles Custom Validation:
 Django's form system automatically looks for methods named clean_<field_name>()
for custom validation.
 These methods are called after the default validation logic for the field has run,
ensuring that the field data is already cleaned and validated to some extent
(self.cleaned_data).
 If you forget to return the cleaned value (message in this case) at the end of the
method, the original value won't be retained, potentially causing unexpected behavior.
Step-by-Step Explanation:
1. Form Definition
First, let's review your ContactForm class:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
e_mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
 forms.CharField(max_length=100): Defines a subject field with a maximum length
of 100 characters.
 forms.EmailField(required=False): Defines an optional e_mail field for email input.
 forms.CharField(widget=forms.Textarea): Defines a message field using a Textarea
widget, suitable for multi-line input.
2. Adding Custom Validation Method
Now, let's add the clean_message() method for custom validation:

54
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

def clean_message(self):
message = self.cleaned_data['message'] # Retrieve cleaned data for 'message'
num_words = len(message.split()) # Count words in the message

# Custom validation rule: Require at least 4 words


if num_words < 4:
raise forms.ValidationError("Not enough words! Please enter at least four words.")

return message # Return the cleaned 'message' data


 def clean_message(self): This method is a custom validation hook provided by
Django forms. It automatically runs after standard field validation.
 self.cleaned_data['message']: Retrieves the cleaned (validated) data for the message
field.
 num_words = len(message.split()): Splits the message into words and counts them
using split(). This gives us the number of words in the message.
 Validation Logic:
o if num_words < 4: Checks if the number of words in the message is less than
4.
o raise forms.ValidationError(...): Raises a ValidationError if the condition
(num_words < 4) is true. The message attached to the ValidationError will be
displayed to the user.
 Returning Cleaned Data:
o return message: If the validation passes (i.e., the message has at least four
words), the method returns the cleaned message data.

Usage in Views
In your views, when rendering the form, ensure that you handle both GET and POST requests
properly:
from django.shortcuts import render, HttpResponseRedirect
from .forms import ContactForm
from django.core.mail import send_mail

55
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Process valid form data
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('e_mail', '[email protected]'),
['[email protected]'],
)
return HttpResponseRedirect('/contact/thanks/')
else:
# Initialize form with initial data or empty form
form = ContactForm()

return render(request, 'contact_form.html', {'form': form})


 form = ContactForm(request.POST): Creates an instance of ContactForm with data
from a POST request.
 form.is_valid(): Checks if the form data passes all validation, including custom
validation (clean_message()).
 Handling Valid Form Submission: If the form is valid (form.is_valid() returns True),
you can process the cleaned data (cd) as needed (e.g., sending an email).
 Rendering the Form: Passes the form instance (form) to the template
(contact_form.html) for rendering. Any validation errors, including those from
clean_message(), will be displayed automatically by Django.
Specifying Labels
Customizing Labels in Django Forms
from django import forms
class ContactForm(forms.Form):

56
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

subject = forms.CharField(max_length=100)
e_mail = forms.EmailField(required=False, label='Your e-mail address')
message = forms.CharField(widget=forms.Textarea)
Explanation:
1. Field Definitions:
o subject = forms.CharField(max_length=100): Defines a subject field with a
maximum length of 100 characters.
o e_mail = forms.EmailField(required=False, label='Your e-mail address'):
Defines an e_mail field for email input. The label parameter customizes the
label displayed in the HTML form to "Your e-mail address".
o message = forms.CharField(widget=forms.Textarea): Defines a message
field using a Textarea widget, suitable for multi-line input.
2. Customizing Labels:
o By default, Django forms generate labels by replacing underscores with spaces
and capitalizing the first letter (e_mail becomes "E-mail").
o You can override this default behavior by explicitly setting the label attribute
when defining a form field, as shown with label='Your e-mail address' for the
e_mail field.
3. Effect:
o When you render the form in a template, the label for the e_mail field will be
displayed as "Your e-mail address" instead of the default "E-mail".

Customizing Form Presentation in Django Templates


Customizing Django form presentation in templates allows you to tailor the user interface to
match your application's design and usability needs. By using CSS for styling and Django
template language ({% ... %}) for dynamic content and error handling, you can create
visually appealing and user-friendly forms that enhance the user experience
Example Template (contact_form.html)
<html>
<head>
<title>Contact us</title>
<style type="text/css">
ul.errorlist {

57
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

margin: 0;
padding: 0;
}
.errorlist li {
background-color: red;
color: white;
display: block;
font-size: 10px;
margin: 0 0 3px;
padding: 4px 5px;
}
.field {
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>Contact us</h1>

{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}

<form action="" method="post">


<div class="field">
{{ form.subject.errors }}
<label for="id_subject">Subject:</label>

58
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

{{ form.subject }}
</div>

<div class="field">
{{ form.e_mail.errors }}
<label for="id_e-mail">Your e-mail address:</label>
{{ form.e_mail }}
</div>

<div class="field">
{{ form.message.errors }}
<label for="id_message">Message:</label>
{{ form.message }}
</div>

<input type="submit" value="Submit">


</form>
</body>
</html>
Explanation:
1. CSS Styling:
o ul.errorlist: Targets the unordered list (<ul>) used for displaying form errors.
Styles it to remove margins and paddings, and gives it a red background with
white text.
o .errorlist li: Styles each error item (<li>) within the error list. Sets a red
background, white text, and adds padding for better readability.
o .field: Adds margin below each form field (<div class="field">) to separate
them visually.
2. Error Handling:
o {% if form.errors %}: Checks if there are any form-level errors. If present,
displays a message to the user to correct errors.

59
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62

o {{ form.fieldname.errors }}: Displays the errors associated with each field


(subject, e_mail, message) if there are any.
3. Form Fields:
o Each form field is enclosed within a <div class="field"> for styling and
structure.
o <label for="id_fieldname">Label Text:</label>: Provides a label for each
form field. The for attribute links the label to the corresponding input field
using its id.
o {{ form.fieldname }}: Renders the input widget for each form field (subject,
e_mail, message).
4. Submitting the Form:
o <input type="submit" value="Submit">: Displays a submit button for
users to submit the form.
Handling Validation Errors Specifically for message Field
To handle validation errors specifically for the message field, you can further customize its
display:
<div class="field{% if form.message.errors %} errors{% endif %}">
{% if form.message.errors %}
<ul>
{% for error in form.message.errors %}
<li><strong>{{ error }}</strong></li>
{% endfor %}
</ul>
{% endif %}
<label for="id_message">Message:</label>
{{ form.message }}
</div>
 .field.errors Class: Adds the errors class to the div container if there are errors
associated with the message field.
 Displaying Errors: Iterates over each error in form.message.errors and displays them
within an unordered list (<ul>).
.

60
Koustav Biswas, Dept. Of CSE, DSATM
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. Of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE
Dept. of ISE

You might also like