主题
Django框架入门和ORM操作
1. Django框架介绍
Django是一个高级Python Web框架,遵循"电池包含"的设计理念,提供了完整的Web开发解决方案。它被广泛应用于构建大型、复杂的Web应用。
1.1 Django的特点
- 全功能:内置ORM、表单处理、认证系统等
- 安全性:内置防止SQL注入、XSS、CSRF等攻击的功能
- 可扩展性:支持插件和第三方库
- MTV架构:Model-Template-View架构模式
- 管理后台:自动生成的管理界面
- 国际化:支持多语言和时区
1.2 Django与其他框架的比较
| 框架 | 特点 | 适用场景 |
|---|---|---|
| Django | 全功能、电池包含 | 大型应用、企业级项目 |
| Flask | 轻量级、灵活 | 小型应用、API开发 |
| FastAPI | 类型提示、自动文档 | API开发、高性能需求 |
| Pyramid | 灵活、可扩展 | 中型应用、自定义需求 |
2. Django环境搭建
2.1 安装Python
Django需要Python 3.8或更高版本。首先确保你的系统已经安装了Python。
2.2 安装Django
使用pip安装Django:
bash
# 创建虚拟环境
python3 -m venv venv
# 激活虚拟环境
# Windows
venv\Scripts\activate
# Linux/macOS
source venv/bin/activate
# 安装Django
pip install Django
# 验证安装
python -m django --version3. 创建Django项目
3.1 初始化项目
使用Django的命令行工具创建项目:
bash
# 创建项目
django-admin startproject mysite
# 进入项目目录
cd mysite
# 创建应用
python manage.py startapp blog3.2 项目结构
创建完成后,项目结构如下:
mysite/
├── mysite/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blog/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py3.3 配置项目
编辑mysite/settings.py文件,配置项目:
python
# 注册应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # 注册blog应用
]
# 数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 时区配置
TIME_ZONE = 'Asia/Shanghai'
# 语言配置
LANGUAGE_CODE = 'zh-Hans'
# 静态文件配置
STATIC_URL = 'static/'4. Django ORM基础
4.1 ORM概念
ORM(Object-Relational Mapping)是一种将数据库表结构映射到对象的技术,它允许开发者使用面向对象的方式操作数据库。
4.2 定义模型
在blog/models.py中定义数据库模型:
python
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, blank=True, null=True)
tags = models.ManyToManyField('Tag', blank=True)
def publish(self):
self.published_at = timezone.now()
self.save()
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name4.3 模型字段类型
Django提供了多种字段类型:
| 字段类型 | 描述 | 示例 |
|---|---|---|
| CharField | 字符串 | name = models.CharField(max_length=100) |
| TextField | 长文本 | content = models.TextField() |
| IntegerField | 整数 | age = models.IntegerField() |
| FloatField | 浮点数 | price = models.FloatField() |
| DecimalField | 十进制数 | amount = models.DecimalField(max_digits=10, decimal_places=2) |
| BooleanField | 布尔值 | active = models.BooleanField(default=False) |
| DateField | 日期 | birth_date = models.DateField() |
| DateTimeField | 日期时间 | created_at = models.DateTimeField(auto_now_add=True) |
| ForeignKey | 外键关系 | author = models.ForeignKey(User, on_delete=models.CASCADE) |
| ManyToManyField | 多对多关系 | tags = models.ManyToManyField('Tag') |
| OneToOneField | 一对一关系 | profile = models.OneToOneField('Profile', on_delete=models.CASCADE) |
4.4 数据库迁移
执行数据库迁移命令:
bash
# 生成迁移文件
python manage.py makemigrations
# 执行迁移
python manage.py migrate
# 查看迁移状态
python manage.py showmigrations5. ORM查询操作
5.1 基本查询
python
# 获取所有对象
posts = Post.objects.all()
# 获取单个对象(不存在会抛出异常)
post = Post.objects.get(id=1)
# 获取单个对象(不存在返回None)
post = Post.objects.filter(id=1).first()
# 过滤查询
published_posts = Post.objects.filter(published_at__isnull=False)
recent_posts = Post.objects.filter(created_at__gte=timezone.now() - timezone.timedelta(days=7))
# 排序
posts = Post.objects.order_by('-created_at') # 降序
posts = Post.objects.order_by('title') # 升序
# 限制数量
recent_posts = Post.objects.order_by('-created_at')[:5]
# 排除查询
unpublished_posts = Post.objects.exclude(published_at__isnull=False)
# 搜索
posts = Post.objects.filter(title__contains='Django')
posts = Post.objects.filter(title__icontains='django') # 不区分大小写5.2 高级查询
python
# Q对象(复杂条件)
from django.db.models import Q
posts = Post.objects.filter(Q(title__contains='Django') | Q(content__contains='Django'))
posts = Post.objects.filter(Q(published_at__isnull=False) & Q(author__username='admin'))
# F对象(比较同一模型的不同字段)
from django.db.models import F
posts = Post.objects.filter(updated_at__gt=F('created_at'))
# 聚合查询
from django.db.models import Count, Sum, Avg, Max, Min
post_count = Post.objects.count()
avg_price = Product.objects.aggregate(Avg('price'))
max_price = Product.objects.aggregate(Max('price'))
# 分组查询
from django.db.models import Count
category_post_counts = Post.objects.values('category__name').annotate(post_count=Count('id'))
# 关联查询
# 正向查询
post = Post.objects.get(id=1)
author_name = post.author.username
# 反向查询
user = User.objects.get(username='admin')
user_posts = user.post_set.all() # 默认使用模型名小写_set
# 预加载(避免N+1查询)
# 预加载外键
posts = Post.objects.select_related('author', 'category').all()
# 预加载多对多
posts = Post.objects.prefetch_related('tags').all()
# 组合使用
posts = Post.objects.select_related('author', 'category').prefetch_related('tags').all()5.3 更新和删除
python
# 更新单个对象
post = Post.objects.get(id=1)
post.title = '新标题'
post.save()
# 批量更新
Post.objects.filter(published_at__isnull=True).update(published_at=timezone.now())
# 删除单个对象
post = Post.objects.get(id=1)
post.delete()
# 批量删除
Post.objects.filter(published_at__lt=timezone.now() - timezone.timedelta(days=365)).delete()6. Django视图和URL
6.1 视图函数
在blog/views.py中定义视图:
python
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from .models import Post, Category, Tag
from .forms import PostForm
def post_list(request):
posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
@login_required
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
@login_required
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
@login_required
def post_draft_list(request):
posts = Post.objects.filter(published_at__isnull=True).order_by('-created_at')
return render(request, 'blog/post_draft_list.html', {'posts': posts})
@login_required
def post_publish(request, pk):
post = get_object_or_404(Post, pk=pk)
post.publish()
return redirect('post_detail', pk=pk)
@login_required
def post_remove(request, pk):
post = get_object_or_404(Post, pk=pk)
post.delete()
return redirect('post_list')6.2 URL配置
在blog/urls.py中配置URL:
python
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'),
path('post/<int:pk>/edit/', views.post_edit, name='post_edit'),
path('drafts/', views.post_draft_list, name='post_draft_list'),
path('post/<int:pk>/publish/', views.post_publish, name='post_publish'),
path('post/<int:pk>/remove/', views.post_remove, name='post_remove'),
]在mysite/urls.py中包含应用URL:
python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]7. Django表单
7.1 模型表单
在blog/forms.py中定义表单:
python
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'category', 'tags']
widgets = {
'content': forms.Textarea(attrs={'rows': 10}),
}7.2 自定义表单
python
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, label='姓名')
email = forms.EmailField(label='邮箱')
message = forms.CharField(widget=forms.Textarea, label='留言')
def clean_name(self):
name = self.cleaned_data.get('name')
if len(name) < 2:
raise forms.ValidationError('姓名至少需要2个字符')
return name7.3 表单验证
python
# 视图中处理表单
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# 处理表单数据
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# 发送邮件或保存数据
return redirect('success')
else:
form = ContactForm()8. Django管理后台
8.1 注册模型
在blog/admin.py中注册模型:
python
from django.contrib import admin
from .models import Post, Category, Tag
admin.site.register(Category)
admin.site.register(Tag)
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'category', 'published_at', 'created_at')
list_filter = ('category', 'author', 'published_at')
search_fields = ('title', 'content')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'published_at'
ordering = ('-published_at', '-created_at')8.2 创建超级用户
bash
python manage.py createsuperuser8.3 访问管理后台
启动开发服务器:
bash
python manage.py runserver访问 http://localhost:8000/admin/ 登录管理后台。
9. Django中间件
9.1 中间件概念
中间件是Django处理请求和响应的钩子,它可以在请求到达视图之前或响应返回客户端之前执行一些操作。
9.2 内置中间件
Django默认包含以下中间件:
python
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]9.3 自定义中间件
创建自定义中间件:
python
# blog/middleware.py
class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求处理前
print('请求到达')
response = self.get_response(request)
# 响应处理后
print('响应返回')
return response在settings.py中注册中间件:
python
MIDDLEWARE = [
# 其他中间件
'blog.middleware.CustomMiddleware',
]10. Django静态文件和媒体文件
10.1 静态文件配置
在settings.py中配置静态文件:
python
# 静态文件URL
STATIC_URL = 'static/'
# 静态文件目录
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# 生产环境静态文件根目录
STATIC_ROOT = BASE_DIR / 'staticfiles'10.2 媒体文件配置
在settings.py中配置媒体文件:
python
# 媒体文件URL
MEDIA_URL = 'media/'
# 媒体文件根目录
MEDIA_ROOT = BASE_DIR / 'media'在urls.py中添加媒体文件URL:
python
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# 其他URL
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)11. Django认证系统
11.1 内置认证视图
在urls.py中包含认证URL:
python
from django.contrib.auth import views as auth_views
urlpatterns = [
# 登录
path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
# 登出
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
# 密码重置
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]11.2 用户注册
创建用户注册视图:
python
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('post_list')
else:
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})11.3 权限控制
python
from django.contrib.auth.decorators import login_required, permission_required
# 要求登录
@login_required
def profile(request):
return render(request, 'profile.html')
# 要求特定权限
@permission_required('blog.change_post')
def post_edit(request, pk):
# 视图代码
pass
# 检查用户是否有权限
if request.user.has_perm('blog.change_post'):
# 用户有修改文章的权限
pass
if request.user.has_perms(['blog.add_post', 'blog.change_post']):
# 用户有添加和修改文章的权限
pass12. Django REST Framework
12.1 安装DRF
bash
pip install djangorestframework12.2 配置DRF
在settings.py中添加DRF:
python
INSTALLED_APPS = [
# 其他应用
'rest_framework',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
],
}12.3 创建API视图
python
from rest_framework import viewsets
from rest_framework import permissions
from .models import Post, Category
from .serializers import PostSerializer, CategorySerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated]
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [permissions.IsAuthenticated]12.4 序列化器
在blog/serializers.py中定义序列化器:
python
from rest_framework import serializers
from .models import Post, Category, Tag
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name']
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True)
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'category', 'tags', 'published_at', 'created_at']12.5 配置API路由
在blog/urls.py中配置API路由:
python
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
from .api_views import PostViewSet, CategoryViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'categories', CategoryViewSet)
urlpatterns = [
# 其他URL
path('api/', include(router.urls)),
]13. 部署Django应用
13.1 使用Gunicorn和Nginx
- 安装Gunicorn
bash
pip install gunicorn- 创建Gunicorn配置文件
python
# gunicorn.conf.py
bind = '0.0.0.0:8000'
workers = 4
worker_class = 'gevent'
timeout = 30- 启动Gunicorn
bash
gunicorn mysite.wsgi:application -c gunicorn.conf.py- 配置Nginx
nginx
server {
listen 80;
server_name example.com;
location /static/ {
alias /path/to/mysite/staticfiles/;
expires 30d;
}
location /media/ {
alias /path/to/mysite/media/;
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}13.2 使用Docker
创建Dockerfile:
dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "mysite.wsgi:application", "-b", "0.0.0.0:8000"]创建docker-compose.yml:
yaml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DEBUG=False
- SECRET_KEY=your-secret-key
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_DB=mysite
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:14. 最佳实践
14.1 项目结构
- 使用应用分离功能
- 合理组织代码结构
- 使用版本控制
14.2 安全性
- 使用HTTPS
- 防止SQL注入
- 防止XSS攻击
- 防止CSRF攻击
- 安全处理用户上传
- 使用强密码
- 定期更新依赖
14.3 性能优化
- 使用缓存
- 优化数据库查询
- 使用索引
- 启用Gzip压缩
- 优化静态文件
- 使用CDN
14.4 测试
- 编写单元测试
- 编写集成测试
- 使用测试覆盖率工具
- 自动化测试
14.5 部署
- 使用环境变量
- 配置不同环境的设置
- 备份数据
- 监控应用
- 日志管理
15. 实战项目:博客系统
15.1 项目结构
blog_project/
├── blog/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations/
│ ├── models.py
│ ├── serializers.py
│ ├── templates/
│ │ └── blog/
│ │ ├── base.html
│ │ ├── post_list.html
│ │ ├── post_detail.html
│ │ ├── post_edit.html
│ │ └── post_draft_list.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── blog_project/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── static/
│ ├── css/
│ └── js/
├── media/
├── requirements.txt
└── manage.py15.2 核心功能
- 文章发布和管理
- 分类和标签
- 评论功能
- 搜索功能
- 响应式设计
- API接口
15.3 技术栈
- 后端:Django 4.0+, Python 3.9+
- 数据库:PostgreSQL
- 前端:HTML, CSS, JavaScript, Bootstrap
- API:Django REST Framework
- 部署:Gunicorn, Nginx, Docker
16. 总结
Django是一个功能强大、设计优雅的Python Web框架,它提供了完整的Web开发解决方案,包括ORM、表单处理、认证系统、管理后台等。通过本文的学习,你已经掌握了:
- Django框架的基本概念和特点
- Django环境搭建和项目创建
- Django ORM的使用和数据库操作
- Django视图和URL配置
- Django表单和验证
- Django管理后台
- Django中间件
- Django静态文件和媒体文件
- Django认证系统
- Django REST Framework
- Django应用部署
- 最佳实践和实战项目
Django的学习曲线相对平缓,易于上手,同时又足够强大,可以满足各种Web开发需求。希望本文对你的Django学习之旅有所帮助!
17. 练习与思考
- 实现一个完整的博客系统,包括文章、评论、分类、标签等功能
- 添加用户认证和授权功能
- 实现RESTful API接口
- 集成第三方登录(如GitHub、Google)
- 添加搜索功能和分页
- 实现文件上传和图片处理
- 优化数据库查询和页面加载速度
- 编写完整的测试用例
通过这些练习,你将进一步巩固Django的使用技能,为实际项目开发打下坚实的基础。