Skip to main content

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:

  1. Improve maintainability by keeping related code together
  2. Enhance reusability by creating apps that work across multiple projects
  3. 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 ProjectDjango App
Collection of apps and configurationsFocused module that performs a specific function
Contains settings, base URLs, and WSGI/ASGI configurationContains models, views, templates, and URLs
Usually only one per websiteUsually many per website
Created with django-admin startprojectCreated 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:

bash
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:

python
# 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:

python
# 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:

bash
python manage.py makemigrations blog
python manage.py migrate

Step 5: Create Views

Add a view to display blog posts:

python
# 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:

python
# 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:

python
# 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:

html
<!-- 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:

python
# 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:

python
# 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:

python
# 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:

python
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:

bash
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

bash
python manage.py startapp polls
python
# myproject/settings.py
INSTALLED_APPS = [
# ...
'polls.apps.PollsConfig',
]

Step 2: Define Models

python
# 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

python
# 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

python
# 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'),
]
python
# 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

html
<!-- 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 %}
html
<!-- 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>
html
<!-- 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

python
# 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

  1. Create a Todo App: Build a simple todo app with Task models, list and detail views, and the ability to mark tasks as complete.

  2. 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.

  3. App Extension: Add custom template tags to one of your apps to extend its functionality.

  4. Multi-App Project: Create a project with at least three interacting apps (e.g., a blog with comments and user profiles).

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)