如何使Django ForeignKey适应可重用的应用程序?

时间:2013-06-08 12:12:50

标签: django dry code-reuse django-cms

我为Django-CMS写了一个旋转木马插件,它显示了截图。底层模型有一些与轮播相关的参数(高度,动画风格等),以及ForeignKeyScreenshotGroup

class ScreenshotGroup(models.Model):
    name = models.CharField(max_length=60)
    screenshots = models.ManyToManyField(Screenshot, through="ScreenshotGroupMember")

class Screenshot(models.Model):
    name = models.CharField(max_length=60)
    desc = models.TextField(_("description"), blank=True)
    img = models.ImageField(upload_to='img/')

class CarouselPluginModel(CMSPlugin):
    group = models.ForeignKey(ScreenshotGroup)
    height = models.IntegerField()
    ...

轮播的视图方法包含:

context['item_list'] = instance.group.screenshots.all()

(实际上,因为我使用的是Django-CMS,它位于cms_plugins.py render方法中,而不是view方法。)

模板通过以下方式引用屏幕截图字段:

{% for item in item_list %}
    {{ item.name }}
    {{ item.desc }}
    ...{{ item.img }}...
{% endfor %}

我的问题是:我想概括我的carousel插件,以便在其他项目中重用它,因此不依赖于Screenshot模型。我可以用for替换模板的include循环的内容,以允许每个项目指定如何在轮播中显示项目。但是如何概括CarouselPluginModel的{​​{1}}?

在任何特定的应用程序中,我只希望允许一种类型的模型(在我的示例中为ForeignKey) - 我不希望管理控制台允许包含任何其他模型。

谢谢!

1 个答案:

答案 0 :(得分:2)

基于karthikr建议的通用外键概念,这是我采用的完整解决方案。拼图的其他部分是:

  • 使用settings.py中的条目来限制允许哪些模型 通用外键;
  • 嗅探所选模型的多对多字段;
  • 使用{% include "carousel_item.html" %} 用于概括项目显示的模板。我会提供默认值 在应用程序中实现,但这种方式最终用户没有 必须符合我预先定义的字段。

models.py

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.conf import settings

allowed_models = getattr(settings, 'ALLOWED_MODELS_IN_CAROUSEL', [])
# must be a list of dictionaries with keys: app_label and model, e.g:
# ALLOWED_MODELS_IN_CAROUSEL=[{'app_label':'myapp', 'model':'screenshotgroup'},]

fk_models = None
if allowed_models:
    # don't like this repetition - how can I improve this?
    fk_models = models.Q(app_label = allowed_models[0]['app_label'].lower(), 
                         model = allowed_models[0]['model'].lower())
    for m in allowed_models[1:]:
        fk_models = fk_models | models.Q(app_label = m['app_label'].lower(),
                                         model = m['model'].lower())

class CarouselPluginModel(CMSPlugin):
    content_type = models.ForeignKey(ContentType, limit_choices_to = fk_models)
    object_id = models.PositiveIntegerField()
    content_group = generic.GenericForeignKey('content_type', 'object_id')
    ...

视图需要在所选模型中找到ManyToManyField,例如:

if instance.content_group and instance.content_group._meta.many_to_many:
    m2m_fieldname = instance.content_group._meta.many_to_many[0].name
    context['item_list'] = getattr(instance.content_group, m2m_fieldname).all()

模板可能如下所示:

{% for item in item_list %}
    {% include "carousel_item.html" %}
{% endfor %}

最后,我会建议您使用的模型在其描述中包含id,因为管理面板必须按ID选择,例如:

class ScreenshotGroup(models.Model):
    name = models.CharField(max_length=60)
    screenshots = models.ManyToManyField(Screenshot, through="ScreenshotGroupMember")
    def __unicode__(self):
        return u"{0} (id {1})".format(self.name, self.id)