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