Django新手,努力了解如何实现自定义查询集

问题描述 投票:0回答:2

所以我对Django很新,我昨天开始玩,并且一直在玩标准的民意调查教程。

上下文

我希望能够根据自定义方法的结果过滤活动问题(在这种情况下,它是Question.is_open()方法(如下图1所示)。

我理解的问题

当我尝试使用像questions.objects.filter(is_open=true)这样的过滤器访问活动问题时,它失败了。如果我理解正确,这依赖于通过模型管理器公开的查询集,该模型管理器只能根据sql数据库中的记录进行过滤。

我的问题

1)我是否以大多数pythonic / django / dry方式解决这个问题?我应该通过继承models.Manager并生成自定义查询集来公开这些方法吗? (这似乎是在线的共识)。

2)如果我应该使用自定义查询集的管理器子类,我不确定代码是什么样的。例如,我应该通过cursor.execute使用sql(根据文档here,这似乎是非常低的水平)?或者是否有一种更好的,更高级别的方式来实现django本身?

我很欣赏任何有关如何处理此问题的见解。

谢谢

马特

我的models.py

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published',default=timezone.now())
    start_date = models.DateTimeField('poll start date',default=timezone.now())
    closed_date = models.DateTimeField('poll close date', default=timezone.now() + datetime.timedelta(days=1))


    def time_now(self):
        return timezone.now()

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    def is_open(self):
        return ((timezone.now() > self.start_date) and (timezone.now() < self.closed_date))

    def was_opened_recently(self):
        return self.start_date >= timezone.now() - datetime.timedelta(days=1) and self.is_open()

    def was_closed_recently(self):
        return self.closed_date >= timezone.now() - datetime.timedelta(days=1) and not self.is_open()

    def is_opening_soon(self):
        return self.start_date <= timezone.now() - datetime.timedelta(days=1)

    def closing_soon(self):
        return self.closed_date <= timezone.now() - datetime.timedelta(days=1)

[Update]

就像后续行动一样。我已经使用硬编码的SQL字符串(仅用于测试)将默认管理器子类化,但是,它失败了,因为它不是属性

class QuestionManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset()

    def get_expired(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                      select id, question_text, closed_date, start_date, pub_date from polls_question
                      where ( polls_question.start_date < '2017-12-24 00:08') and (polls_question.closed_date > '2017-12-25 00:01') 
                      order by pub_date;""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], closed_date=row[2], start_date=row[3], pub_date=row[4])
                result_list.append(p)
        return result_list

我用 active_poll_list = Question.objects.get_expired()调用方法

但我得到了例外

Exception Value:    
'Manager' object has no attribute 'get_expired'

我真的不确定我明白为什么这不起作用。一定是我对如何调用从管理器返回查询集的方法的误解。

任何建议将不胜感激。

谢谢

django django-models django-queryset
2个回答
1
投票

你的问题中有很多东西,我会尝试尽可能多地覆盖。

当您尝试获取模型的查询集时,只能使用字段属性作为查找。这意味着在您的示例中您可以执行以下操作:

Question.objects.filter(question_text='What's the question?')

要么:

Question.objects.filter(question_text__icontains='what')

但是你无法查询方法:

Question.objects.filter(is_open=True)

没有领域is_open。它是模型类的一种方法,在过滤查询集时不能使用它。

您在类Question中声明的方法可能更好地作为属性(@property)或缓存属性进行装饰。对于后来的导入:

from django.utils.functional import cached_property

并装饰这样的方法:

@cached_property
def is_open(self):
    # ...

这将使计算的值可用作属性,而不是方法:

question = Question.objects.get(pk=1)
print(questin.is_open)

为时间字段指定默认值时,您可能非常想要这样:

pub_date = models.DateTimeField('date published', default=timezone.now)

注意 - 它只是timezone.now!创建条目时应调用callable。否则,方法timezone.now()将在django应用程序第一次启动时调用,所有条目都将保存该时间。

如果要向管理器添加额外的方法,则必须将自定义管理器分配给objects

class Question(models.Model):
    # the fields ...
    objects = QuestionManager()

之后,方法get_expired将可用:

Question.objects.get_expired()

我希望这可以帮助您理解代码中出错的一些内容。


0
投票

看起来我在定义Question.objects时错过了方括号

它仍然没有用,但我想我可以从这里弄明白。

© www.soinside.com 2019 - 2024. All rights reserved.