Skip to main content
 首页 » 编程设计

python之Django 抽象与非抽象模型继承

2024年08月21日33cloudgamer

考虑这两个模型类 Book 和 Article:

class Book(models.Model): 
    title = models.CharField(max_length=100) 
 
 
class Article(models.Model): 
    title = models.CharField(max_length=100) 

想象一下这个场景:一本书可以有很多注释,但一个注释只能引用一本书。文章也一样。我开发了两种解决方案:

非抽象解决方案

class Annotation(models.Model): 
    body = models.TextField() 
    article = models.ForeignKey(Article, blank=True) 
    book = models.ForeignKey(Book, blank=True) 
 
    class Meta: 
        default_related_name = 'annotations' 

优点

  • 代码是干的
  • 可以使用 book.annotations 或 article.annotations 访问注释

缺点

  • 浪费数据库内存(article、book两个字段之一必须为none)

抽象类解决方案

class Annotation(models.Model): 
    body = models.TextField() 
 
    class Meta: 
        abstract = True 
 
 
class BookAnnotation(Annotation): 
    book = models.ForeignKey(Book, blank=True) 
 
    class Meta: 
        default_related_name = 'book_annotations' 
 
 
class ArticleAnnotation(Annotation): 
    article = models.ForeignKey(Article, blank=True) 
 
    class Meta: 
        default_related_name = 'article_annotations' 

优点

  • 清晰区分书籍和文章注释
  • 没有内存浪费

缺点

  • 不太符合注解的概念(对象类型应该相同)
  • 可以使用 book.book_annotations 或 article.article_annotations 访问注释(有点丑不是吗?)。我不能使用 annotations 作为相关名称,因为 Django 禁止这样做,它说 reverse query name for BookAnnotation clashes with field name ArticleAnnotation。

处理这种情况的最佳方法是什么?

请您参考如下方法:

您可能会考虑 generic relations在这里。

class Annotation(models.Model): 
    body = models.TextField() 
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 
    object_id = models.PositiveIntegerField() 
    annotated_object = GenericForeignKey('content_type', 'object_id') 
 
class Book(models.Model): 
    ... 
    annotations = GenericRelation('Annotation') 
 
class Article(models.Model): 
    ... 
    annotations = GenericRelation('Annotation') 

现在您可以将 annotation.annotated_object 设置为任何书籍或文章实例,并反过来从书籍和文章对象中执行 obj.annotations。