第4章:Django模型(Models)
4.3 Django 模型方法和属性
在Django模型中,除了定义字段之外,我们还可以为模型添加方法和属性来增强其功能。这些方法和属性可以帮助我们实现业务逻辑、数据验证、计算属性等功能。
__str__方法
__str__为模型实例提供可读的字符串表示。
python
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title作用:
- 在Django管理后台显示有意义的对象名称
- 在Shell中调试时提供可读的信息
- 在模板中显示对象时使用
自定义方法
我们可以为模型添加自定义方法来封装业务逻辑,使代码更加清晰和可维护。
基本自定义方法
python
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def is_recent(self):
"""检查文章是否是最近发布的(24小时内)"""
from django.utils import timezone
return (timezone.now() - self.created_at).total_seconds() < 86400
def publish(self):
"""发布文章"""
self.published = True
self.save()
def unpublish(self):
"""取消发布文章"""
self.published = False
self.save()
def get_excerpt(self, length=100):
"""获取文章摘要"""
if len(self.content) <= length:
return self.content
return self.content[:length] + '...'使用示例
python
# 创建文章实例
article = Article.objects.create(
title="Django模型方法详解",
content="这是一篇关于Django模型方法的详细教程..."
)
# 调用自定义方法
print(article.is_recent()) # 检查是否最近发布
print(article.get_excerpt(50)) # 获取50字符的摘要
article.publish() # 发布文章属性装饰器
使用 @property 装饰器可以创建只读的计算属性,这些属性不会存储在数据库中,而是在访问时动态计算。
基本属性
python
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
word_count = models.PositiveIntegerField(default=0)
@property
def reading_time(self):
"""估算阅读时间(假设每分钟阅读200字)"""
if self.word_count == 0:
return "未知"
minutes = self.word_count // 200
if minutes == 0:
return "少于1分钟"
return f"{minutes}分钟"
@property
def is_popular(self):
"""根据某些条件判断是否受欢迎"""
# 这里可以添加更复杂的逻辑
return self.word_count > 1000带setter的属性
python
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
@property
def formatted_title(self):
"""获取格式化后的标题"""
return self.title.upper()
@formatted_title.setter
def formatted_title(self, value):
"""设置标题(自动转换为小写)"""
self.title = value.lower()使用示例
python
article = Article(title="Hello World", content="这是一些内容", word_count=450)
print(article.reading_time) # 输出: 2分钟
print(article.is_popular) # 输出: False
article.formatted_title = "NEW ARTICLE"
print(article.title) # 输出: new articleMeta类选项
Meta类用于定义模型的元数据,这些选项不会创建数据库字段,但会影响模型的行为。
常用Meta选项
python
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# 数据库表名
db_table = 'blog_articles'
# 排序规则
ordering = ['-created_at']
# 单数形式和复数形式的名称
verbose_name = '文章'
verbose_name_plural = '文章列表'
# 权限设置
permissions = [
('can_publish', '可以发布文章'),
('can_edit_all', '可以编辑所有文章'),
]
# 唯一约束
constraints = [
models.UniqueConstraint(
fields=['title', 'created_at'],
name='unique_title_per_day'
)
]
# 索引
indexes = [
models.Index(fields=['title']),
models.Index(fields=['created_at', 'published']),
]Meta选项详解
- db_table: 指定数据库表名
- ordering: 默认排序规则
- verbose_name: 单数名称(管理后台显示)
- verbose_name_plural: 复数名称
- permissions: 额外的权限
- constraints: 数据库约束
- indexes: 数据库索引
- abstract: 是否为抽象基类
- proxy: 是否为代理模型
完整示例
python
from django.db import models
from django.utils import timezone
from django.urls import reverse
class BlogPost(models.Model):
title = models.CharField(max_length=200, verbose_name="标题")
slug = models.SlugField(unique=True, verbose_name="URL标识")
content = models.TextField(verbose_name="内容")
author = models.ForeignKey('auth.User', on_delete=models.CASCADE, verbose_name="作者")
published = models.BooleanField(default=False, verbose_name="已发布")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
def __str__(self):
return self.title
@property
def reading_time(self):
"""计算阅读时间"""
word_count = len(self.content.split())
minutes = max(1, word_count // 200) # 至少1分钟
return f"{minutes}分钟"
@property
def is_recent(self):
"""是否是最近更新的(7天内)"""
return (timezone.now() - self.updated_at).days < 7
def get_absolute_url(self):
"""获取文章的绝对URL"""
return reverse('blog:post_detail', kwargs={'slug': self.slug})
def publish(self):
"""发布文章"""
self.published = True
self.save()
def unpublish(self):
"""取消发布"""
self.published = False
self.save()
class Meta:
verbose_name = "博客文章"
verbose_name_plural = "博客文章"
ordering = ['-created_at']
indexes = [
models.Index(fields=['slug']),
models.Index(fields=['published', 'created_at']),
]
constraints = [
models.UniqueConstraint(fields=['slug'], name='unique_slug')
]最佳实践
- 保持方法简洁: 每个方法应该只负责一个明确的功能
- 使用属性装饰器: 对于计算属性,使用
@property而不是方法 - 合理使用Meta: 使用Meta类来组织模型级别的配置
- 考虑性能: 避免在方法中执行昂贵的数据库查询
- 使用缓存: 对于计算成本高的属性,可以考虑使用缓存
python
from django.core.cache import cache
class Article(models.Model):
# ... 字段定义 ...
@property
def popular_tags(self):
"""获取热门标签(带缓存)"""
cache_key = f'article_{self.id}_popular_tags'
tags = cache.get(cache_key)
if tags is None:
# 昂贵的计算或查询
tags = self.tags.all()[:5]
cache.set(cache_key, tags, timeout=300) # 缓存5分钟
return tags小结
模型方法和属性是Django ORM的强大功能,它们允许我们:
- ✅ 为模型实例添加业务逻辑
- ✅ 创建计算属性
- ✅ 定义模型级别的配置
- ✅ 提高代码的可读性和可维护性
通过合理使用这些特性,我们可以创建更加健壮和灵活的Django应用程序。
下一篇
我们将学习数据库操作,包括数据库配置、迁移系统和QuerySet的使用。