Django Apps
Introduction
Django applications, or "apps," are a fundamental building block of any Django project. Unlike some web frameworks that treat an entire project as a single unit, Django encourages you to think of your project as a collection of loosely coupled, reusable applications. This modular approach is one of Django's greatest strengths, making your code more organized, maintainable, and reusable.
In this tutorial, we'll explore what Django apps are, how they work, and how to create and use them effectively in your projects. Whether you're building a simple blog or a complex web application, understanding Django's app architecture will help you create more organized and scalable code.
What Are Django Apps?
A Django app is a self-contained package that provides a specific piece of functionality for your project. Each app typically follows the Model-View-Template (MVT) pattern and includes its own models, views, templates, and URLs.
Think of apps as LEGO blocks for your web application. Each one serves a specific purpose and can be put together with others to build a complete project. Some common examples of Django apps include:
- A blog app that handles posts, comments, and categories
- An authentication app that manages user registration and login
- A shopping cart app that tracks items and processes checkout
By organizing your code into apps, you can:
- Improve maintainability by keeping related code together
- Enhance reusability by creating apps that work across multiple projects
- Collaborate more effectively with other developers by having clear boundaries
Project vs App: What's the Difference?
Before diving deeper, it's important to understand the distinction between a Django project and a Django app:
Django Project | Django App |
---|---|
Collection of apps and configurations | Focused module that performs a specific function |
Contains settings, base URLs, and WSGI/ASGI configuration | Contains models, views, templates, and URLs |
Usually only one per website | Usually many per website |
Created with django-admin startproject | Created with python manage.py startapp |
Creating Your First Django App
Let's create a simple blog app to demonstrate how Django apps work in practice.
Step 1: Start a New App
From your project directory, run:
python manage.py startapp blog
This will create a new directory called blog
with the following structure:
blog/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
Step 2: Register Your App
To tell Django to use your app, add it to the INSTALLED_APPS
list in your project's settings.py
file:
# myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # Add your new app here
]
Step 3: Define Models
Let's create a simple blog post model in the models.py
file:
# blog/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(default=timezone.now)
def __str__(self):
return self.title
Step 4: Create Migrations and Apply Them
After defining your models, create and apply migrations:
python manage.py makemigrations blog
python manage.py migrate
Step 5: Create Views
Add a view to display blog posts:
# blog/views.py
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.order_by('-published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
Step 6: Create URLs
Create a urls.py
file in your blog app:
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog' # This helps with namespacing
urlpatterns = [
path('', views.post_list, name='post_list'),
]
Then include these URLs in your project's urls.py
:
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
Step 7: Create Templates
Create a templates directory structure:
blog/
├── templates/
│ └── blog/
│ └── post_list.html
And add a simple template:
<!-- blog/templates/blog/post_list.html -->
<!DOCTYPE html>
<html>
<head>
<title>My Blog</title>
</head>
<body>
<h1>Blog Posts</h1>
{% for post in posts %}
<div>
<h2>{{ post.title }}</h2>
<p>{{ post.published_date }}</p>
<p>{{ post.content }}</p>
</div>
{% empty %}
<p>No posts yet.</p>
{% endfor %}
</body>
</html>
Understanding the App Configuration Class
Every Django app has an apps.py
file that contains an AppConfig
class. This class provides metadata about your app and lets you configure certain aspects of its behavior:
# blog/apps.py
from django.apps import AppConfig
class BlogConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog'
verbose_name = 'Blog Application'
def ready(self):
# This code runs when the app is ready
# You can register signals or perform other setup here
pass
You can specify which config class to use when registering your app in INSTALLED_APPS
:
# myproject/settings.py
INSTALLED_APPS = [
# ...
'blog.apps.BlogConfig', # Use this instead of just 'blog'
# ...
]
Django App Best Practices
1. Keep Apps Focused and Small
Each app should do one thing and do it well. If your app is growing too large, consider splitting it into multiple apps.
2. Use Descriptive App Names
Choose names that clearly describe what the app does:
- Good:
accounts
,blog
,shop
,payments
- Avoid:
app1
,stuff
,utils
3. Create a Custom App Template Structure
Consider adding more directories to organize your app better:
blog/
├── __init__.py
├── admin.py
├── apps.py
├── forms.py # Form definitions
├── managers.py # Custom model managers
├── middleware.py # App-specific middleware
├── migrations/
├── models.py
├── services.py # Business logic
├── signals.py # Signal handlers
├── static/ # App-specific static files
│ └── blog/
├── templates/ # App-specific templates
│ └── blog/
├── templatetags/ # Custom template tags
├── tests/ # Organized tests
│ ├── __init__.py
│ ├── test_forms.py
│ ├── test_models.py
│ └── test_views.py
├── urls.py
└── views.py
4. Use App Namespaces
Always use namespaces for your URLs, templates, and static files:
# URLs namespacing
app_name = 'blog'
# Template namespacing
templates/blog/post_list.html
# Static file namespacing
static/blog/style.css
Reusable Apps
One of the key benefits of Django's app architecture is the ability to reuse apps across projects. Here's how to create a reusable app:
1. Create a Standalone App
Start by creating a more generalized version of your app that doesn't depend on specific project settings.
2. Package Your App
Create a setup.py
file to make your app installable:
from setuptools import setup, find_packages
setup(
name="django-simple-blog",
version="0.1",
packages=find_packages(),
include_package_data=True,
install_requires=[
"Django>=3.2",
],
description="A simple Django blog app",
author="Your Name",
author_email="[email protected]",
url="https://github.com/yourusername/django-simple-blog",
)
3. Include Templates and Static Files
Create a MANIFEST.in
file to include non-Python files:
include LICENSE
include README.md
recursive-include blog/templates *
recursive-include blog/static *
4. Install Your App
You can install your app from a local directory, Git repository, or PyPI:
pip install -e /path/to/your/app
# or
pip install git+https://github.com/yourusername/django-simple-blog.git
Real-World Example: Creating a Polls App
Let's create a more comprehensive example of a polls app, similar to Django's official tutorial:
Step 1: Create and Register the App
python manage.py startapp polls
# myproject/settings.py
INSTALLED_APPS = [
# ...
'polls.apps.PollsConfig',
]
Step 2: Define Models
# polls/models.py
from django.db import models
from django.utils import timezone
import datetime
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
Step 3: Create Views
# polls/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
Step 4: Configure URLs
# polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
# myproject/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
Step 5: Create Templates
<!-- polls/templates/polls/index.html -->
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
<!-- polls/templates/polls/detail.html -->
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
<!-- polls/templates/polls/results.html -->
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
Step 6: Register with Admin
# polls/admin.py
from django.contrib import admin
from .models import Question, Choice
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
inlines = [ChoiceInline]
list_display = ('question_text', 'pub_date', 'was_published_recently')
list_filter = ['pub_date']
search_fields = ['question_text']
admin.site.register(Question, QuestionAdmin)
Summary
Django apps are the cornerstone of Django's modular architecture, allowing you to organize your code into reusable components. Key takeaways from this tutorial include:
- Django apps are self-contained packages that provide specific functionality
- Apps help you maintain a clean separation of concerns in your projects
- Creating an app involves defining models, views, URLs, and templates
- Apps can be reused across different projects
- Following best practices like namespacing and focused design makes your apps more maintainable
By breaking your project into logical apps, you'll create more maintainable code and potentially save time in future projects by reusing your work.
Exercises
-
Create a Todo App: Build a simple todo app with Task models, list and detail views, and the ability to mark tasks as complete.
-
Convert Existing App to Reusable Package: Take an app you've created and convert it into a reusable package that can be installed via pip.
-
App Extension: Add custom template tags to one of your apps to extend its functionality.
-
Multi-App Project: Create a project with at least three interacting apps (e.g., a blog with comments and user profiles).
Additional Resources
- Django Documentation on Applications
- Django Packages - A directory of reusable Django apps
- Django Extensions - A collection of custom extensions for Django
- Cookiecutter Django Package - A template for creating reusable Django packages
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)