Django开发一个投票应用
一、目标:前端 + 后端 + 静态文件
前端:
首页:简单展示一个列表页面
投票页:有投票选项,投票按钮
结果页:展示投票后各个投票选项的投票数
后端
admin:增删改查投票选项,能够按照时间排序投票选项,能够增删改查用户。
静态文件
css样式引入,a标签链接颜色修改,body的background设置为图片
目录结构
详细目录结构
二、前端展示效果
首页
投票页
结果页
三、后端admin
首页
按时间排序投票选项
增删改查某一投票选项
用户的增删改查
四、具体实现
- 实现一个queckstart(见前一篇)
- 连接数据库并建立models模型
- views视图函数实现
- teplates各个页面模板页面实现
- urls注册views函数
- 静态文件渲染修饰
- 配置admin.py
- 自定义admin的模板文件
- 主项目目录修改settings
- 启动应用
连接数据库并建立models模型
主项目项目settings.py配置数据库,我这里直接用的Django自带的sqlite3,配置如下
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': str(BASE_DIR / 'db.sqlite3'), } }
注意:'NAME': str(BASE_DIR / 'db.sqlite3') 这里的str,不加上会提示类型不对。
投票应用polls建立models模型
import datetime from django.utils import timezone from django.db import models 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') # @admin.display( # boolean=True, # ordering='pub_date', # description='Published recently?', # ) # 自定义判断是否最近一天发布方法(返回True或Flase) def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now # 自定义对象返回的魔术方法,对象返回question_text def __str__(self): return self.question_text # 同__str__ __repr__ = __str__ class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) # 关系键,上面的Question表中的具体对象(记录)会多一个choice_set的属性 choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text __repr__ = __str__
views视图函数实现
from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from django.urls import path 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.""" return Question.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Question template_name = 'polls/detail.html' 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']) selected_choice.votes += 1 selected_choice.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) # ----------------------------------------------------------------------------下面这版更好理解 # from django.shortcuts import render, get_object_or_404, get_list_or_404 # from django.template import loader # from django.http import HttpResponse, Http404, HttpResponseRedirect # from .models import Question, Choice # from django.urls import reverse # # Create your views here. # # def index(request): # latest_question_list = get_list_or_404(Question)[: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']) # selected_choice.votes += 1 # selected_choice.save() # return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) # except (KeyError, Choice.DoesNotExist): # return render(request, 'polls/detail.html', { # 'question': question, # 'error_message': "You didn't select a choice.", # })
teplates各个页面模板页面实现
index页面(首页)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> {% load static %} <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}"> </head> <body> {% 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 %} </body> </html>
detail页面(投票页面)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>detail</title> </head> <body> <h1>{{ question.question_text }}</h1> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% 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 %} <input type="submit" value="Vote"> </form> </body> </html>
results页面(投票结果页面)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>results</title> </head> <body> <h1>{{ question.question_test }}</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> </body> </html>
urls注册views函数
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'), ] # --------------------------------------------------------------------下面一版更好理解 # from django.urls import path # from . import views # # app_name = 'polls' # urlpatterns = [ # path('', views.index, name='index'), # path('<int:question_id>/', views.detail, name='detail'), # path('<int:question_id>/results/', views.results, name='results'), # path('<int:question_id>/vote/', views.vote, name='vote'), # ]
静态文件渲染修饰style.css
li a{ color: green; } body { background: white url("images/background.gif") no-repeat; }
配置admin.py
from django.contrib import admin from .models import Question, Choice # Register your models here. class ChoiceInline(admin.TabularInline): model = Choice extra = 3 # class ChoiceInline(admin.StackedInline): #StackedInline为更加详细的choice展现形式 # 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'] admin.site.register(Question, QuestionAdmin)
自定义admin的模板文件(django/contrib/admin/templates/admin/base_site.html复制到 主项目目录/templates/admin/base_site.html,然后修改如下)
{% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1> {% endblock %}
主项目目录settings修改TEMPLATES
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', ], }, }, ]
启动应用
python manage.py runserver
一些坑:
1. polls投票应用投票时候,总是被异常捕获,进入不了投票结果页results.html
后来发现是一个低级错误,由于这里收不到POST请求,所以就一直被捕获KeyError
2.静态文件渲染那里,一直刷新是不会有效果的,一定要重启应用。(修改配置py等文件,应用会自动帮你重启,静态文件修改应用不会帮你自动重启)
ORM数据库迁移
python manage.py makemigrations #为模型的改变生成迁移文件
python manage.py migrate # 应用数据库迁移