创建一个支持多种pos类型的博客

2024-09-29 02:18:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我是Django的一个新用户,我正在尝试如何创建一个可以支持多种类型元素的模型。在

这是一个情节:我想在我的应用程序上创建一个博客模块。 为此,我创建了一个模型页面,它描述了一个博客页面。以及一个模型PageElement,它描述了博客上的一篇文章。每个页面可以包含许多页面元素。在

一个PageElement可以有很多类型,因为我希望我的用户可以像发布短文本、视频或图片一样发布。我也希望(例如)用户可以发布对另一个模型的引用(比如对用户的引用)。根据用户发布的内容类型,HTML页面将以不同的方式显示每个PageElement。在

但是我不知道声明PageElement类以支持所有这些情况的正确方法是:(

这是我的页面模型:

class Page(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)

    # Basical informations
    title = models.CharField(max_length=150)
    description = models.TextField(blank=True)

    # Foreign links
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='pages_as_user'
    )

    created_at = models.DateTimeField(default=timezone.now)

    # Other fields ....

    class Meta:
        indexes = [
            models.Index(fields=['uuid']),
            models.Index(fields=['user', 'artist'])
        ]

现在,我有两个解决方案,第一个使用继承:当您在博客上创建一个新帖子时,您创建一个从PageElement模型继承的元素。以下是我针对每种情况的不同模型:

^{pr2}$

如果必须使用“纯”Python,那么这个解决方案就是我选择的解决方案。因为我可以将每个PageElement存储在字典中并按类过滤它们。这种解决方案可以很容易地扩展到未来的新类型的内容。在

但对于Django车型。看来这不是最好的解决办法。因为从数据库中获取所有PageElement子级非常困难(我不能只写“page.elements页“要获取所有类型的所有元素,我需要手动获取所有%(class)s_elements元素并将它们连接起来:/)。我已经考虑过下面这样的解决方案(我还没有尝试过),但是对于这个问题(对于需要处理大量请求的数据库来说)似乎太过了:

class Page(models.Model):
    # ...
    def get_elements(self):
        # Retrieve all PageElements children linked to the current Page
        R = []
        fields = self._meta.get_fields(include_hidden=True)
        for f in fields:
            try:
                if '_elements' in f.name:
                    R += getattr(self, f.name)
            except TypeError as e:
                continue

        return R

我的第二个“解决方案”使用一个唯一的类,它包含我需要的所有字段。根据我想要创建的PageElement的类型,我会将类型字段设置为正确的值,将值放入相应的字段中,并将所有其他未使用的字段放入NULL

class PageElement(models.Model):
    page = models.OneToOneField(
        Page,
        on_delete=models.CASCADE,
        related_name='elements'
    )

    updated_at = models.DateTimeField(default=timezone.now)
    created_at = models.DateTimeField(default=timezone.now)

    TYPES_CHOICE = (
        ('img', 'Image'),
        ('vid', 'Video'),
        ('txt', 'Text'),
        ('usr', 'User'),
    )
    type = models.CharField(max_length=60, choices=TYPES_CHOICE)

    # For type Image
    image = models.ImageField(null=True)
    image_url = models.URLField(null=True)

    # For type Video
    video = models.FileField(null=True)
    video_url = models.URLField(null=True)

    # For type Text
    text = models.TextField(null=True)

    # For type User
    user = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE,
        related_name='elements',
        null=True
    )

使用此解决方案,我可以在单个请求中检索所有元素page.elements页". 但是它的可扩展性不如前一个(我需要修改整个表结构来添加一个新字段或一种新的元素)。在

老实说,我完全不知道哪种解决方案是最好的。我确信还有其他(更好的)解决方案,但我糟糕的面向对象技能并不能让我思考这些问题(:()。。。在

我想要一个将来可以很容易修改的解决方案(例如,如果我想在Blog上添加一个新类型的“calendar”,它引用日期时间)。如果我想检索与页面相关的所有元素,那么在我的应用程序中很容易使用它。。。在

感谢您的关注:)


Tags: 用户name模型true元素类型fieldsmodels
3条回答

我不确定它是否适合您的问题,但在这种情况下使用GenericForeignKeys/ContentType framework可能是合适的。当一个人掌握了这个概念,它是相当强大的。在

构造示例:

class Page(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    page_element = GenericForeignKey('content_type', 'object_id')
    ...

现在可以通过GenericFK将任何模型对象连接到页面模型。因此,在以后的阶段添加一个新类型(作为一个新的模型),并不具有侵入性。在

更新:

正如一条评论所指出的那样,这个构造并不能很好地支持页面中的许多pageElement。在

详细说明一下,解决这个问题的一个方法,仍然是利用通用的。。。在

^{pr2}$

一个页面可以有许多“抽象”的PageElements,而content_对象是“具体的PageElement模型/实现”。 易于检索特定页面的所有元素,并允许检查ContentType来检查元素的类型等

只是解决这个特殊问题的许多方法中的一种。在

为了在Django中建立Page和{}之间的关系,您宁愿使用外键关系,而不是继承。在

class PageImageElement(PageElement):
    page = models.ForeignKey(Page,
                             on_delete=models.CASCADE,
                             related_name='images')
    image = models.ImageField(null=True)
    image_url = models.URLField(null=True)

每个用户的帖子都会创建一个Page的实例。每次向页面添加图像都会创建一个PageImageElement的实例,您可以使用相关名称查询它们。这种方式可以非常容易地访问单个Page的所有视频、图像、文本模块。在

关于一个相关的注释,我想说PageElement类可以是抽象的see the docs,如果您像在video = models.FileField(null=True)中那样声明可能包含null值的字段,那么它可能也值得声明{},否则在创建未定义这些字段的对象时会出现错误。例如,在这里讨论:differentiate null=True, blank=True in django

I can't just write "page.elements" to get all elements of all types

实际上,如果你使用multi-table inheritance,就可以了。问题是返回的所有记录都是PageElement的实例,这意味着您将丢失子类类型的所有信息以及这些子对象可能保存的其他数据。
有很多软件包可以解决这种多态性问题: django packages: Model inheritance

相关问题 更多 >