Django代理模型如何添加额外的模型字段?

11

我正在编写一个类似于报纸的 Django 应用程序。我有文章,然后我有出现在某些上下文中的那些文章的定制版本。因此,我可以有一篇文章的版本,它出现在报纸的首页上,并且有一个原始标题的更短版本。因此,我有:

class Article(models.Model):
    """ A newspaper article with lots of fields """
    title = models.CharField(max_length=255)
    content = models.CharField(max_length=255)

    # Lots of fields...

我希望有一个CustomArticlè对象作为Articlè的代理,但具有可选的替代标题:

class CustomArticle(Article):
    """ An alternate version of a article """
    alternate_title = models.CharField(max_length=255)

    @property
    def title(self):
        """ use the alternate title if there is one """
        if self.custom_title:
            return self.alternate_title
        else:
            return self.title
            
    class Meta:
        proxy = True
    
    # Other fields and methods

很遗憾,我无法向代理添加新字段:

>>> TypeError: Abstract base class containing model fields not permitted for proxy model 'CustomArticle'

所以,我可以像这样做:

class CustomArticle(models.Model):
    # Other methods...
    
    original = models.ForeignKey('Article')

    def __getattr__(self, name):
        if hasattr(self.original):
            return getattr(self.original, name)
        else:
            return super(self, CustomArticle).__getattr__(name)

但不幸的是,__getattr__ 在Django模型中似乎无法使用。由于Article类中的字段可能会发生更改,因此为CustomArticle创建每个字段的@property方法并不可行。那么正确的做法是什么?


我想要澄清一下我试图实现的工作流程:一个作者创建了一篇文章。随后,编辑可以从那篇已存在的文章中创建一个或多个自定义文章,以便在不同的上下文中使用(例如,首页标题的版本,其他地方的引用版本等)。 - Joshmaker
3个回答

3

看起来这个__getattr__可能会有用:

def __getattr__(self, key):

    if key not in ('original', '_ original_cache'):
        return getattr(self.original, key)      
    raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, key))

对于条件查找,这可以避免递归。 - Nour Wolf

1
尝试像这样做:

class CustomArticle(models.Model):
    # Other methods...

    original = models.ForeignKey('Article')

    def __getattr__(self, name):
        return getattr(self.original, name)

那似乎在90%的情况下都能正常工作,但仍有一些情况会导致它进入无限循环并崩溃(特别是在Django管理界面中)。 - Joshmaker

1

我已经研究过这个问题,但我不确定如何将CustomArticle连接到特定的Article,以便它的ptr_id始终指向数据库中现有的Article。 - Joshmaker
Django将从子模型到父模型创建一个OneToOneField(一种ForeignKey)。这意味着您将在该字段上获得通常的引用完整性约束。如果您使用django<1.3,每当删除Article实例时,都会删除引用CustomArticle实例。从django 1.3开始,您应该能够指定删除Article实例时所需的行为。 - ftartaggia
如果需要这样的功能,您还应该验证一下,Django是否允许您从已存在的Article实例创建CustomArticle(在问题领域的术语中,这相当于将一个Article转换为CustomArticle的可能性)。 - ftartaggia
工作流程如下:作者创建文章。稍后,编辑可以从现有文章中创建一个或多个自定义文章,以在不同的上下文中使用。我的经验是,对于父/子关系,Django 不允许我从现有文章创建自定义文章。 - Joshmaker
1
“one or more” 的意思是您不能使 CustomArticle 使用表继承。您需要通过多对一的关系手动处理此问题。 - ftartaggia
关于覆盖Django模型的__getattr__方法,我需要深入研究一下这个主题 :-( - ftartaggia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接