0

我有一个包含三个字段的模型来确定文本是否应该在模板中可见。

如果选中布尔字段,它将覆盖两个日期字段。

如果布尔字段未选中,则仅当 startdate 为过去时才返回 True,如果设置了 enddate,则仅当日期设置为将来时才返回 true。

我怎样才能做到这一点?

这是我尝试过的,但没有像我想要的那样工作:

class Entry(models.Model):
    name = models.CharField(max_length=200)

    visible = models.BooleanField(default=False, help_text="If checked, the text will be visible, even if the datefield is set")
    visible_start = models.DateTimeField(help_text="Visible from", blank=True, null=True)
    visible_end = models.DateTimeField(help_text="Visible to", blank=True, null=True)

    def is_visible(self):
        now = timezone.now()

        if not self.visible:
            if self.visible_start and now < self.visible_start:
                return False

            if self.visible_end and now > self.visible_end:  
                return False            

        return True
4

2 回答 2

3

您可以实现一个管理器,例如(未经测试):

class VisibleEntryManager(models.Manager):
    def get_query_set(self):
        now = datetime.now()
        return super(VisibleEntryManager, self).get_query_set()\
               .filter(visible=True)\
               .filter(visible_start__gte=now, visible_end__lte=now) # Or use Q objects

class Entry(models.Model):
    # other stuff
    visible_objects = VisibleEntryManager()

编辑:管理员使用默认管理器(通过调用_default_managerModel)。如果你想推迟管理对象,你可以这样做:

class EntryAdmin(admin.ModelAdmin):
    # other stuff

    def queryset(self, request):
        if request.user.is_superuser or request.user.is_staff:  # example rule
            qs = self.model.objects.all()  # could be admin_objects if you want to set 
                                           # .objects as the default filtered option on 
                                           # the Entry model.
            ordering = self.get_ordering(request)
            if ordering:
                qs = qs.order_by(*ordering)
            return qs
        return super(ProductAdmin, self).queryset(request)
于 2013-10-15T08:57:45.113 回答
1

开始和停止具有 null=True。因此,您可以检查:Null < nowNull > Now(始终为 False)。如果 start 和 stop 在 now 条件内,则下面的代码将返回 True。

def is_visible(self):
    now = datetime.now()

    if self.is_visible:
        # is_visible field is checked.
        return True

    #elif self.visible_start < now and self.visible_stop == Null:
    #    return True

    elif self.visible_start < now and self.visible_stop > now:
        # Start is before now and stop is after now.
        return True

    else:
        # All other options
        return False

更新

我添加了这个来显示何时使用过滤器、方法以及何时使用管理器。

看起来你想做这样的事情:

# views.py
objects = Entry.objects.all()

# template.html
{% for obj in objects %}
    {% if obj.is_visible %}
        {{ obj.title }}
    {% endif %}
{% endfor %}

不要这样做!这将循环所有条目(可以是很多)。这是一场表演的噩梦!首先过滤条目。像这样:

# views.py
now = datetime.now()    
objs0 = Entry.objects.filter(visible=True)
objs1 = Entry.objects.filter(visible_start__lte=now, visible_stop__gt=now)
objects = objs0 | objs1

# template.html
{% for obj in objects %}
    {{ obj.title }}
{% endfor %}

这只会循环抛出相关的对象。如果此列表在您的项目中多次出现,并且您注意到多次在视图上方写入,那么请考虑使用模型管理器。就像@HeddeVanDerHeide 所建议的那样。编写管理器后,您可以获得所有可见对象:

objects = Entry.visible_objects.all()

为什么不直接对模型方法进行过滤?

objects = Entry.objects.filter(is_visible=True)

因为 Django 不是这样工作的。查询是针对数据库执行的,而数据库不知道方法。但是模型方法有什么用呢?如果要显示条目之间的差异,请使用模型方法:

# models.py
def is_important(self):
    now = datetime.now()
    if self.start < now and self.stop > now:
        return True
    else:
        return False

# views.py
objects = Entries.objects.all()

# template.html
{% for obj in objects %}
    <p{% if obj.is_important %} class="highlight"{% endif %}>{{ obj.title }}</p>
{% endfor %}

当您想在管理列表显示中显示布尔值时,模型方法使这成为可能:

class EntryAdmin(admin.ModelAdmin):
    list_display = ('title', 'is_visible')

从文档中:

在模型上定义自定义方法以向您的对象添加自定义“行级”功能。而 Manager 方法旨在做“表范围”的事情。

于 2013-10-15T09:02:02.530 回答