如何将任何对象/模型与另一个Django模型无关地链接?

时间:2009-06-09 10:11:28

标签: python django django-models content-management-system

我正在编写一个基于Django的简单CMS。大多数内容管理系统依赖于使用具有一个或多个可编辑区域的模板在固定URL上具有固定页面。要拥有可编辑区域,您需要一个Page。要使系统计算出哪个页面,您需要URL。

问题出现在你不再处理“页面”(那些FlatPages页面或其他东西),而是来自另一个模型的实例。例如,如果我有一个产品型号,我可能希望创建一个在其中有多个可编辑区域的详细信息页面。

可以将这些区域构建到模型中,但在我的情况下,有几个模型,并且我想要显示多少数据的差异。

因此,我想在模板级别构建CMS,并根据“页面”实例或它使用的模型指定块(可编辑区域)的内容。

我有一个想法,也许我可以在页面上转储自定义模板标签,如下所示:

{% block unique_object "unique placeholder name" %}

然后根据传入的两个参数找到一个“块”。例如:

<h1>{{ product_instance.name }}</h1>
{% block product_instance "detail: product short description" %}
{% block product_instance "detail: product video" %}
{% block product_instance "detail: product long description" %}

听起来很漂亮,对吧?我遇到的问题是如何为区域创建一个“密钥”,以便我可以拉出正确的块?我将处理一个完全未知的对象(它可能是一个“页面”对象,一个URL,一个模型实例,任何东西 - 它甚至可能是一艘船</fg>)。

其他Django微应用程序必须这样做。你可以使用django-tagging标记任何东西,对吧?我试图了解它是如何工作的,但我正在画空白。

所以,首先,我疯了吗?假设我没有,这看起来像是一个相对理智的想法,我应该如何将对象+字符串链接到块/可编辑区域?

注意:编辑将在页面上完成,因此让用户编辑区域没有任何实际问题。我不需要在管理员中做任何反向误导。我最终的梦想是允许第三个参数指定这是什么类型的内容区域(文本,图像,视频等)。如果您对此有任何意见,我很高兴看到它们!

3 个答案:

答案 0 :(得分:6)

django-tagging使用Django的contenttypes框架。文档在解释它方面做得比我能做得好得多,但最简单的描述就是“可以指向任何其他模型的通用外键。”

这可能是您正在寻找的,但从您的描述中,您也可能想要做一些与其他现有项目非常相似的事情:

  • django-flatblocks (“...就像django.contrib.flatpages一样,但对于页面的某些部分;就像您希望在主要内容旁边显示的可编辑帮助框一样。 “)

  • django-better-chunks (“将其视为可插入模板并从管理界面进行管理的少量可重复使用内容的平面页。”)

等等。如果它们相似,那么它们将为您提供一个良好的起点。

答案 1 :(得分:2)

您想要一种在给定特定对象的通用模板上显示某些特定于对象的内容的方法吗?

为了支持模型和其他对象,我们需要两个中间模型;一个处理字符串,一个处理模型。我们可以用一个模型做到这一点,但这不太合适。这些模型将提供内容和字符串/模型之间的链接。

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

CONTENT_TYPE_CHOICES = (
    ("video", "Video"),
    ("text", "Text"),
    ("image", "Image"),
)

def _get_template(name, type):
    "Returns a list of templates to load given a name and a type"
    return ["%s_%s.html" % (type, name), "%s.html" % name, "%s.html" % type]

class ModelContentLink(models.Model):
    key = models.CharField(max_length=255) # Or whatever you find appropriate
    type = models.CharField(max_length=31, choices= CONTENT_TYPE_CHOICES)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

    def get_template(self):
        model_name = self.object.__class__.__name__.lower()
        return _get_template(model_name, self.type)

class StringContentLink(models.Model):
    key = models.CharField(max_length=255) # Or whatever length you find appropriate
    type = models.CharField(max_length=31, choices= CONTENT_TYPE_CHOICES)
    content = models.TextField()

    def get_template(self):
        return _get_template(self.content, self.type)

现在,我们只需要一个模板标记来获取它们,然后尝试加载模型的get_template()方法给出的模板。我有点紧迫,所以我会把它留在这里,并在约1小时内更新。如果您认为这种方法似乎很好,请告诉我。

答案 2 :(得分:2)

使用contenttypes框架实现您描述的查找策略非常简单:

class Block(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey() # not actually used here, but may be handy
    key = models.CharField(max_length=255)
    ... other fields ...

    class Meta:
       unique_together = ('content_type', 'object_id', 'key')

def lookup_block(object, key):
    return Block.objects.get(content_type=ContentType.objects.get_for_model(object),
                             object_id=object.pk,
                             key=key)

@register.simple_tag
def block(object, key)
   block = lookup_block(object, key)
   ... generate template content using 'block' ...

需要注意的是,您无法在object调用中使用Block.objects.get字段,因为它不是真正的数据库字段。您必须使用content_typeobject_id

我调用了模型Block,但是如果你有一些情况,多个唯一的(object, key)元组映射到同一个块,它实际上可能是一个本身具有{{{ 1}}到您的实际ForeignKey模型或帮助应用程序中的相应模型,如Van Gale所提到的那样。