2014-09-26 4 views
1

Скажем, у меня был кусок кода, как:Проверить QuerySet пусто в Django

class DetailView(generic.DetailView): 
    model = Question 
    template_name = 'polls/detail.html' 

    def get_queryset(self): 
     """ 
     Excludes any questions that aren't published yet. 
     """ 
     all_entries = Choice.objects.all() 

     if not all_entries: 
      return Question.objects.filter(pub_date__lte=timezone.now()) 

Я пытаюсь получить все варианты от вопроса, и возвращает 404, если нет ни одного доступного. Однако я только удалось реализовать часть и получаю ошибку:

'NoneType' object has no attribute 'filter'

Это берется из самого дна Django tutorial где он упоминает

For example, it’s silly that Questions can be published on the site that have no Choices. So, our views could check for this, and exclude such Questions.

Где я буду неправильно?

EDIT:

Я изменил код, ссылающийся "all_entries" с:

all_entries = Choice.objects.all().count() 

if all_entries > 0: 
    return Question.objects.filter(pub_date__lte=timezone.now()) 

но просто возвращает все вопросы, есть ли у них выбор или нет ...

Models.py

from django.db import models 
import datetime 
from django.utils import timezone 

class Question(models.Model): 
    question_text = models.CharField(max_length=200) 
    pub_date = models.DateTimeField('date published') 

    def __str__(self):    # __unicode__ on Python 2 
     return self.question_text 

    def was_published_recently(self): 
     now = timezone.now() 
     return now - datetime.timedelta(days=1) <= self.pub_date <= now 
    was_published_recently.admin_order_field = 'pub_date' 
    was_published_recently.boolean = True 
    was_published_recently.short_description = 'Published recently?' 


class Choice(models.Model): 
    question = models.ForeignKey(Question) 
    choice_text = models.CharField(max_length=200) 
    votes = models.IntegerField(default=0) 

    def __str__(self):    # __unicode__ on Python 2 
     return self.choice_text 

EDIT Для cms_mgr

В основном я хочу, чтобы проверить, что число вариантов, связанных с указанным вопросом является пустым. Когда я перехожу к этой ссылке - http://127.0.0.1:8000/polls/3/ Я хочу получить вопрос с id ('3') и проверить количество вариантов, которые он содержит.

+0

Как выглядит код модели «Вопрос»? –

+0

См. Edit – Jon

ответ

4

Один из способов приблизиться ко всем вопросам, если и только если у них есть связанные варианты, нужно получить список Choices, а не вопросы, а затем оценить ваш запрос. Например:

Question.objects.filter(pk__in=[x.question.pk for x in Choice.objects.all()]) 

бит внутри квадратных скобок является list comprehension. Список понятий действительно полезен и заслуживает внимания. В вышеприведенном коде сначала будет оцениваться понимание. В основном говорится: «для каждого x в Choice.objects.all() положить x.pk в этот список». Затем запрос будет возвращать каждый Question, для которого существует по крайней мере один связанный Choice.

Если вы хотите получить запрос Choices для каждого Question, то они уже доступны для вас. Для любого примера из Question, мы позвоним нашему q, вы можете получить его ассоциированный Choices с q.choice_set.all(), который будет возвращать пустой, если их нет.

Для реализации: сначала измените имя своего DetailView, чтобы избежать путаницы с общим. Назовем это QuestionDetailView. Добавьте context_object_name, чтобы сделать ваш шаблон более удобным для чтения позже. Не переделывайте запрос по умолчанию, чтобы исключить неопубликованные вопросы, потому что правильный способ сделать это - с model manager.

class QuestionDetailView(generic.DetailView): 
    model = Question 
    template_name = 'polls/detail.html' 
    context_object_name = 'question' 

В шаблоне для этой точки зрения связанных выбор на вопрос будет уже быть доступны для вас что-то вроде {% for choice in question.choice_set.all %}. Это question называется так потому, что мы дали ему context_object_name, чтобы вы могли использовать любое имя, которое вам нравится. Обратите внимание, что нет необходимости в () после all в шаблоне.

Если вам нужно выполнить какую-либо другую работу с выбором, прежде чем возвращать их в свой шаблон, вы можете сделать это в представлении. Таким образом, в вашем QuestionDetailView вы могли бы добавить:

def get_context_data(self, **kwargs): 
    # call the base implementation to get original context 
    context = super(DetailView, self).get_context_data(**kwargs) 
    context['choices'] = self.object.choice_set.all() # your question's choice set, to manipulate 
                 # as you see fit 
    return context 

Я объясню, что здесь происходит. Это добавляет дополнительный контекст к тому, что будет отображаться в представлении для вашего шаблона. Код, который я написал, просто вернет все варианты вопроса, поэтому не добавляет к тому, что уже было, но вы можете выполнять любые другие действия, которые вам нужны. Измененный набор вариантов будет доступен в вашем шаблоне как choices, чтобы вы могли сделать {% for choice in choices %}.

Независимо от того, каким образом вы выберете пустые варианты выбора, вы можете легко удовлетворить его ({% if choices %} или {% if question.choice_set.count %} в шаблоне, например). Вероятно, вы хотите обработать любой пустой набор запросов более элегантно, чем 404, потому что вы действительно не хотите, чтобы пользователи направлялись на страницы ошибок для прогнозируемого результата, такого как пустой запрос.

+0

Что такое «х»? Это похоже на то, о чем вы думали? Question.objects.filter (pk__in = [Question.id for x in Choice.objects.all()]) – Jon

+0

Спасибо, это, по крайней мере, компилируется сейчас, но количество возвращаемых вариантов кажется всего 2. У меня 3 вопроса - один, содержащий 3 варианта, один из которых содержит 2 варианта и один пуст. Я ожидал всего 5 вариантов. Кроме того, есть ли способ получить выбор по идентификатору вопроса из этого списка? – Jon

+0

Он делает? Кажется, что ваш код получает полный выбор из всех вопросов. Получение значения 2 меня смущает, поскольку, во-первых, есть всего 5 вариантов, а другой, мне нужно получить количество вариантов для этого конкретного вопроса. – Jon

1

Вы ничего не возвращаете, если all_entries is не пусто. Если функция явно не возвращает значение, Python возвращает None.

+0

Спасибо, что помешало ошибке, но теперь он отображает вопрос, независимо от количества вариантов. – Jon

0

То, что вы пытаетесь сделать, не так просто, как может показаться. Вы должны отфильтровать набор вопросов по их количеству вариантов, а не проверить, существуют ли варианты, а затем вернуть вопрос.

Чтобы узнать, как это можно сделать с аннотацией, вы должны проверить this other answer.

+0

Нет ли более простого способа? У меня уже есть объект Question, не можете ли вы как-то просто получить выбор из объекта? – Jon

+0

Это когда вы будете использовать 'choice_set'. Для любого 'вопроса'' q' его соответствующие «выбор» являются 'q.choice_set.all()'. –

0

Возможно, стоит упомянуть учебник part 7 далее в этой серии, дает подсказку о правильном способе устранения следующей проблемы из корня.

For example, it’s silly that Questions can be published on the site that have no Choices. So, our views could check for this, and exclude such Questions.

Я думаю, что лучший способ исправить это, чтобы не создавать «Вопросы нет выбора», в первую очередь (при условии, что никто не собирается умерить с базой данных напрямую). В этом учебном курсе описано, как изменить страницу сайта администратора, где мы должны разрешать администратору добавлять Вопрос с помощью как минимум 2 вариантов.