init
This commit is contained in:
		
						commit
						e24f9c39cb
					
				
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
# Django #
 | 
			
		||||
*.log
 | 
			
		||||
*.pot
 | 
			
		||||
*.pyc
 | 
			
		||||
__pycache__
 | 
			
		||||
db.sqlite3
 | 
			
		||||
media
 | 
			
		||||
							
								
								
									
										3
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
# django-polls
 | 
			
		||||
 | 
			
		||||
Basic django application built after the 4.1 tutorial.
 | 
			
		||||
							
								
								
									
										21
									
								
								manage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								manage.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
"""Django's command-line utility for administrative tasks."""
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
 | 
			
		||||
    try:
 | 
			
		||||
        from django.core.management import execute_from_command_line
 | 
			
		||||
    except ImportError as exc:
 | 
			
		||||
        raise ImportError(
 | 
			
		||||
            "Couldn't import Django. Are you sure it's installed and "
 | 
			
		||||
            "available on your PYTHONPATH environment variable? Did you "
 | 
			
		||||
            "forget to activate a virtual environment?"
 | 
			
		||||
        ) from exc
 | 
			
		||||
    execute_from_command_line(sys.argv)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
							
								
								
									
										0
									
								
								mysite/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								mysite/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										121
									
								
								mysite/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								mysite/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,121 @@
 | 
			
		||||
"""
 | 
			
		||||
Django settings for mysite project.
 | 
			
		||||
 | 
			
		||||
Generated by 'django-admin startproject' using Django 2.2.28.
 | 
			
		||||
 | 
			
		||||
For more information on this file, see
 | 
			
		||||
https://docs.djangoproject.com/en/2.2/topics/settings/
 | 
			
		||||
 | 
			
		||||
For the full list of settings and their values, see
 | 
			
		||||
https://docs.djangoproject.com/en/2.2/ref/settings/
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
			
		||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Quick-start development settings - unsuitable for production
 | 
			
		||||
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
 | 
			
		||||
 | 
			
		||||
# SECURITY WARNING: keep the secret key used in production secret!
 | 
			
		||||
SECRET_KEY = 'mad12iktf*686=(b@g8ntze_1ee$2i-@ozrj@kiw8t!k0f36a*'
 | 
			
		||||
 | 
			
		||||
# SECURITY WARNING: don't run with debug turned on in production!
 | 
			
		||||
DEBUG = True
 | 
			
		||||
 | 
			
		||||
ALLOWED_HOSTS = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Application definition
 | 
			
		||||
 | 
			
		||||
INSTALLED_APPS = [
 | 
			
		||||
    'polls.apps.PollsConfig',
 | 
			
		||||
    'django.contrib.admin',
 | 
			
		||||
    'django.contrib.auth',
 | 
			
		||||
    'django.contrib.contenttypes',
 | 
			
		||||
    'django.contrib.sessions',
 | 
			
		||||
    'django.contrib.messages',
 | 
			
		||||
    'django.contrib.staticfiles',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
MIDDLEWARE = [
 | 
			
		||||
    'django.middleware.security.SecurityMiddleware',
 | 
			
		||||
    'django.contrib.sessions.middleware.SessionMiddleware',
 | 
			
		||||
    'django.middleware.common.CommonMiddleware',
 | 
			
		||||
    'django.middleware.csrf.CsrfViewMiddleware',
 | 
			
		||||
    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
			
		||||
    'django.contrib.messages.middleware.MessageMiddleware',
 | 
			
		||||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
ROOT_URLCONF = 'mysite.urls'
 | 
			
		||||
 | 
			
		||||
TEMPLATES = [
 | 
			
		||||
    {
 | 
			
		||||
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
 | 
			
		||||
        'DIRS': [BASE_DIR  + '/templates'],
 | 
			
		||||
        'APP_DIRS': True,
 | 
			
		||||
        'OPTIONS': {
 | 
			
		||||
            'context_processors': [
 | 
			
		||||
                'django.template.context_processors.debug',
 | 
			
		||||
                'django.template.context_processors.request',
 | 
			
		||||
                'django.contrib.auth.context_processors.auth',
 | 
			
		||||
                'django.contrib.messages.context_processors.messages',
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
WSGI_APPLICATION = 'mysite.wsgi.application'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Database
 | 
			
		||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 | 
			
		||||
 | 
			
		||||
DATABASES = {
 | 
			
		||||
    'default': {
 | 
			
		||||
        'ENGINE': 'django.db.backends.sqlite3',
 | 
			
		||||
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Password validation
 | 
			
		||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
 | 
			
		||||
 | 
			
		||||
AUTH_PASSWORD_VALIDATORS = [
 | 
			
		||||
    {
 | 
			
		||||
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
 | 
			
		||||
    },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Internationalization
 | 
			
		||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
 | 
			
		||||
 | 
			
		||||
LANGUAGE_CODE = 'en-us'
 | 
			
		||||
 | 
			
		||||
TIME_ZONE = 'Europe/Berlin'
 | 
			
		||||
 | 
			
		||||
USE_I18N = True
 | 
			
		||||
 | 
			
		||||
USE_L10N = True
 | 
			
		||||
 | 
			
		||||
USE_TZ = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Static files (CSS, JavaScript, Images)
 | 
			
		||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
 | 
			
		||||
 | 
			
		||||
STATIC_URL = '/static/'
 | 
			
		||||
							
								
								
									
										22
									
								
								mysite/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								mysite/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
"""mysite URL Configuration
 | 
			
		||||
 | 
			
		||||
