FSD Compiled Notes
FSD Compiled Notes
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():
2
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62
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>
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.
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.
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:
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.
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.
8
Documented By:
Koustav Biswas, Dept. Of CSE, DSATM.
Full Stack Development 21CS62
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
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
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.
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
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:
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
{% include template_name %}
Template Inheritance
extends tag in Templates
{% extends 'template_name.html' %}
Full Stack Development 21CS62
MODULE 2
MODELS
The notes are prepared from the prescribed textbook
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.
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 layer addresses these issues, making database interactions simpler and
more abstract.
def book_list(request):
books = Book.objects.order_by('name')
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).
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
4
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
5
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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
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
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
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
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:
17
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
18
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
19
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
20
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
21
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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
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
26
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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
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
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.
31
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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.
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
29
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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()
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
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})
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})
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
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
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
45
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():
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()
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
48
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():
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()
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:
50
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
51
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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
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!'})
53
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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
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()
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".
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 %}
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>
59
Koustav Biswas, Dept. Of CSE, DSATM
Full Stack Development 21CS62
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