2009-02-18 70 views
4

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

这本书只包含上市故事,故事细节和搜索的代码。我想扩大这个范围,并为类别和故事建立一个包含嵌套列表的页面。

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

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

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

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

是否有可能为类别模型添加某种类型的管理器,只在类别上使用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 %} 

回答

11

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

做不到这一点......

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

......还有......

{% for story in category.public_stories %} 

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

+0

谢谢。我无法弄清use_for_related_fields是如何工作的。试图将其添加到我的ViewableManager但没有区别。第二种解决方案运行良好。我只需要记住使用category.public_stories而不是category.story_set。 – 2009-02-20 00:02:54

2

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

{% category.story_set.get_query_set %} 

编辑:@ insin的答案是好多了,我会使用它。

+0

谢谢你的答复,但我无法弄清楚这一点。我在ViewableManager中添加了use_for_related_fields = True,并尝试了category.story_set.get_query_set,但它仍然返回了非公开故事。 – 2009-02-20 00:03:57

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

只要改变你的经理声明的位置。

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

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

根据the documentation

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