Knowledge / Computer Applications / Python web frameworks: Django and Flask

Python web frameworks: Django and Flask

Computer Applications (MCA) · Enterprise and web development · free study notes for exams, placements and interviews

In short

Django and Flask are the two dominant Python web frameworks: Django is a "batteries-included" full-stack framework with its own ORM, admin, forms, auth and template engine, while Flask is a microframework that supplies only routing, request handling and Jinja2 templates and leaves the rest to extensions. Both sit behind the WSGI (synchronous) or ASGI (asynchronous) interface, which is how a Python application talks to a web server such as gunicorn or uvicorn behind Nginx. The topic is examined in MCA/BCA "Web Technologies" and "Enterprise Application Development" papers through MVT architecture, request-response flow, ORM and template questions, and is tested in almost every Python backend interview in India. Employers hiring on this stack include Zoho, Freshworks, Zomato, Swiggy, Dream11, Razorpay, Instamojo, government e-governance vendors (NIC, C-DAC projects) and most Bengaluru/Pune/Hyderabad product startups. Django 5.2 is the current long-term-support release (April 2025) and Flask 3.x the current Flask series; releases advance every few months, so always check the installed version with pip show django.

Explained

Where a Python framework sits: WSGI and ASGI

Django architecture: MVT

Django ORM, migrations and admin

Templates: Django Template Language and Jinja2

Flask: microframework design

Security, sessions and Indian compliance

Testing, background work and deployment

Key points

Formulas and examples

Sync worker count (gunicorn rule of thumb): W = (2 × CPU cores) + 1 — W workers, each handling one request at a time
Throughput capacity: C = W / T_avg (C in requests/second, W workers, T_avg mean response time in seconds)
Little's law: L = λ × W_t (L = mean number of requests in the system, λ = arrival rate in req/s, W_t = mean time in system in s)
Queries issued by an unoptimised loop: Q = 1 + N (the N+1 problem; N = rows in the outer QuerySet)
Effective response time with a cache: T_eff = h × T_cache + (1 − h) × T_db (h = cache hit ratio, 0 to 1)
Pagination: OFFSET = (page − 1) × page_size; total pages = ceil(total_rows / page_size)
Cost per million requests = monthly server cost (₹) / monthly requests in millions

Example 1 — sizing gunicorn. A Django app runs on a 4-vCPU cloud VM; mean response time is 120 ms; peak traffic is 50 req/s.

Example 2 — Little's law and blocking I/O. λ = 40 req/s and each request spends 250 ms in the system (180 ms of it waiting on a slow external API).

Example 3 — curing N+1. A job list page shows 50 listings, each printing job.employer.name; each query takes 3 ms.

Example 4 — cache hit ratio. A dashboard view costs 150 ms from PostgreSQL and 2 ms from Redis; @cache_page achieves a 90% hit ratio.

Example 5 — pagination and hosting cost. A Flask job board shows 20 results per page over 4,350 rows, and runs on a ₹1,800/month 2-vCPU VM at an average of 10 req/s.

AspectDjangoFlask
TypeFull-stack, batteries includedMicroframework
ORMBuilt-in Django ORM + migrationsNone; usually SQLAlchemy + Alembic
Admin interfaceAutomatic, customisableNone (Flask-Admin extension)
Forms and validationBuilt-in Forms/ModelFormsFlask-WTF extension
Auth and sessionsBuilt-in user model, permissionsFlask-Login + manual work
CSRF protectionOn by defaultVia Flask-WTF
TemplatesDjango Template Language (Jinja2 optional)Jinja2
Modularity unitAppBlueprint
Routing styleCentral urls.py with path()@app.route decorators
Learning curveSteeper, many conventionsGentle, few conventions
Best fitCRUD products, portals, dashboards, e-governanceMicroservices, ML model serving, small APIs, prototypes
Async supportASGI, async views, async ORM in progressAsync views on WSGI; use FastAPI/Quart for heavy async
# Django: model, view, URL
class Job(models.Model):
    title = models.CharField(max_length=200)
    employer = models.ForeignKey('Employer', on_delete=models.CASCADE,
                                 related_name='jobs')
    city = models.CharField(max_length=80, db_index=True)
    ctc_lpa = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    posted_on = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-posted_on']
        indexes = [models.Index(fields=['city', 'posted_on'])]

def job_list(request):
    qs = (Job.objects.select_related('employer')
          .filter(city__iexact=request.GET.get('city', 'Kochi')))
    return render(request, 'jobs/list.html', {'jobs': qs[:20]})

# urls.py
urlpatterns = [path('jobs/', job_list, name='job-list')]
# Flask: factory, blueprint, route
from flask import Flask, Blueprint, request, jsonify, render_template

bp = Blueprint('jobs', __name__)

@bp.route('/jobs', methods=['GET'])
def jobs():
    city = request.args.get('city', 'Kochi')
    rows = Job.query.filter(Job.city.ilike(city)).limit(20).all()
    if request.accept_mimetypes.best == 'application/json':
        return jsonify([r.as_dict() for r in rows])
    return render_template('jobs.html', jobs=rows)

def create_app(config_object='config.Production'):
    app = Flask(__name__)
    app.config.from_object(config_object)
    db.init_app(app)
    app.register_blueprint(bp, url_prefix='/api')
    return app

Where it is used

Interview questions

Common mistakes

Related topics

REST APIs · Back-end development with Node.js · Authentication and authorisation: sessions, OAuth and JWT · API design: GraphQL, gRPC and OpenAPI · Spring Boot and microservices development · Front-end frameworks: React basics

Open in the Knowledge reader →Find jobs →