Skip to content

第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 article

Meta类选项

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选项详解

  1. db_table: 指定数据库表名
  2. ordering: 默认排序规则
  3. verbose_name: 单数名称(管理后台显示)
  4. verbose_name_plural: 复数名称
  5. permissions: 额外的权限
  6. constraints: 数据库约束
  7. indexes: 数据库索引
  8. abstract: 是否为抽象基类
  9. 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')
        ]

最佳实践

  1. 保持方法简洁: 每个方法应该只负责一个明确的功能
  2. 使用属性装饰器: 对于计算属性,使用 @property 而不是方法
  3. 合理使用Meta: 使用Meta类来组织模型级别的配置
  4. 考虑性能: 避免在方法中执行昂贵的数据库查询
  5. 使用缓存: 对于计算成本高的属性,可以考虑使用缓存
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的强大功能,它们允许我们:

  1. ✅ 为模型实例添加业务逻辑
  2. ✅ 创建计算属性
  3. ✅ 定义模型级别的配置
  4. ✅ 提高代码的可读性和可维护性

通过合理使用这些特性,我们可以创建更加健壮和灵活的Django应用程序。

下一篇

我们将学习数据库操作,包括数据库配置、迁移系统和QuerySet的使用。

5.1 数据库操作基础 →

目录

返回课程目录

Released under the Apache 2.0 License.