Skip to content

Django - Quick Guide

Django is a high-level Python web framework designed for rapid development of secure and maintainable websites. It encourages clean, pragmatic design and follows the ‘Don’t Repeat Yourself’ (DRY) principle.

Note: Licensed under the BSD license, maintained by the Django Software Foundation (DSF).

  • Started 2003 at Lawrence Journal-World newspaper.
  • Publicly released July 2005.
  • Actively developed open-source project with a large community.
  • Loosely Coupled: Components are independent.
  • Less Code: Reduces boilerplate.
  • DRY: Avoid redundancy.
  • Fast Development: Tools speed up the process.
  • Clean Design: Promotes best practices.
  • Explicit over Implicit: Avoids ‘magic’.
  • ORM (Object-Relational Mapper): Interact with databases using Python.
  • Admin Interface: Automatic site administration.
  • URL Routing: Clean URL design.
  • Template System: Separate presentation from logic.
  • Form Handling: Validation and processing.
  • Security: Built-in protections.
  • Scalability: Decoupled components.
  • Versatility: Build various web applications.
  • Batteries Included: Many built-in features.
  • Development Server: Lightweight server for testing.

Django generally follows the Model-View-Template (MVT) architectural pattern, a variation of the common Model-View-Controller (MVC) pattern.

  • Model: Represents the data structure (usually database tables). Defines fields and behaviors of your data.
  • View: Handles the request logic. It receives an HTTP request, interacts with models (if necessary), and returns an HTTP response, often by rendering a template.
  • Template: Defines the presentation layer (HTML). It includes placeholders for data (variables) and basic logic (tags, filters) provided by the view.
  • URL Dispatcher: Maps requested URLs to specific views.

In MVT, the ‘View’ acts more like the ‘Controller’ in MVC (handling logic), and the ‘Template’ acts like the ‘View’ in MVC (handling presentation). Django itself handles much of the controller-like framework aspects (routing requests, etc.).

  1. User requests a URL.
  2. Django’s URL Dispatcher finds the matching URL pattern and calls the associated View.
  3. The View executes logic, potentially interacting with Models to fetch or save data.
  4. The View selects a Template and passes data (context) to it.
  5. The Template Engine renders the Template with the context, generating HTML.
  6. The View returns the rendered HTML as an HttpResponse back to the user’s browser.

Setting up a Django development environment involves installing Python, Django itself, and usually a database system.

Django requires Python. Check the specific Django version documentation for compatible Python versions (modern Django requires Python 3.8+). Download Python from python.org if you don’t have it. Verify your installation with python --version or python3 --version.

Best Practice: Use virtual environments to isolate project dependencies. Create one with:

$ python -m venv myenv
$ source myenv/bin/activate # On Linux/macOS
$ myenv\Scripts\activate # On Windows

With your virtual environment activated, install Django using pip:

$ pip install Django

Verify installation:

$ python -m django --version

Django supports PostgreSQL, MySQL, SQLite, and Oracle. For development, SQLite is often sufficient and requires no separate server setup (it’s configured by default). For production, PostgreSQL or MySQL are commonly used. You’ll need to install the database server itself and the corresponding Python driver (psycopg2-binary for PostgreSQL, mysqlclient for MySQL) within your virtual environment.

Configure your database connection in settings.py’s DATABASES dictionary.

Django includes a lightweight development web server (python manage.py runserver). This is not suitable for production. For deployment, common choices include Gunicorn or uWSGI running behind a reverse proxy like Nginx, or using Apache with mod_wsgi.

A Django Project represents a web application.

$ django-admin startproject myproject
myproject/
├── manage.py
└── myproject/
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
  • manage.py: Command-line utility.
  • settings.py: Project configuration.
  • urls.py: Main URL routing.
  • wsgi.py/asgi.py: Server entry points.
  • Set DEBUG = True (for development only).
  • Configure DATABASES (defaults to SQLite).
  • Review INSTALLED_APPS, MIDDLEWARE, TEMPLATES, STATIC_URL.
