第一个项目
本节将带你创建一个完整的 Django 项目,包括博客文章的增删改查功能。通过这个项目,你将学习 Django 的核心概念和工作流程。
创建项目
使用 django-admin 命令创建项目:
# 创建项目
django-admin startproject myblog
# 进入项目目录
cd myblog
这会创建以下目录结构:
myblog/
├── manage.py # 项目管理脚本
└── myblog/ # 项目配置目录
├── __init__.py
├── settings.py # 项目设置
├── urls.py # 主 URL 配置
├── asgi.py # ASGI 入口
└── wsgi.py # WSGI 入口
manage.py 的作用
manage.py 是 Django 项目的管理脚本,常用的命令包括:
| 命令 | 说明 |
|---|---|
runserver | 启动开发服务器 |
startapp | 创建新应用 |
makemigrations | 生成数据库迁移文件 |
migrate | 执行数据库迁移 |
createsuperuser | 创建管理员账户 |
shell | 进入 Django 交互式 Shell |
collectstatic | 收集静态文件 |
创建应用
在 Django 中,项目是配置和应用的集合,应用是完成特定功能的模块。让我们创建一个博客应用:
python manage.py startapp blog
这会创建以下目录结构:
blog/
├── __init__.py
├── admin.py # 管理后台配置
├── apps.py # 应用配置
├── models.py # 数据模型
├── tests.py # 测试文件
├── views.py # 视图函数
└── migrations/ # 数据库迁移目录
└── __init__.py
注册应用
创建应用后,需要在项目中注册它。编辑 myblog/settings.py:
# myblog/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 添加自定义应用
'blog',
]
定义数据模型
模型是数据的单一、明确的信息来源。它包含存储数据的基本字段和行为。编辑 blog/models.py:
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
"""文章分类"""
name = models.CharField('分类名称', max_length=100)
slug = models.SlugField('URL 别名', unique=True)
description = models.TextField('分类描述', blank=True)
class Meta:
verbose_name = '分类'
verbose_name_plural = '分类'
ordering = ['name']
def __str__(self):
return self.name
class Post(models.Model):
"""博客文章"""
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
]
title = models.CharField('标题', max_length=200)
slug = models.SlugField('URL 别名', unique_for_date='publish')
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='posts',
verbose_name='作者'
)
content = models.TextField('内容')
category = models.ForeignKey(
Category,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='posts',
verbose_name='分类'
)
status = models.CharField(
'状态',
max_length=10,
choices=STATUS_CHOICES,
default='draft'
)
publish = models.DateTimeField('发布时间', auto_now_add=True)
created = models.DateTimeField('创建时间', auto_now_add=True)
updated = models.DateTimeField('更新时间', auto_now=True)
class Meta:
verbose_name = '文章'
verbose_name_plural = '文章'
ordering = ['-publish']
def __str__(self):
return self.title
模型字段说明
- CharField:用于存储字符串,必须指定
max_length - SlugField:用于存储 URL 友好的字符串,通常用于生成 URL
- TextField:用于存储长文本
- DateTimeField:用于存储日期时间
- ForeignKey:定义多对一关系
auto_now_add=True:创建时自动设置当前时间auto_now=True:每次保存时自动更新为当前时间
执行数据库迁移
Django 使用迁移系统来管理数据库结构的变化:
# 生成迁移文件
python manage.py makemigrations
# 查看迁移 SQL
python manage.py sqlmigrate blog 0001
# 执行迁移
python manage.py migrate
迁移过程:
创建管理后台
Django 内置了强大的管理后台。首先创建管理员账户:
python manage.py createsuperuser
按提示输入用户名、邮箱和密码。
注册模型到管理后台
编辑 blog/admin.py:
# blog/admin.py
from django.contrib import admin
from .models import Category, Post
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']
prepopulated_fields = {'slug': ('name',)}
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'slug', 'author', 'category', 'status', 'publish']
list_filter = ['status', 'created', 'publish', 'category']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ['author']
date_hierarchy = 'publish'
ordering = ['status', '-publish']
启动开发服务器
python manage.py runserver
访问 http://127.0.0.1:8000/admin/,使用刚才创建的管理员账户登录,你将看到博客文章的管理界面。
创建视图
视图负责处理用户请求并返回响应。编辑 blog/views.py:
# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post, Category
def post_list(request):
"""文章列表"""
posts = Post.objects.filter(status='published')
categories = Category.objects.all()
return render(request, 'blog/post_list.html', {
'posts': posts,
'categories': categories,
})
def post_detail(request, pk):
"""文章详情"""
post = get_object_or_404(Post, pk=pk, status='published')
return render(request, 'blog/post_detail.html', {'post': post})
def category_posts(request, slug):
"""分类文章列表"""
category = get_object_or_404(Category, slug=slug)
posts = Post.objects.filter(category=category, status='published')
return render(request, 'blog/category_posts.html', {
'category': category,
'posts': posts,
})
视图函数解析
request参数是 Django 的 HttpRequest 对象,包含请求的所有信息get_object_or_404()是一个快捷函数,如果对象不存在则返回 404 错误render()函数将模板和上下文渲染成 HttpResponse
配置 URL
URL 配置将 URL 模式映射到视图函数。首先在 blog 应用中创建 urls.py:
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('category/<slug:slug>/', views.category_posts, name='category_posts'),
]
然后在项目的主 URL 配置中包含应用的 URL:
# myblog/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
URL 参数类型
| 类型 | 说明 | 示例 |
|---|---|---|
str | 匹配非空字符串,不含 / | 'hello' |
int | 匹配正整数 | 123 |
slug | 匹配字母、数字、下划线、连字符 | 'my-post' |
uuid | 匹配 UUID 格式 | '075194d3-6885-417e-a8a8-6c931e272f00' |
path | 匹配任意非空字符串,包含 / | 'path/to/page' |
创建模板
模板负责渲染页面内容。在 blog 应用目录下创建模板目录:
blog/
└── templates/
└── blog/
├── base.html
├── post_list.html
├── post_detail.html
└── category_posts.html
基础模板
创建 blog/templates/blog/base.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}我的博客{% endblock %}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
header { background: #333; color: white; padding: 20px 0; }
header h1 { margin-bottom: 10px; }
nav a { color: white; margin-right: 15px; text-decoration: none; }
nav a:hover { text-decoration: underline; }
main { padding: 20px 0; }
.post-list { list-style: none; }
.post-item { margin-bottom: 30px; padding-bottom: 30px; border-bottom: 1px solid #eee; }
.post-title a { color: #333; text-decoration: none; }
.post-title a:hover { color: #007bff; }
.post-meta { color: #666; font-size: 14px; margin: 10px 0; }
.post-content { margin-top: 15px; }
footer { background: #333; color: white; text-align: center; padding: 20px; margin-top: 40px; }
</style>
</head>
<body>
<header>
<div class="container">
<h1>我的博客</h1>
<nav>
<a href="{% url 'blog:post_list' %}">首页</a>
</nav>
</div>
</header>
<main class="container">
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2024 我的博客. Powered by Django.</p>
</footer>
</body>
</html>
文章列表模板
创建 blog/templates/blog/post_list.html:
{% extends 'blog/base.html' %}
{% block title %}文章列表 - 我的博客{% endblock %}
{% block content %}
<h2>文章列表</h2>
{% if posts %}
<ul class="post-list">
{% for post in posts %}
<li class="post-item">
<h3 class="post-title">
<a href="{% url 'blog:post_detail' pk=post.pk %}">{{ post.title }}</a>
</h3>
<div class="post-meta">
作者:{{ post.author.username }} |
分类:<a href="{% url 'blog:category_posts' slug=post.category.slug %}">{{ post.category.name }}</a> |
发布时间:{{ post.publish|date:"Y年m月d日" }}
</div>
<div class="post-content">
{{ post.content|truncatewords:50 }}
</div>
</li>
{% endfor %}
</ul>
{% else %}
<p>暂无文章。</p>
{% endif %}
{% if categories %}
<div class="categories">
<h3>分类</h3>
<ul>
{% for category in categories %}
<li>
<a href="{% url 'blog:category_posts' slug=category.slug %}">
{{ category.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
文章详情模板
创建 blog/templates/blog/post_detail.html:
{% extends 'blog/base.html' %}
{% block title %}{{ post.title }} - 我的博客{% endblock %}
{% block content %}
<article class="post">
<h2>{{ post.title }}</h2>
<div class="post-meta">
作者:{{ post.author.username }} |
分类:{{ post.category.name }} |
发布时间:{{ post.publish|date:"Y年m月d日 H:i" }}
</div>
<div class="post-content">
{{ post.content|linebreaks }}
</div>
</article>
<p style="margin-top: 20px;">
<a href="{% url 'blog:post_list' %}">返回文章列表</a>
</p>
{% endblock %}
分类文章模板
创建 blog/templates/blog/category_posts.html:
{% extends 'blog/base.html' %}
{% block title %}{{ category.name }} - 我的博客{% endblock %}
{% block content %}
<h2>分类:{{ category.name }}</h2>
{% if posts %}
<ul class="post-list">
{% for post in posts %}
<li class="post-item">
<h3 class="post-title">
<a href="{% url 'blog:post_detail' pk=post.pk %}">{{ post.title }}</a>
</h3>
<div class="post-meta">
发布时间:{{ post.publish|date:"Y年m月d日" }}
</div>
</li>
{% endfor %}
</ul>
{% else %}
<p>该分类下暂无文章。</p>
{% endif %}
<p style="margin-top: 20px;">
<a href="{% url 'blog:post_list' %}">返回文章列表</a>
</p>
{% endblock %}
运行项目
# 启动开发服务器
python manage.py runserver
访问以下 URL 测试项目:
- 文章列表:
http://127.0.0.1:8000/blog/ - 管理后台:
http://127.0.0.1:8000/admin/
在管理后台添加几篇文章和分类后,你就能在前台看到内容了。
项目总结
通过这个项目,你学习了:
- 项目和应用:Django 项目由多个应用组成,每个应用负责特定功能
- 模型:使用 Python 类定义数据结构,Django 自动生成数据库表
- 迁移:使用迁移系统管理数据库结构变化
- 管理后台:Django 内置的管理界面可以快速管理数据
- 视图:处理请求并返回响应的函数
- URL 配置:将 URL 映射到视图
- 模板:使用模板语法渲染 HTML 页面
下一步
接下来你可以学习: