Django经理在模型中设置了_set

时间:2009-02-18 23:40:15

标签: django django-models

我正在学习Django的过程中,但我无法弄清楚如何自己解决这个问题。我正在阅读本书开发人员库 - 使用Django进行Python Web开发,在一章中,您将构建一个包含两个模型(故事和类别)的简单CMS系统,一些通用视图和自定义视图以及模板视图。

本书仅包含列出故事,故事详情和搜索的代码。我想扩展它并构建一个包含类别和故事的嵌套列表的页面。

- Category1
-- Story1
-- Story2
- Category2
- Story3 etc.

我设法弄清楚如何为类别列表添加我自己的通用object_list视图。我的问题是,如果Story是公开的,则Story模型具有STATUS_CHOICES,并且自定义管理器只会默认获取公共Stories。我无法弄清楚如何告诉我的通用类别列表视图也使用自定义管理器,只获取公共故事。一切都有效,除了那个小问题。我能够在一个页面上为所有类别的故事创建所有类别的列表,唯一的问题是该列表包含非公共故事。

我不知道我是否走在正确的轨道上。我的urls.py包含一个获取所有Category对象的通用视图,在我的模板中我使用 category.story_set.all 来获取该类别的所有Story对象,然后我循环。< / p>

我认为可以在模板中添加if语句,并使用模型文件中的VIEWABLE_STATUS来检查是否应该列出它。该解决方案的问题在于它与DRY不兼容。

是否可以为Category模型添加某种类型的管理器,只有在类别上使用story_set时才能获取公共Story对象?

或者这是解决我问题的错误方法吗?

相关代码

urls.py(仅限类别列表视图):

urlpatterns += patterns('django.views.generic.list_detail',
    url(r'^categories/$', 'object_list', {'queryset': Category.objects.all(),
                                          'template_object_name': 'category'
                                         }, name='cms-categories'),

models.py:

from markdown import markdown
import datetime
from django.db import models
from django.db.models import permalink
from django.contrib.auth.models import User

VIEWABLE_STATUS = [3, 4]

class ViewableManager(models.Manager):
    def get_query_set(self):
        default_queryset = super(ViewableManager, self).get_query_set()
        return default_queryset.filter(status__in=VIEWABLE_STATUS)

class Category(models.Model):
    """A content category"""
    label = models.CharField(blank=True, max_length=50)
    slug = models.SlugField()

    class Meta:
        verbose_name_plural = "categories"

    def __unicode__(self):
        return self.label

    @permalink
    def get_absolute_url(self):
        return ('cms-category', (), {'slug': self.slug})

class Story(models.Model):
    """A hunk of content for our site, generally corresponding to a page"""

    STATUS_CHOICES = (
        (1, "Needs Edit"),
        (2, "Needs Approval"),
        (3, "Published"),
        (4, "Archived"),
    )

    title = models.CharField(max_length=100)
    slug = models.SlugField()
    category = models.ForeignKey(Category)
    markdown_content = models.TextField()
    html_content = models.TextField(editable=False)
    owner = models.ForeignKey(User)
    status = models.IntegerField(choices=STATUS_CHOICES, default=1)
    created = models.DateTimeField(default=datetime.datetime.now)
    modified = models.DateTimeField(default=datetime.datetime.now)

    class Meta:
        ordering = ['modified']
        verbose_name_plural = "stories"

    def __unicode__(self):
        return self.title

    @permalink
    def get_absolute_url(self):
        return ("cms-story", (), {'slug': self.slug})

    def save(self):
        self.html_content = markdown(self.markdown_content)
        self.modified = datetime.datetime.now()
        super(Story, self).save()

    admin_objects = models.Manager()
    objects = ViewableManager()

category_list.html(相关模板):

{% extends "cms/base.html" %}
{% block content %}
    <h1>Categories</h1>
    {% if category_list %}
        <ul id="category-list">
        {% for category in category_list %}
            <li><a href="{{ category.get_absolute_url }}">{{ category.label }}</a></li>
            {% if category.story_set %}
                <ul>
                    {% for story in category.story_set.all %}
                        <li><a href="{{ story.get_absolute_url }}">{{ story.title }}</a></li>
                    {% endfor %}
                </ul>
            {% endif %}
        {% endfor %}
        </ul>
    {% else %}
        <p>
            Sorry, no categories at the moment.
        </p>
    {% endif %}
{% endblock %}

4 个答案:

答案 0 :(得分:11)

我发现文档稍有不清楚,但use_for_related_fields Manager属性可能就是您要查找的内容。当你尝试它时,请注意this functionality uses _default_manager,它始终指向Model类中声明的第一个Manager,而不管它给出了什么属性名称。您可以随时通过覆盖相应queryset课程中的ModelAdmin方法来自定义管理网站中可修改的项目。

失败了......

class Category(models.Model):
    def public_stories(self):
        return Story.objects.filter(category=self)

...和...

{% for story in category.public_stories %}

这至少避免重复确定哪些故事可见的逻辑。

答案 1 :(得分:4)

如果您需要特定于模板的解决方案,则可以创建自定义过滤器。这样您就可以避免触及模型类

这将是您的过滤器:

from django import template

register = template.Library()

@register.filter
def viewable(queryset):
    return queryset.filter(status__in=VIEWABLE_STATUS)

您可以在模板中使用它

{% category.story_set|viewable %}

答案 2 :(得分:2)

要详细说明@insin的答案,您应该将use_for_related_fields = True添加到ViewableManager课程。这样,Category.story_set将返回自定义管理器的实例,该实例具有您需要的get_query_set方法。您的模板标记看起来像

{% category.story_set.get_query_set %}

编辑:@ insin的回答要好得多,我会用它。

答案 3 :(得分:2)

只需更改经理声明的位置即可。

admin_objects = models.Manager()
objects = ViewableManager()

objects = ViewableManager()
admin_objects = models.Manager()

根据the documentation

  

类上的默认管理器是在类上声明的第一个管理器(如果存在),或父层次结构中第一个抽象基类的默认管理器(如果存在)。如果没有显式声明默认管理器,则使用Django的普通默认管理器。