The `urlpatterns` list routes URLs to views. For more information please see:
 | 
			
		||||
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
 | 
			
		||||
Examples:
 | 
			
		||||
Function views
 | 
			
		||||
    1. Add an import:  from my_app import views
 | 
			
		||||
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
 | 
			
		||||
Class-based views
 | 
			
		||||
    1. Add an import:  from other_app.views import Home
 | 
			
		||||
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
 | 
			
		||||
Including another URLconf
 | 
			
		||||
    1. Import the include() function: from django.urls import include, path
 | 
			
		||||
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.urls import include, path
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('polls/', include('polls.urls')),
 | 
			
		||||
    path('admin/', admin.site.urls),
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										16
									
								
								mysite/wsgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mysite/wsgi.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
WSGI config for mysite project.
 | 
			
		||||
 | 
			
		||||
It exposes the WSGI callable as a module-level variable named ``application``.
 | 
			
		||||
 | 
			
		||||
For more information on this file, see
 | 
			
		||||
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from django.core.wsgi import get_wsgi_application
 | 
			
		||||
 | 
			
		||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
 | 
			
		||||
 | 
			
		||||
application = get_wsgi_application()
 | 
			
		||||
							
								
								
									
										0
									
								
								polls/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								polls/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								polls/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								polls/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
 | 
			
		||||
# Register your models here.
 | 
			
		||||
from .models import Question, Choice
 | 
			
		||||
 | 
			
		||||
class ChoiceInline(admin.TabularInline):
 | 
			
		||||
    model = Choice
 | 
			
		||||
    extra = 3
 | 
			
		||||
 | 
			
		||||
class QuestionAdmin(admin.ModelAdmin):
 | 
			
		||||
    fieldsets = [
 | 
			
		||||
        (None,               {'fields': ['question_text']}),
 | 
			
		||||
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
 | 
			
		||||
    ]
 | 
			
		||||
    inlines = [ChoiceInline]
 | 
			
		||||
    list_display = ('question_text', 'pub_date', 'was_published_recently')
 | 
			
		||||
    list_filter = ['pub_date']
 | 
			
		||||
    search_fields = ['question_text']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
admin.site.register(Question, QuestionAdmin)
 | 
			
		||||
							
								
								
									
										5
									
								
								polls/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								polls/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PollsConfig(AppConfig):
 | 
			
		||||
    name = 'polls'
 | 
			
		||||
							
								
								
									
										32
									
								
								polls/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								polls/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
