【问题标题】:How do I adapt Django ForeignKey to a reusable app?如何使 Django ForeignKey 适应可重用的应用程序?
【发布时间】:2013-06-04 15:57:35
【问题描述】:

我为 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()
    ...

轮播的view方法包含:

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

(其实因为我用的是Django-CMS,所以在cms_plugins.pyrender方法中,而不是view方法中。)

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

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

我的问题是:我想推广我的轮播插件以在其他项目中重用它,所以这不依赖于Screenshot 模型。我可以用include 替换模板的for 循环的内容,以允许每个项目指定如何在轮播中显示项目。但是我如何概括CarouselPluginModelForeignKey

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

谢谢!

【问题讨论】:

    标签: django dry code-reuse django-cms


    【解决方案1】:

    基于 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)
    

    【讨论】:

      猜你喜欢
      • 2010-12-05
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 2012-03-14
      • 2017-08-25
      • 2011-08-22
      • 2014-12-18
      • 1970-01-01
      相关资源
      最近更新 更多