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 # 应用数据库迁移

 

posted @ 2021-05-27 18:32  豆浆D  阅读(199)  评论(0编辑  收藏  举报