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

时间:2017-12-25 12:24:07

标签: django django-models django-queryset

所以我对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)

[更新]

就像后续行动一样。我已经使用硬编码的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'

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

我们非常感谢任何建议。

由于

2 个答案:

答案 0 :(得分: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()

我希望这有助于您了解代码中出现的一些问题。

答案 1 :(得分:0)

在定义Question.objects

时,我似乎错过了方括号

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