# Generated by Django 2.2.28 on 2023-01-10 18:31
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Question',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('question_text', models.CharField(max_length=200)),
 | 
			
		||||
                ('pub_date', models.DateTimeField(verbose_name='date published')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Choice',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('choice_text', models.CharField(max_length=200)),
 | 
			
		||||
                ('votes', models.IntegerField(default=0)),
 | 
			
		||||
                ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Question')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										0
									
								
								polls/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								polls/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										27
									
								
								polls/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								polls/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
# Create your models here.
 | 
			
		||||
 | 
			
		||||
class Question(models.Model):
 | 
			
		||||
    question_text = models.CharField(max_length=200)
 | 
			
		||||
    pub_date = models.DateTimeField('date published')
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.question_text
 | 
			
		||||
    @admin.display(
 | 
			
		||||
            boolean=True,
 | 
			
		||||
            ordering='pub_date',
 | 
			
		||||
            description='Published recently?',
 | 
			
		||||
    )
 | 
			
		||||
    def was_published_recently(self):
 | 
			
		||||
        now = timezone.now()
 | 
			
		||||
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
 | 
			
		||||
 | 
			
		||||
class Choice(models.Model):
 | 
			
		||||
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
 | 
			
		||||
    choice_text = models.CharField(max_length=200)
 | 
			
		||||
    votes = models.IntegerField(default=0)
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.choice_text
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								polls/static/polls/images/background.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								polls/static/polls/images/background.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6.4 MiB  | 
							
								
								
									
										7
									
								
								polls/static/polls/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								polls/static/polls/style.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
li a {
 | 
			
		||||
    color: green;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
    background: white url("images/background.png") no-repeat;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								polls/templates/polls/detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								polls/templates/polls/detail.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
<form action="{% url 'polls:vote' question.id %}" method="post">
 | 
			
		||||
{% csrf_token %}
 | 
			
		||||
<fieldset>
 | 
			
		||||
    <legend><h1>{{ question.question_text }}</h1></legend>
 | 
			
		||||
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 | 
			
		||||
    {% for choice in question.choice_set.all %}
 | 
			
		||||
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
 | 
			
		||||
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
</fieldset>
 | 
			
		||||
<input type="submit" value="Vote">
 | 
			
		||||
</form>
 | 
			
		||||
							
								
								
									
										13
									
								
								polls/templates/polls/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								polls/templates/polls/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
{% load static %}
 | 
			
		||||
 | 
			
		||||
<link rel="stylesheet" href="{% static 'polls/style.css' %}">
 | 
			
		||||
 | 
			
		||||
{% if latest_question_list %}
 | 
			
		||||
    <ul>
 | 
			
		||||
    {% for question in latest_question_list %}
 | 
			
		||||
        <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </ul>
 | 
			
		||||
{% else %}
 | 
			
		||||
    <p>No polls are available.</p>
 | 
			
		||||
{% endif %}
 | 
			
		||||
							
								
								
									
										9
									
								
								polls/templates/polls/results.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								polls/templates/polls/results.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
<h1>{{ question.question_text }}</h1>
 | 
			
		||||
 | 
			
		||||
<ul>
 | 
			
		||||
{% for choice in question.choice_set.all %}
 | 
			
		||||
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
 | 
			
		||||
{% endfor %}
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<a href="{% url 'polls:detail' question.id %}">Vote again?</a
 | 
			
		||||
							
								
								
									
										123
									
								
								polls/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								polls/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,123 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from .models import Question
 | 
			
		||||
 | 
			
		||||
class QuestionModelTests(TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_was_published_recently_with_future_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        was_published_recently() returns False for questions whose pub_date
 | 
			
		||||
        is in the future.
 | 
			
		||||
        """
 | 
			
		||||
        time = timezone.now() + datetime.timedelta(days=30)
 | 
			
		||||
        future_question = Question(pub_date=time)
 | 
			
		||||
        self.assertIs(future_question.was_published_recently(), False)
 | 
			
		||||
 | 
			
		||||
    def test_was_published_recently_with_old_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        was_published_recently() returns False for questions whose pub_date
 | 
			
		||||
        is older than 1 day.
 | 
			
		||||
        """
 | 
			
		||||
        time = timezone.now() - datetime.timedelta(days=1, seconds=1)
 | 
			
		||||
        old_question = Question(pub_date=time)
 | 
			
		||||
        self.assertIs(old_question.was_published_recently(), False)
 | 
			
		||||
 | 
			
		||||
    def test_was_published_recently_with_recent_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        was_published_recently() returns True for questions whose pub_date
 | 
			
		||||
        is within the last day.
 | 
			
		||||
        """
 | 
			
		||||
        time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
 | 
			
		||||
        recent_question = Question(pub_date=time)
 | 
			
		||||
        self.assertIs(recent_question.was_published_recently(), True)
 | 
			
		||||
 | 
			
		||||
class QuestionDetailViewTests(TestCase):
 | 
			
		||||
    def test_future_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        The detail view of a question with a pub_date in the future
 | 
			
		||||
        returns a 404 not found.
 | 
			
		||||
        """
 | 
			
		||||
        future_question = create_question(question_text='Future question.', days=5)
 | 
			
		||||
        url = reverse('polls:detail', args=(future_question.id,))
 | 
			
		||||
        response = self.client.get(url)
 | 
			
		||||
        self.assertEqual(response.status_code, 404)
 | 
			
		||||
 | 
			
		||||
    def test_past_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        The detail view of a question with a pub_date in the past
 | 
			
		||||
        displays the question's text.
 | 
			
		||||
        """
 | 
			
		||||
        past_question = create_question(question_text='Past Question.', days=-5)
 | 
			
		||||
        url = reverse('polls:detail', args=(past_question.id,))
 | 
			
		||||
        response = self.client.get(url)
 | 
			
		||||
        self.assertContains(response, past_question.question_text)
 | 
			
		||||
 | 
			
		||||
def create_question(question_text, days):
 | 
			
		||||
    """
 | 
			
		||||
    Create a question with the given `question_text` and published the
 | 
			
		||||
    given number of `days` offset to now (negative for questions published
 | 
			
		||||
    in the past, positive for questions that have yet to be published).
 | 
			
		||||
    """
 | 
			
		||||
    time = timezone.now() + datetime.timedelta(days=days)
 | 
			
		||||
    return Question.objects.create(question_text=question_text, pub_date=time)
 | 
			
		||||
 | 
			
		||||
class QuestionIndexViewTests(TestCase):
 | 
			
		||||
    def test_no_questions(self):
 | 
			
		||||
        """
 | 
			
		||||
        If no questions exist, an appropriate message is displayed.
 | 
			
		||||
        """
 | 
			
		||||
        response = self.client.get(reverse('polls:index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
        self.assertContains(response, "No polls are available.")
 | 
			
		||||
        self.assertQuerysetEqual(response.context['latest_question_list'], [])
 | 
			
		||||
 | 
			
		||||
    def test_past_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        Questions with a pub_date in the past are displayed on the
 | 
			
		||||
        index page.
 | 
			
		||||
        """
 | 
			
		||||
        question = create_question(question_text="Past question.", days=-30)
 | 
			
		||||
        response = self.client.get(reverse('polls:index'))
 | 
			
		||||
        self.assertQuerysetEqual(
 | 
			
		||||
            response.context['latest_question_list'],
 | 
			
		||||
            [question],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_future_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        Questions with a pub_date in the future aren't displayed on
 | 
			
		||||
        the index page.
 | 
			
		||||
        """
 | 
			
		||||
        create_question(question_text="Future question.", days=30)
 | 
			
		||||
        response = self.client.get(reverse('polls:index'))
 | 
			
		||||
        self.assertContains(response, "No polls are available.")
 | 
			
		||||
        self.assertQuerysetEqual(response.context['latest_question_list'], [])
 | 
			
		||||
 | 
			
		||||
    def test_future_question_and_past_question(self):
 | 
			
		||||
        """
 | 
			
		||||
        Even if both past and future questions exist, only past questions
 | 
			
		||||
        are displayed.
 | 
			
		||||
        """
 | 
			
		||||
        question = create_question(question_text="Past question.", days=-30)
 | 
			
		||||
        create_question(question_text="Future question.", days=30)
 | 
			
		||||
        response = self.client.get(reverse('polls:index'))
 | 
			
		||||
        self.assertQuerysetEqual(
 | 
			
		||||
            response.context['latest_question_list'],
 | 
			
		||||
            [question],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_two_past_questions(self):
 | 
			
		||||
        """
 | 
			
		||||
        The questions index page may display multiple questions.
 | 
			
		||||
        """
 | 
			
		||||
        question1 = create_question(question_text="Past question 1.", days=-30)
 | 
			
		||||
        question2 = create_question(question_text="Past question 2.", days=-5)
 | 
			
		||||
        response = self.client.get(reverse('polls:index'))
 | 
			
		||||
        self.assertQuerysetEqual(
 | 
			
		||||
            response.context['latest_question_list'],
 | 
			
		||||
            [question2, question1],
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										11
									
								
								polls/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								polls/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from . import views
 | 
			
		||||
 | 
			
		||||
app_name="polls"
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', views.IndexView.as_view(), name='index'),
 | 
			
		||||
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
 | 
			
		||||
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
 | 
			
		||||
    path('<int:question_id>/vote/', views.vote, name='vote'),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										51
									
								
								polls/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								polls/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
from django.shortcuts import get_object_or_404, render
 | 
			
		||||
 | 
			
		||||
# Create your views here.
 | 
			
		||||
from django.http import HttpResponseRedirect
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.views import generic
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
 | 
			
		||||
from .models import Choice, Question
 | 
			
		||||
 | 
			
		||||
class IndexView(generic.ListView):
 | 
			
		||||
    template_name = 'polls/index.html'
 | 
			
		||||
    context_object_name = 'latest_question_list'
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return the last five published questions (not including those set to be
 | 
			
		||||
        published in the future).
 | 
			
		||||
        """
 | 
			
		||||
        return Question.objects.filter(
 | 
			
		||||
            pub_date__lte=timezone.now()
 | 
			
		||||
        ).order_by('-pub_date')[:5]
 | 
			
		||||
 | 
			
		||||
class DetailView(generic.DetailView):
 | 
			
		||||
    model = Question
 | 
			
		||||
    template_name = 'polls/detail.html'
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        """
 | 
			
		||||
        Excludes any questions that aren't published yet.
 | 
			
		||||
        """
 | 
			
		||||
        return Question.objects.filter(pub_date__lte=timezone.now())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResultsView(generic.DetailView):
 | 
			
		||||
    model = Question
 | 
			
		||||
    template_name = 'polls/results.html'
 | 
			
		||||
 | 
			
		||||
def vote(request, question_id):
 | 
			
		||||
    question = get_object_or_404(Question, pk=question_id)
 | 
			
		||||
    try:
 | 
			
		||||
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
 | 
			
		||||
    except (KeyError, Choice.DoesNotExist):
 | 
			
		||||
        return render(request, 'polls/detail.html', {
 | 
			
		||||
            'question': question,
 | 
			
		||||
            'error_message': "You didn't select a choice.",
 | 
			
		||||
        })
 | 
			
		||||
    else:
 | 
			
		||||
        selected_choice.votes += 1
 | 
			
		||||
        selected_choice.save()
 | 
			
		||||
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
 | 
			
		||||
							
								
								
									
										9
									
								
								templates/admin/base_site.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								templates/admin/base_site.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
{% extends "admin/base.html" %}
 | 
			
		||||
 | 
			
		||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block branding %}
 | 
			
		||||
<h1 id="site-name"><a href="{% url 'admin:index' %}">PankiPoll Admin</a></h1>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block nav-global %}{% endblock %}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user