Mezzanine/django 模板渲染不调用模型方法

Mezzanine/django template rendering not calling model methods

TLDR: 自定义 view/template 仅有时调用自定义模型方法。有时结果错误(方法没有打印消息),有时结果正确(方法有打印消息)。

我是 Mezzanine 的新手(更习惯于 Pyramid)并且我遇到了一些模板渲染的奇怪的非确定性问题。我创建了一个带有内部 Django 应用程序的 Mezzanine 项目,用于添加 3 种新类型:Experiment(Page,RichText),Instrument(Page,RichText),DailyStatus(Displayable,Ownable,RichText)。我在 DailyStatus 上定义了一个 ForeignKey 以 link 它到单个实验。

由于 DailyStatus 不是 Page 的子类,因此我创建了自己的视图和模板(扩展 base.html)。由于 DailyStatus 具有类似的用途,因此该模板从博客详细信息模板中获取了大量信息。在模板中,我调用了两种方法来获取使用 DailyStatus status_date DateField.

的实验(get_previous_by_status_dateget_next_by_status_date)中的上一个和下一个 DailyStatus

有时,当我访问每日状态页面时,prev/next 按钮会按预期工作(显示实验中的下一个状态或在实验中不显示任何 if first/last)。但其他时候,当服务器 restarts/reloads 时,它似乎正在通过 publish_date 选择 prev/next 或者只是不工作。它从不在服务器的单个实例期间切换这些状态。我已经使用 pdb 和 ipdb 尝试对此进行调试,并且已经到了调用 daily_status_inst.get_previous_by_status_date() 产生错误结果而 daily_status_inst._get_next_or_previous_by_status_date(False) 产生正确结果的地步。正确状态和错误状态之间的上下文似乎没有任何不同(我将指令转储到文件中)。我已将 print 添加到各个 prev/next 方法中,有时它们甚至似乎没有被调用(无输出)但它们 return 是一个不正确的值。

有谁知道发生了什么事吗?或者任何其他有用的调试技巧?我的 DailyStatus 模型如下:

class DailyStatus(Displayable, Ownable, RichText):
    experiment = models.ForeignKey(Experiment, related_name="daily_statuses")
    status_date = models.DateField(_("Status Date"))

    class Meta:
        verbose_name = _("Daily Status")
        verbose_name_plural = _("Daily Statuses")
        ordering = ("-status_date",)
        order_with_respect_to = "experiment"

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        from django.core.urlresolvers import reverse
        return reverse('daily_status_detail',
                       kwargs={
                           "experiment": self.experiment.slug,
                           "slug": self.slug,
                       })

    def _get_next_or_previous_by_status_date(self, is_next, **kwargs):
        arg = "status_date__gt" if is_next else "status_date__lt"
        order = "status_date" if is_next else "-status_date"
        lookup = {arg: self.status_date, "experiment": self.experiment}
        try:
            queryset = DailyStatus.objects.published
        except AttributeError:
            queryset = DailyStatus.objects.all
        try:
            dstatus = queryset(**kwargs).filter(**lookup).order_by(order)[0]
            return dstatus
        except IndexError:
            return False

    def get_next_by_status_date(self, **kwargs):
        dstatus = self._get_next_or_previous_by_status_date(True, **kwargs)
        return dstatus

    def get_previous_by_status_date(self, **kwargs):
        dstatus = self._get_next_or_previous_by_status_date(False, **kwargs)
        return dstatus

最简单的模板:

<html>
<head>
</head>
<body>
    {{ daily_status.get_previous_by_status_date }}
    <p></p>
    {{ daily_status.get_next_by_status_date }}
</body>
</html>

使用:

* Mezzanine 3.1.10
* Django 1.6.11
* Python 3.4.3
* SQLite 3.8.10.1
* Darwin 14.3.0

感谢您的帮助。

更新 1:

我已经追踪到 prev/next 方法是否 _curried。当它们被柯里化时,它从不调用实际的方法(或者至少不打印任何输出)。当它是正常绑定方法时,我得到预期的输出:

# custom view
status_posts = DailyStatus.objects.published().filter(experiment__slug=experiment).all()
print(status_posts.get_next_by_status_date)
# get_object_or_404 and so on with rendering...

# print produces:
<bound method DailyStatus._curried of <DailyStatus: 2013: Update 1>>
# or (after a couple server restarts):
<bound method DailyStatus.get_next_by_status_date of <DailyStatus: 2013: Update 1>>

花了一段时间,但我想通了。这一切都与信号的魔力(我假设基于问题的竞争条件性质)和 contribute_to_class 方法有关。下面是正确的模型,或者至少是一种适用于我尝试做的工作的解决方案。

当您在模型上创建 DateField 并将其添加到其他对象(我认为)时,会调用 contribute_to_class 方法,该方法会添加 get_next_by_%sget_previous_by_%s 方法与字段名称。因此,当我使用 get_previous_by_status_date 名称创建方法时,添加的 contribute_to_class 方法与添加的我的方法之间存在竞争条件。由于您不能在模板中使用参数调用方法,因此我不得不创建自动添加实验过滤器的新方法。

如果我有什么不对的地方纠正我。幸好我不害怕阅读源代码。

class DailyStatus(Displayable, Ownable, RichText):
    experiment = models.ForeignKey(Experiment, related_name="daily_statuses")
    status_date = models.DateField(_("Status Date"))

    class Meta:
        verbose_name = _("Daily Status")
        verbose_name_plural = _("Daily Statuses")
        ordering = ("-status_date",)
        order_with_respect_to = "experiment"

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        from django.core.urlresolvers import reverse
        return reverse('daily_status_detail',
                       kwargs={
                           "experiment": self.experiment.slug,
                           "slug": self.slug,
                       })

    def get_next_by_status_date_exp(self, **kwargs):
        """
        Retrieves next object by status date.
        """
        kwargs.setdefault("experiment", self.experiment)
        return self.get_next_by_status_date(**kwargs)

    def get_previous_by_status_date_exp(self, **kwargs):
        """
        Retrieves previous object by status date.
        """
        kwargs.setdefault("experiment", self.experiment)
        return self.get_previous_by_status_date(**kwargs)