$ python manage.py runserver

Access http://127.0.0.1:8000/ in your browser to see the welcome page.

A Django project is made up of Apps. An app is a reusable module handling specific functionality (e.g., blog, polls).

Navigate to your project directory (where manage.py is) and run:

$ python manage.py startapp myapp

This creates a myapp directory:

myapp/
├── __init__.py
├── admin.py # Admin site configuration
├── apps.py # App configuration
├── migrations/ # Database migration files
│ └── __init__.py
├── models.py # Data models (database structure)
├── tests.py # Application tests
└── views.py # Request handling logic (views)

Add your app to the INSTALLED_APPS list in your project’s settings.py file. Use the app config class path found in myapp/apps.py:

myproject/settings.py
INSTALLED_APPS = [
# ... other apps
'myapp.apps.MyappConfig', # Or just 'myapp'
'django.contrib.admin',
'django.contrib.auth',
# ... more default apps
]
  1. Define models in models.py.
  2. Run python manage.py makemigrations myapp to create migration files.
  3. Run python manage.py migrate to apply changes to the database.
  4. Create views in views.py to handle requests.
  5. Create templates (e.g., in myapp/templates/myapp/) for presentation.
  6. Define URL patterns in myapp/urls.py and include them in the project’s urls.py.
  7. Optionally, register models in admin.py to manage them via the admin site.
  8. Write tests in tests.py.

Django’s admin is a powerful, auto-generated interface for managing your site’s content.

  • Ensure django.contrib.admin and its dependencies are in INSTALLED_APPS.
  • Ensure required middleware (SessionMiddleware, AuthenticationMiddleware, etc.) are in MIDDLEWARE.
  • Include admin URLs in project urls.py: path('admin/', admin.site.urls).
1. Run migrations: `python manage.py migrate`
2. Create a superuser: `python manage.py createsuperuser`

Run python manage.py runserver and go to /admin/. Log in with the superuser credentials.

The admin site lists registered models. You can perform CRUD operations (Create, Read, Update, Delete) on model instances. To make your own models manageable, register them in your app’s admin.py file.

Example myapp/admin.py:

from django.contrib import admin
from .models import YourModel # Import your model
admin.site.register(YourModel) # Register your model

Views handle request logic and return responses. They live in views.py.

myapp/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world!")
myapp/views.py
from django.shortcuts import render
import datetime
def current_time(request):
now = datetime.datetime.now()
return render(request, 'myapp/time.html', {'current_time': now})

myapp/templates/myapp/time.html:

<p>Current time: {{ current_time }}</p>
myapp/views.py
from django.http import HttpResponse
def detail(request, item_id):
return HttpResponse(f"You're looking at item {item_id}.")

Parameters are captured from the URL configuration.

Django maps requested URLs to views using urls.py files.

The main entry point for URL routing. It typically includes URLs from individual apps.

myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include('myapp.urls')), # Include app URLs
]

Defines URL patterns specific to the app.

myapp/urls.py
from django.urls import path
from . import views
# app_name = 'myapp' # Optional: Namespace for URLs
urlpatterns = [
path('', views.index, name='index'), # e.g., /myapp/
path('time/', views.current_time, name='current_time'), # e.g., /myapp/time/
path('item/<int:item_id>/', views.detail, name='detail'), # e.g., /myapp/item/5/
]

Explanation:

  • path(): Defines a URL route.
  • First argument: URL pattern string. Converters like <int:item_id> capture parts of the URL and pass them as arguments to the view.
  • Second argument: The view function or class-based view’s as_view() method.
  • name: A unique name for the URL pattern, used for reversing URLs (e.g., in templates with {% url %}).

