diff --git a/djtut/Pipfile b/djtut/Pipfile index 57036ec..017ca0a 100644 --- a/djtut/Pipfile +++ b/djtut/Pipfile @@ -5,3 +5,7 @@ verify_ssl = true [packages] Django = "<2.0" +psycopg2 = "<2.8" + +[requires] +python_version = "3.7" diff --git a/djtut/Pipfile.lock b/djtut/Pipfile.lock index 9c9bdbe..f1f1e9d 100644 --- a/djtut/Pipfile.lock +++ b/djtut/Pipfile.lock @@ -1,10 +1,12 @@ { "_meta": { "hash": { - "sha256": "b7536124d192e59fcfecb3a2ff5163f40244f4d6684bb2d1411c86b64132e6af" + "sha256": "091688c1bd388439552d0e26d333666867899615bbcafabf614f67f1466fd51d" }, "pipfile-spec": 6, - "requires": {}, + "requires": { + "python_version": "3.7" + }, "sources": [ { "name": "pypi", @@ -22,6 +24,42 @@ "index": "pypi", "version": "==1.11.29" }, + "psycopg2": { + "hashes": [ + "sha256:02445ebbb3a11a3fe8202c413d5e6faf38bb75b4e336203ee144ca2c46529f94", + "sha256:0e9873e60f98f0c52339abf8f0339d1e22bfe5aae0bcf7aabd40c055175035ec", + "sha256:1148a5eb29073280bf9057c7fc45468592c1bb75a28f6df1591adb93c8cb63d0", + "sha256:259a8324e109d4922b0fcd046e223e289830e2568d6f4132a3702439e5fd532b", + "sha256:28dffa9ed4595429e61bacac41d3f9671bb613d1442ff43bcbec63d4f73ed5e8", + "sha256:314a74302d4737a3865d40ea50e430ce1543c921ba10f39d562e807cfe2edf2a", + "sha256:36b60201b6d215d7658a71493fdf6bd5e60ad9a0cffed39906627ff9f4f3afd3", + "sha256:3f9d532bce54c4234161176ff3b8688ff337575ca441ea27597e112dfcd0ee0c", + "sha256:5d222983847b40af989ad96c07fc3f07e47925e463baa5de716be8f805b41d9b", + "sha256:6757a6d2fc58f7d8f5d471ad180a0bd7b4dd3c7d681f051504fbea7ae29c8d6f", + "sha256:6a0e0f1e74edb0ab57d89680e59e7bfefad2bfbdf7c80eb38304d897d43674bb", + "sha256:6ca703ccdf734e886a1cf53eb702261110f6a8b0ed74bcad15f1399f74d3f189", + "sha256:8513b953d8f443c446aa79a4cc8a898bd415fc5e29349054f03a7d696d495542", + "sha256:9262a5ce2038570cb81b4d6413720484cb1bc52c064b2f36228d735b1f98b794", + "sha256:97441f851d862a0c844d981cbee7ee62566c322ebb3d68f86d66aa99d483985b", + "sha256:a07feade155eb8e69b54dd6774cf6acf2d936660c61d8123b8b6b1f9247b67d6", + "sha256:a9b9c02c91b1e3ec1f1886b2d0a90a0ea07cc529cb7e6e472b556bc20ce658f3", + "sha256:ae88216f94728d691b945983140bf40d51a1ff6c7fe57def93949bf9339ed54a", + "sha256:b360ffd17659491f1a6ad7c928350e229c7b7bd83a2b922b6ee541245c7a776f", + "sha256:b4221957ceccf14b2abdabef42d806e791350be10e21b260d7c9ce49012cc19e", + "sha256:b90758e49d5e6b152a460d10b92f8a6ccf318fcc0ee814dcf53f3a6fc5328789", + "sha256:c669ea986190ed05fb289d0c100cc88064351f2b85177cbfd3564c4f4847d18c", + "sha256:d1b61999d15c79cf7f4f7cc9021477aef35277fc52452cf50fd13b713c84424d", + "sha256:de7bb043d1adaaf46e38d47e7a5f703bb3dab01376111e522b07d25e1a79c1e1", + "sha256:e393568e288d884b94d263f2669215197840d097c7e5b0acd1a51c1ea7d1aba8", + "sha256:ed7e0849337bd37d89f2c2b0216a0de863399ee5d363d31b1e5330a99044737b", + "sha256:f153f71c3164665d269a5d03c7fa76ba675c7a8de9dc09a4e2c2cdc9936a7b41", + "sha256:f1fb5a8427af099beb7f65093cbdb52e021b8e6dbdfaf020402a623f4181baf5", + "sha256:f36b333e9f86a2fba960c72b90c34be6ca71819e300f7b1fc3d2b0f0b2c546cd", + "sha256:f4526d078aedd5187d0508aa5f9a01eae6a48a470ed678406da94b4cd6524b7e" + ], + "index": "pypi", + "version": "==2.7.7" + }, "pytz": { "hashes": [ "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", diff --git a/djtut/mysite/mysite/settings.py b/djtut/mysite/mysite/settings.py index fa57479..3eb36fe 100644 --- a/djtut/mysite/mysite/settings.py +++ b/djtut/mysite/mysite/settings.py @@ -1,120 +1,65 @@ -""" -Django settings for mysite project. - -Generated by 'django-admin startproject' using Django 1.11.29. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.11/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/1.11/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'rbkosjwn!o^)&kb-fr^3^+y60bbz^76f#jt_7a%n1pt8jjd%bv' - -# SECURITY WARNING: don't run with debug turned on in production! +SECRET_KEY = "rbkosjwn!o^)&kb-fr^3^+y60bbz^76f#jt_7a%n1pt8jjd%bv" DEBUG = True - ALLOWED_HOSTS = [] - - -# Application definition - INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "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', + "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' - +ROOT_URLCONF = "mysite.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - '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', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "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/1.11/ref/settings/#databases - +WSGI_APPLICATION = "mysite.wsgi.application" DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "mysite", + "USER": "mysite", + "HOST": "localhost", + "PORT": "5432", + "PASSWORD": "notsecurelol", } } - - -# Password validation -# https://docs.djangoproject.com/en/1.11/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', + "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/1.11/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" USE_I18N = True - USE_L10N = True - USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.11/howto/static-files/ - -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/djtut/mysite/mysite/urls.py b/djtut/mysite/mysite/urls.py index b58234f..e9d0e30 100644 --- a/djtut/mysite/mysite/urls.py +++ b/djtut/mysite/mysite/urls.py @@ -1,21 +1,7 @@ -"""mysite URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf.urls import url +from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ - url(r'^admin/', admin.site.urls), + url("^polls/", include("polls.urls")), + url("^admin/", admin.site.urls), ] diff --git a/djtut/mysite/polls/__init__.py b/djtut/mysite/polls/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djtut/mysite/polls/admin.py b/djtut/mysite/polls/admin.py new file mode 100644 index 0000000..6af8ff6 --- /dev/null +++ b/djtut/mysite/polls/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +from .models import Question + +admin.site.register(Question) diff --git a/djtut/mysite/polls/apps.py b/djtut/mysite/polls/apps.py new file mode 100644 index 0000000..292f00d --- /dev/null +++ b/djtut/mysite/polls/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PollsConfig(AppConfig): + name = "polls" diff --git a/djtut/mysite/polls/migrations/0001_initial.py b/djtut/mysite/polls/migrations/0001_initial.py new file mode 100644 index 0000000..9c9bdfd --- /dev/null +++ b/djtut/mysite/polls/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-03-14 01:39 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + 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)), + ], + ), + 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.AddField( + model_name='choice', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Question'), + ), + ] diff --git a/djtut/mysite/polls/migrations/__init__.py b/djtut/mysite/polls/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djtut/mysite/polls/models.py b/djtut/mysite/polls/models.py new file mode 100644 index 0000000..380716f --- /dev/null +++ b/djtut/mysite/polls/models.py @@ -0,0 +1,12 @@ +from django.db import models + + +class Question(models.Model): + question_text = models.CharField(max_length=200) + pub_date = models.DateTimeField("date published") + + +class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) diff --git a/djtut/mysite/polls/templates/polls/detail.html b/djtut/mysite/polls/templates/polls/detail.html new file mode 100644 index 0000000..966e015 --- /dev/null +++ b/djtut/mysite/polls/templates/polls/detail.html @@ -0,0 +1,12 @@ +

{{ question.question_text }}

+ +{% if error_message %}

{{ error_mmessage }}

{% endif %} + +
+ {% csrf_token %} + {% for choice in question.choice_set.all %} + +
+ {% endfor %} + +
diff --git a/djtut/mysite/polls/templates/polls/index.html b/djtut/mysite/polls/templates/polls/index.html new file mode 100644 index 0000000..46f1a35 --- /dev/null +++ b/djtut/mysite/polls/templates/polls/index.html @@ -0,0 +1,9 @@ +{% if latest_question_list %} + +{% else %} +

No polls are available.

+{% endif %} diff --git a/djtut/mysite/polls/templates/polls/results.html b/djtut/mysite/polls/templates/polls/results.html new file mode 100644 index 0000000..4d20005 --- /dev/null +++ b/djtut/mysite/polls/templates/polls/results.html @@ -0,0 +1,9 @@ +

{{ question.question_text }}

+ + + +Vote again? diff --git a/djtut/mysite/polls/tests.py b/djtut/mysite/polls/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/djtut/mysite/polls/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/djtut/mysite/polls/urls.py b/djtut/mysite/polls/urls.py new file mode 100644 index 0000000..54c3961 --- /dev/null +++ b/djtut/mysite/polls/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url + +from . import views + +app_name = "polls" +urlpatterns = [ + url("^$", views.index, name="index"), + url("^(?P[0-9]+)/$", views.detail, name="detail"), + url("^(?P[0-9]+)/results/$", views.results, name="results"), + url("^(?P[0-9]+)/vote/$", views.vote, name="vote"), +] diff --git a/djtut/mysite/polls/views.py b/djtut/mysite/polls/views.py new file mode 100644 index 0000000..4e7d5d6 --- /dev/null +++ b/djtut/mysite/polls/views.py @@ -0,0 +1,39 @@ +from django.shortcuts import render, get_object_or_404 +from django.http import HttpResponseRedirect, HttpResponse +from django.urls import reverse + +from .models import Question + + +def index(request): + latest_question_list = Question.objects.order_by("-pub_date")[:5] + context = { + "latest_question_list": latest_question_list, + } + return render(request, "polls/index.html", context) + + +def detail(request, question_id): + question = get_object_or_404(Question, pk=question_id) + return render(request, "polls/detail.html", {"question": question}) + + +def results(request, question_id): + question = get_object_or_404(Question, pk=question_id) + return render(request, "polls/results.html", {"question": question}) + + +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/vote.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,)))