Django框架学习第2天:Django模型
一、课程概述
二、模型定义
2.1 基本模型结构
# blog/models.py
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, verbose_name='分类名称')
description = models.TextField(blank=True, verbose_name='分类描述')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
verbose_name = '分类'
verbose_name_plural = verbose_name
ordering = ['-created_time']
def __str__(self):
return self.name
class Post(models.Model):
STATUS_CHOICES = (
('draft', '草稿'),
('published', '发布'),
)
title = models.CharField(max_length=200, verbose_name='标题')
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
body = models.TextField(verbose_name='正文')
publish = models.DateTimeField(default=timezone.now, verbose_name='发布时间')
created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='分类')
tags = models.ManyToManyField('Tag', blank=True, verbose_name='标签')
class Meta:
ordering = ('-publish',)
verbose_name = '文章'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=100, verbose_name='标签名称')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
verbose_name = '标签'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
2.2 常用字段类型
2.3 字段选项
# 字段选项示例
class Product(models.Model):
name = models.CharField(
max_length=100, # 最大长度
verbose_name='商品名称', # 人性化名称
unique=True, # 唯一值
db_index=True, # 数据库索引
help_text='请输入商品名称' # 帮助文本
)
price = models.DecimalField(
max_digits=10, # 最大位数
decimal_places=2, # 小数位数
null=True, # 允许为空
blank=True # 表单可以为空
)
三、ORM操作
3.1 基本的CRUD操作
# 创建对象
# 方法1:
post = Post(title='第一篇博客', body='内容...', author=user)
post.save()
# 方法2:
Post.objects.create(title='第二篇博客', body='内容...', author=user)
# 查询对象
# 获取所有文章
posts = Post.objects.all()
# 获取单个对象
post = Post.objects.get(id=1)
# 过滤对象
draft_posts = Post.objects.filter(status='draft')
recent_posts = Post.objects.filter(publish__year=2024)
# 更新对象
# 方法1:
post = Post.objects.get(id=1)
post.title = '新标题'
post.save()
# 方法2:
Post.objects.filter(id=1).update(title='新标题')
# 删除对象
post = Post.objects.get(id=1)
post.delete()
3.2 高级查询
# 查询API示例
from django.db.models import Q, Count, Avg
from django.utils import timezone
# Q对象 - 复杂查询
# 查找标题包含"Django"或内容包含"Python"的文章
Post.objects.filter(Q(title__contains='Django') | Q(body__contains='Python'))
# 查找不是草稿且作者是指定用户的文章
Post.objects.filter(~Q(status='draft'), author=user)
# 聚合查询
# 统计每个分类下的文章数量
categories = Category.objects.annotate(post_count=Count('post'))
# 查找文章数量大于5的分类
popular_categories = Category.objects.annotate(
post_count=Count('post')
).filter(post_count__gt=5)
# 关联查询
# 获取指定作者的所有文章
user.blog_posts.all()
# 获取包含指定标签的所有文章
tag = Tag.objects.get(name='Django')
tag.post_set.all()
3.3 查询优化
# select_related() - 处理外键和一对一关系
# 一次性获取文章及其作者和分类信息
posts = Post.objects.select_related('author', 'category').all()
# prefetch_related() - 处理多对多关系
# 一次性获取文章及其标签信息
posts = Post.objects.prefetch_related('tags').all()
# 组合使用
posts = Post.objects.select_related('author', 'category').prefetch_related('tags').all()
四、数据库迁移
4.1 迁移命令
# 生成迁移文件
python manage.py makemigrations
# 执行迁移
python manage.py migrate
# 查看迁移文件的SQL语句
python manage.py sqlmigrate blog 0001
# 检查迁移文件
python manage.py check
4.2 数据迁移示例
# migrations/0002_auto_20240112_1234.py
from django.db import migrations, models
def populate_slug(apps, schema_editor):
Post = apps.get_model('blog', 'Post')
for post in Post.objects.all():
post.slug = f"{post.title.lower().replace(' ', '-')}-{post.id}"
post.save()
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='post',
name='slug',
field=models.SlugField(default='', max_length=250),
preserve_default=False,
),
migrations.RunPython(populate_slug),
]
五、实战练习:博客评论系统
# blog/models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80, verbose_name='评论者')
email = models.EmailField(verbose_name='邮箱')
body = models.TextField(verbose_name='评论内容')
created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
active = models.BooleanField(default=True, verbose_name='是否显示')
class Meta:
ordering = ('created',)
verbose_name = '评论'
verbose_name_plural = verbose_name
def __str__(self):
return f'Comment by {self.name} on {self.post}'
5.1 评论视图
# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post, Comment
from .forms import CommentForm
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post,
slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
# 获取该文章的所有活动评论
comments = post.comments.filter(active=True)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# 创建评论对象但不保存到数据库
new_comment = comment_form.save(commit=False)
# 指定评论的文章
new_comment.post = post
# 保存评论到数据库
new_comment.save()
else:
comment_form = CommentForm()
return render(request,
'blog/post/detail.html',
{'post': post,
'comments': comments,
'comment_form': comment_form})
六、进阶技巧
6.1 自定义模型管理器
# blog/models.py
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class Post(models.Model):
# ... 其他字段 ...
objects = models.Manager() # 默认管理器
published = PublishedManager() # 自定义管理器
# 使用自定义管理器
published_posts = Post.published.all()
6.2 模型信号
# blog/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Post
@receiver(post_save, sender=Post)
def create_post_slug(sender, instance, created, **kwargs):
if created:
instance.slug = f"{instance.title.lower().replace(' ', '-')}-{instance.id}"
instance.save()
七、常见问题和解决方案
- 数据库迁移冲突:
# 重置迁移
python manage.py migrate blog zero
python manage.py makemigrations blog
python manage.py migrate blog
- 关联字段删除:
# 使用on_delete选项
author = models.ForeignKey(
User,
on_delete=models.SET_NULL, # 设置为NULL
null=True,
blank=True
)
- 大数据查询优化:
# 分批处理
from django.core.paginator import Paginator
paginator = Paginator(Post.objects.all(), 100)
for page in paginator.page_range:
for post in paginator.page(page).object_list:
# 处理每篇文章
pass
八、作业和练习
- 创建一个完整的博客数据模型,包含用户、分类、标签、文章和评论
- 实现文章的增删改查功能
- 添加评论功能
- 实现文章分类和标签功能
- 优化查询性能
九、扩展阅读
- Django Model字段类型详解
- 数据库事务处理
- Django ORM高级查询技巧
- 数据库性能优化方案
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!