Separates presentation (HTML) from logic (Python).

  • {{ variable }}: Outputs the value of a variable from the context.
  • {{ variable.attribute }}: Accesses attributes.
  • {{ variable|filter }}: Applies filters to modify output (e.g., {{ name|lower }}).
  • {% tag %}: Performs logic (e.g., {% if %}, {% for %}, {% url %}, {% extends %}).
  • {% if condition %} … {% else %} … {% endif %}: Conditionals.
  • {% for item in list %} … {% empty %} … {% endfor %}: Loops.
  • {% extends 'base.html' %}: Template inheritance (must be first tag).
  • {% block block_name %} … {% endblock %}: Defines overridable blocks in base templates.
  • {% url 'url_name' arg1 arg2 %}: Reverses URLs.
  • {% load static %}: Loads static file tags.
  • {% static 'path/to/file.css' %}: Generates static file URL.
  • {# comment #} or {% comment %} … {% endcomment %}: Comments.

Views pass data to templates via a context dictionary: render(request, 'template.html', {'my_var': value}).

Models define the structure of your application’s data, mapping to database tables.

myapp/models.py
from django.db import models
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_date = models.DateTimeField('date published', default=timezone.now)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
def __str__(self):
return self.title
class Meta:
ordering = ['-published_date'] # Default ordering

Explanation:

  • Each class inheriting from models.Model represents a database table.
  • Class attributes are model fields (CharField, TextField, DateTimeField, ForeignKey, etc.), representing table columns.
  • ForeignKey, ManyToManyField, OneToOneField define relationships.
  • on_delete=models.CASCADE: Defines behavior when the related object is deleted.
  • __str__: Returns a human-readable string representation of the object (used in admin).
  • Meta class: Optional metadata (e.g., default ordering, table name).

After changing models:

1. Create migrations: `python manage.py makemigrations myapp`
2. Apply migrations to DB: `python manage.py migrate`

Django provides a database-abstraction API (ORM).

from .models import Post
# Get all posts
all_posts = Post.objects.all()
# Get a specific post by primary key (pk)
post = Post.objects.get(pk=1)
# Filter posts
recent_posts = Post.objects.filter(published_date__year=2023)
# Create a new post
new_post = Post.objects.create(title='New Title', content='...', author=request.user)
# Update a post
post.title = 'Updated Title'
post.save()
# Delete a post
post.delete()

Use django.shortcuts.redirect to send the user to a different URL.

from django.shortcuts import redirect
def old_view(request):
# Redirect using a URL name (best practice)
return redirect('new_view_name', permanent=True)
def another_view(request):
# Redirect using a hardcoded path
return redirect('/some/other/path/')
def view_with_args(request, item_id):
# Redirect with arguments
return redirect('item_detail', pk=item_id)

Arguments to redirect:

  • A model instance (uses its get_absolute_url() method).
  • A URL name (from urls.py) plus optional arguments/keyword arguments.
  • A hardcoded URL path string (e.g., /myapp/home/).
  • permanent=True sends a 301 (Permanent) redirect, default is 302 (Temporary).

Use django.urls.reverse to get the URL string without redirecting (useful in models or other places).

from django.urls import reverse
url = reverse('item_detail', kwargs={'pk': 5})
# url will be '/myapp/item/5/' (depending on your URLConf)

Django provides functions for sending emails.

Set up email backend and credentials:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# For development, use console output:
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your_email@example.com'
EMAIL_HOST_PASSWORD = 'your_password'
from django.core.mail import send_mail
send_mail(
'Subject here',
'Here is the message.',
'from@example.com', # Sender
['to@example.com'], # List of recipients
fail_silently=False,
)
from django.core.mail import send_mail
html_message = '<p>This is an **HTML** message.</p>'
send_mail(
'HTML Subject',
'Fallback plain text message.', # Required for non-HTML clients
'from@example.com',
['to@example.com'],
html_message=html_message,
)
from django.core.mail import EmailMessage
email = EmailMessage(
'Hello',
'Body goes here',
'from@example.com',
['to1@example.com', 'to2@example.com'],
bcc=['bcc@example.com'],
reply_to=['another@example.com'],
headers={'Message-ID': 'foo'},
)
# email.attach('logo.png', img_data, 'image/png') # Attach files
email.send()

Class-based views that solve common web development patterns (displaying lists, detail pages, forms, etc.) with minimal code.

  • TemplateView: Renders a template.
  • ListView: Displays a list of objects.
  • DetailView: Displays details for a single object.
  • CreateView, UpdateView, DeleteView: Handle forms for creating, updating, deleting objects.
  • FormView: Handles form display and processing.
myapp/views.py
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'myapp/post_list.html' # Default: <app>/<model>_list.html
context_object_name = 'posts' # Default: object_list
paginate_by = 10 # Optional: Add pagination
myapp/urls.py
from django.urls import path
from .views import PostListView
urlpatterns = [
path('posts/', PostListView.as_view(), name='post_list'),
]
myapp/templates/myapp/post_list.html
<h1>Posts</h1>
<ul>
{% for post in posts %}
<li><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></li>
{% empty %}
<li>No posts yet.</li>
{% endfor %}
</ul>
<!-- Add pagination controls if using paginate_by -->

Django’s forms library handles rendering, validation, and data cleaning.

myapp/forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
myapp/views.py
from django.shortcuts import render, redirect
from .forms import ContactForm
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Process the data in form.cleaned_data
name = form.cleaned_data['name']
email = form.cleaned_data['email']
# ... send email, save to db, etc.
return redirect('success_page') # Redirect after POST
else: # GET
form = ContactForm()
return render(request, 'myapp/contact.html', {'form': form})
myapp/templates/myapp/contact.html
<form method="post">
{% csrf_token %} <!-- Essential for security -->
{{ form.as_p }} <!-- Render fields wrapped in <p> tags -->
<button type="submit">Send</button>
</form>

form.is_valid() runs validation. Errors are accessible via form.errors, form.field_name.errors, form.non_field_errors. Custom validation via clean_fieldname() or clean() methods in the form class.

Handling user-uploaded files.

  • Install Pillow: pip install Pillow (for ImageField).
  • Configure MEDIA_ROOT (filesystem path where files are stored) and MEDIA_URL (URL prefix to serve them) in settings.py.
  • Configure URL patterns to serve media files during development (see Django docs).
myapp/models.py
class UserProfile(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
# Use FileField for non-image files
myapp/forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['avatar'] # Or use forms.ImageField in a forms.Form
myapp/views.py
def upload_avatar(request):
if request.method == 'POST':
# Pass request.FILES to the form!
form = ProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect('profile_view')
else:
form = ProfileForm(instance=request.user.userprofile)
return render(request, 'myapp/upload.html', {'form': form})
myapp/templates/myapp/upload.html
<!-- IMPORTANT: Add enctype for file uploads -->
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>

Deploying Django with Apache often uses mod_wsgi.

Apache server installed, mod_wsgi installed and enabled.

Apache Configuration (httpd.conf or virtual host file):

Section titled “Apache Configuration (httpd.conf or virtual host file):”
# Example mod_wsgi configuration
WSGIScriptAlias / /path/to/your/myproject/myproject/wsgi.py
WSGIPythonHome /path/to/your/virtualenv
WSGIPythonPath /path/to/your/myproject
<Directory /path/to/your/myproject/myproject>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
# Alias for static files (handled by Apache)
Alias /static/ /path/to/your/project/staticfiles/
<Directory /path/to/your/project/staticfiles>
Require all granted
</Directory>
# Alias for media files (handled by Apache)
Alias /media/ /path/to/your/project/mediafiles/
<Directory /path/to/your/project/mediafiles>
Require all granted
</Directory>

Notes:

  • Replace paths with your actual project/virtualenv paths.
  • Ensure DEBUG = False in settings.py.
  • Configure ALLOWED_HOSTS in settings.py.
  • Run python manage.py collectstatic to gather static files into the directory specified by STATIC_ROOT (which should match /path/to/your/project/staticfiles/ above).

A very common production setup uses Gunicorn (or uWSGI) as the application server running Django, with Nginx acting as a reverse proxy in front. Nginx handles incoming requests, serves static/media files directly, and forwards dynamic requests to Gunicorn. This setup is generally considered more performant and flexible.

Cookies store small amounts of data on the client’s browser.

Cookies are set on the HttpResponse object.

from django.shortcuts import render
import datetime
def set_cookie_view(request):
response = render(request, 'myapp/cookie_set.html')
response.set_cookie(
'username',
'testuser',
max_age=3600, # Expires in 1 hour
# expires=datetime.datetime.utcnow() + datetime.timedelta(days=7),
# httponly=True, # Recommended: JS cannot access
# secure=True, # Recommended: Only send over HTTPS
# samesite='Lax' # Recommended: CSRF protection
)
return response

Cookies are read from the HttpRequest object’s COOKIES dictionary.

def read_cookie_view(request):
username = request.COOKIES.get('username', 'Guest') # Use .get() for default
return render(request, 'myapp/cookie_read.html', {'username': username})
def delete_cookie_view(request):
response = render(request, 'myapp/cookie_deleted.html')
response.delete_cookie('username')
return response

Note: Cookies are client-side and can be modified by the user. Do not store sensitive information directly in cookies. Use Django sessions instead.

Sessions allow storing data server-side, associated with a unique session ID stored in a client-side cookie. More secure than plain cookies for user data.

  • Ensure django.contrib.sessions is in INSTALLED_APPS.
  • Ensure django.contrib.sessions.middleware.SessionMiddleware is in MIDDLEWARE (usually included by default).
  • Run python manage.py migrate (if using database-backed sessions, the default).

SESSION_ENGINE setting controls storage (database, cached DB, file, signed cookies).

Access session data via the request.session dictionary.

def session_example_view(request):
# Set a session value
request.session['favorite_color'] = 'blue'
# Get a session value
fav_color = request.session.get('favorite_color', 'unknown')
# Check if key exists
if 'user_id' in request.session:
user_id = request.session['user_id']
# Delete a session value
if 'temp_data' in request.session:
del request.session['temp_data']
# Clear the entire session (logs user out if using auth)
# request.session.flush()
# Set session expiry (e.g., 30 minutes idle)
# request.session.set_expiry(1800)
return render(request, 'myapp/session_info.html', {'color': fav_color})

Sessions are automatically saved when modified.

Caching stores results of expensive operations to improve performance.

Configure the CACHES setting.

CACHES = {
'default': { # 'default' is the standard alias
# Memcached (Recommended for production)
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
# Or use Redis (Requires django-redis package)
# 'BACKEND': 'django_redis.cache.RedisCache',
# 'LOCATION': 'redis://127.0.0.1:6379/1',
# Or Database Cache
# 'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
# 'LOCATION': 'my_cache_table', # Run createcachetable
# Or Filesystem Cache
# 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
# 'LOCATION': '/var/tmp/django_cache',
# Or Local Memory Cache (Per-process, mainly for development)
# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
# 'LOCATION': 'unique-snowflake',
}
}
  • Site-wide: Use UpdateCacheMiddleware and FetchFromCacheMiddleware in MIDDLEWARE.
  • Per-View: Use the @cache_page(timeout_seconds) decorator on a view function, or wrap the view in urls.py.
  • Template Fragment: Use {% load cache %} and {% cache timeout_seconds fragment_name %} ... {% endcache %} within templates.
  • Low-Level API: Use django.core.cache.cache.set('key', value, timeout) and cache.get('key') directly in code.

Example: Per-View Caching

myapp/views.py
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # Cache for 15 minutes
def expensive_view(request):
# ... perform expensive calculation ...
return render(request, 'myapp/results.html', context)

Note: The built-in django.contrib.comments framework was removed from Django core starting version 1.8 and is deprecated. Do not use it for new projects.

  • Third-Party Django Apps: Search PyPI for maintained comment apps like django-comments-xtd which builds upon the concepts of the old framework.
  • External Services: Integrate hosted commenting platforms like Disqus or Commento using JavaScript snippets.

Conceptual Approach (if building your own):

Section titled “Conceptual Approach (if building your own):”

You would typically create a Comment model with fields like user (ForeignKey to User), content (TextField), timestamp (DateTimeField), and importantly, a GenericForeignKey to link the comment to any other model instance (e.g., a Blog Post, a Product, etc.). You would then create forms and views to handle comment submission and display.

Due to the deprecation, detailed implementation steps for the old framework are omitted.

Use django.contrib.syndication to generate RSS or Atom feeds.

myapp/feeds.py
from django.contrib.syndication.views import Feed
from django.urls import reverse
from .models import Post # Assuming a Post model
class LatestPostsFeed(Feed):
title = "My Blog Posts"
link = "/blog/"
description = "Latest updates from my blog."
def items(self):
return Post.objects.order_by('-published_date')[:5]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.content # Or a summary field
# item_link is optional if model has get_absolute_url
def item_link(self, item):
return reverse('post_detail', args=[item.pk])
myapp/urls.py
from django.urls import path
from .feeds import LatestPostsFeed
urlpatterns = [
# ... other urls
path('latest/feed/', LatestPostsFeed(), name='post_feed'),
]

Accessing the feed URL (e.g., /myapp/latest/feed/) generates the XML feed.

AJAX (Asynchronous JavaScript and XML) allows web pages to update content dynamically without full page reloads, often by communicating with the server in the background.

Modern AJAX in Django typically involves:

  1. Frontend JavaScript: Using the browser’s fetch API, libraries like Axios, or frameworks (React, Vue, Angular) to make asynchronous HTTP requests (GET, POST, etc.) to the Django backend.
  2. Django Backend View: A regular Django view that receives the AJAX request, processes it (e.g., queries the database, validates data), and returns data, usually in JSON format using django.http.JsonResponse.
  3. URL Endpoint: A URL pattern in urls.py mapped to the Django view that handles the AJAX request.
  4. CSRF Handling: Ensuring the CSRF token is included in POST/PUT/DELETE requests sent via JavaScript.

Note: Older libraries like django-dajaxice mentioned in previous versions are generally not recommended or maintained anymore. Focus on standard frontend JS and backend JSON responses.

  1. Template (like_button.html):
<button id="like-btn" data-post-id="{{ post.id }}" data-url="{% url 'like_post' %}">
Like ({{ post.like_count }})
</button>
<script>
const likeBtn = document.getElementById('like-btn');
const postId = likeBtn.dataset.postId;
const url = likeBtn.dataset.url;
const csrftoken = getCookie('csrftoken'); // Function to get CSRF cookie value
likeBtn.addEventListener('click', () => {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify({ post_id: postId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
likeBtn.textContent = `Like (${data.new_count})`;
// Maybe update button style
} else {
alert('Error liking post: ' + data.error);
}
})
.catch(error => console.error('Error:', error));
});
// Simple function to get CSRF cookie (see Django docs for robust version)
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
</script>
  1. View (views.py):
import json
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.shortcuts import get_object_or_404
from .models import Post
@require_POST # Ensure this view only accepts POST requests
def like_post_view(request):
try:
data = json.loads(request.body)
post_id = data.get('post_id')
post = get_object_or_404(Post, pk=post_id)
# Add logic to increment like count (consider atomicity)
post.like_count += 1
post.save(update_fields=['like_count'])
return JsonResponse({'success': True, 'new_count': post.like_count})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)}, status=400)
  1. URL (urls.py):
from django.urls import path
from . import views
urlpatterns = [
# ... other urls
path('like_post/', views.like_post_view, name='like_post'),
]

For complex APIs, consider using Django REST Framework (DRF), which provides serializers, viewsets, and routers to build robust REST APIs.