|
@@ -1,216 +1,60 @@
|
|
|
## 静态资源和Ajax请求
|
|
## 静态资源和Ajax请求
|
|
|
|
|
|
|
|
-基于前面两个章节讲解的知识,我们已经可以使用Django框架来完成Web应用的开发了。接下来我们就尝试实现一个投票应用,具体的需求是用户进入应用首先查看到“学科介绍”页面,该页面显示了一个学校所开设的所有学科;通过点击某个学科,可以进入“老师介绍”页面,该页面展示了该学科所有老师的详细情况,可以在该页面上给老师点击“好评”或“差评”;如果用户没有登录,在投票时会先跳转到“登录页”要求用户登录,登录成功才能投票;对于未注册的用户,可以在“登录页”点击“新用户注册”进入“注册页”完成用户注册操作,注册成功后会跳转到“登录页”,注册失败会获得相应的提示信息。
|
|
|
|
|
-
|
|
|
|
|
-### 准备工作
|
|
|
|
|
-
|
|
|
|
|
-由于之前已经详细的讲解了如何创建Django项目以及项目的相关配置,因此我们略过这部分内容,唯一需要说明的是,从上面对投票应用需求的描述中我们可以分析出三个业务实体:学科、老师和用户。学科和老师之间通常是一对多关联关系(一个学科有多个老师,一个老师通常只属于一个学科),用户因为要给老师投票,所以跟老师之间是多对多关联关系(一个用户可以给多个老师投票,一个老师也可以收到多个用户的投票)。首先修改应用下的models.py文件来定义数据模型,先给出学科和老师的模型。
|
|
|
|
|
-
|
|
|
|
|
-```Python
|
|
|
|
|
-from django.db import models
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class Subject(models.Model):
|
|
|
|
|
- """学科"""
|
|
|
|
|
- no = models.IntegerField(primary_key=True, verbose_name='编号')
|
|
|
|
|
- name = models.CharField(max_length=20, verbose_name='名称')
|
|
|
|
|
- intro = models.CharField(max_length=511, default='', verbose_name='介绍')
|
|
|
|
|
- create_date = models.DateField(null=True, verbose_name='成立日期')
|
|
|
|
|
- is_hot = models.BooleanField(default=False, verbose_name='是否热门')
|
|
|
|
|
-
|
|
|
|
|
- def __str__(self):
|
|
|
|
|
- return self.name
|
|
|
|
|
-
|
|
|
|
|
- class Meta:
|
|
|
|
|
- db_table = 'tb_subject'
|
|
|
|
|
- verbose_name = '学科'
|
|
|
|
|
- verbose_name_plural = '学科'
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class Teacher(models.Model):
|
|
|
|
|
- """老师"""
|
|
|
|
|
- no = models.AutoField(primary_key=True, verbose_name='编号')
|
|
|
|
|
- name = models.CharField(max_length=20, verbose_name='姓名')
|
|
|
|
|
- detail = models.CharField(max_length=1023, default='', blank=True, verbose_name='详情')
|
|
|
|
|
- photo = models.CharField(max_length=1023, default='', verbose_name='照片')
|
|
|
|
|
- good_count = models.IntegerField(default=0, verbose_name='好评数')
|
|
|
|
|
- bad_count = models.IntegerField(default=0, verbose_name='差评数')
|
|
|
|
|
- subject = models.ForeignKey(to=Subject, on_delete=models.PROTECT, db_column='sno', verbose_name='所属学科')
|
|
|
|
|
-
|
|
|
|
|
- class Meta:
|
|
|
|
|
- db_table = 'tb_teacher'
|
|
|
|
|
- verbose_name = '老师'
|
|
|
|
|
- verbose_name_plural = '老师'
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-模型定义完成后,可以通过“生成迁移”和“执行迁移”来完成关系型数据库中二维表的创建,当然这需要提前启动数据库服务器并创建好对应的数据库,同时我们在项目中已经安装了PyMySQL而且完成了相应的配置,这些内容此处不再赘述。
|
|
|
|
|
-
|
|
|
|
|
-```Shell
|
|
|
|
|
-(venv)$ python manage.py makemigrations vote
|
|
|
|
|
-...
|
|
|
|
|
-(venv)$ python manage.py migrate
|
|
|
|
|
-...
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-> 注意:为了给vote应用生成迁移文件,需要修改Django项目settings.py文件,在INSTALLED_APPS中添加vote应用。
|
|
|
|
|
-
|
|
|
|
|
-完成模型迁移之后,我们可以直接使用Django提供的后台管理来添加学科和老师信息,这需要先注册模型类和模型管理类,可以通过修改``。
|
|
|
|
|
-
|
|
|
|
|
-```SQL
|
|
|
|
|
-from django.contrib import admin
|
|
|
|
|
-
|
|
|
|
|
-from poll2.forms import UserForm
|
|
|
|
|
-from poll2.models import Subject, Teacher
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class SubjectAdmin(admin.ModelAdmin):
|
|
|
|
|
- list_display = ('no', 'name', 'create_date', 'is_hot')
|
|
|
|
|
- ordering = ('no', )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TeacherAdmin(admin.ModelAdmin):
|
|
|
|
|
- list_display = ('no', 'name', 'detail', 'good_count', 'bad_count', 'subject')
|
|
|
|
|
- ordering = ('subject', 'no')
|
|
|
|
|
|
|
+### 加载静态资源
|
|
|
|
|
|
|
|
|
|
+如果要在Django项目中使用静态资源,可以先创建一个用于保存静态资源的目录。在`vote`项目中,我们将静态资源置于名为`static`的文件夹中,在该文件夹包含了三个子文件夹:css、js和images,分别用来保存外部CSS文件、外部JavaScript文件和图片资源,如下图所示。
|
|
|
|
|
|
|
|
-admin.site.register(Subject, SubjectAdmin)
|
|
|
|
|
-admin.site.register(Teacher, TeacherAdmin)
|
|
|
|
|
-```
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
-接下来,我们就可以修改views.py文件,通过编写视图函数先实现“学科介绍”页面。
|
|
|
|
|
|
|
+为了能够找到保存静态资源的文件夹,我们还需要修改Django项目的配置文件`settings.py`,如下所示:
|
|
|
|
|
|
|
|
```Python
|
|
```Python
|
|
|
-def show_subjects(request):
|
|
|
|
|
- """查看所有学科"""
|
|
|
|
|
- subjects = Subject.objects.all()
|
|
|
|
|
- return render(request, 'subject.html', {'subjects': subjects})
|
|
|
|
|
|
|
+STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
|
|
|
|
|
+STATIC_URL = '/static/'
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-至此,我们还需要一个模板页,模板的配置以及模板页中模板语言的用法在之前已经进行过简要的介绍,如果不熟悉可以看看下面的代码,相信这并不是一件困难的事情。
|
|
|
|
|
-
|
|
|
|
|
-```HTML
|
|
|
|
|
-<!DOCTYPE html>
|
|
|
|
|
-<html lang="en">
|
|
|
|
|
-<head>
|
|
|
|
|
- <meta charset="UTF-8">
|
|
|
|
|
- <title>所有学科信息</title>
|
|
|
|
|
- <style>/* 此处略去了层叠样式表的选择器 */</style>
|
|
|
|
|
-</head>
|
|
|
|
|
-<body>
|
|
|
|
|
- <h1>所有学科</h1>
|
|
|
|
|
- <hr>
|
|
|
|
|
- {% for subject in subjects %}
|
|
|
|
|
- <div>
|
|
|
|
|
- <h3>
|
|
|
|
|
- <a href="/teachers/?sno={{ subject.no }}">{{ subject.name }}</a>
|
|
|
|
|
- {% if subject.is_hot %}
|
|
|
|
|
- <img src="/static/images/hot.png" width="32" alt="">
|
|
|
|
|
- {% endif %}
|
|
|
|
|
- </h3>
|
|
|
|
|
- <p>{{ subject.intro }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- {% endfor %}
|
|
|
|
|
-</body>
|
|
|
|
|
-</html>
|
|
|
|
|
-```
|
|
|
|
|
|
|
+配置好静态资源之后,大家可以运行项目,然后看看之前我们写的页面上的图片是否能够正常加载出来。需要说明的是,在项目正式部署到线上环境后,我们通常会把静态资源交给专门的静态资源服务器(如Nginx、Apache)来处理,而不是有运行Python代码的服务器来管理静态资源,所以上面的配置并不适用于生产环境,仅供项目开发阶段测试使用。使用静态资源的正确姿势我们会在后续的章节为大家讲解。
|
|
|
|
|
|
|
|
-在上面的模板中,我们为每个学科添加了一个超链接,点击超链接可以查看该学科的讲师信息,为此需要再编写一个视图函数来处理查看指定学科老师信息。
|
|
|
|
|
|
|
+### Ajax概述
|
|
|
|
|
|
|
|
-```Python
|
|
|
|
|
-def show_teachers(request):
|
|
|
|
|
- """显示指定学科的老师"""
|
|
|
|
|
- try:
|
|
|
|
|
- sno = int(request.GET['sno'])
|
|
|
|
|
- subject = Subject.objects.get(no=sno)
|
|
|
|
|
- teachers = subject.teacher_set.all()
|
|
|
|
|
- return render(request, 'teachers.html', {'subject': subject, 'teachers': teachers})
|
|
|
|
|
- except (KeyError, ValueError, Subject.DoesNotExist):
|
|
|
|
|
- return redirect('/')
|
|
|
|
|
-```
|
|
|
|
|
|
|
+接下来就可以实现“好评”和“差评”的功能了,很明显如果能够在不刷新页面的情况下实现这两个功能会带来更好的用户体验,因此我们考虑使用[Ajax](https://zh.wikipedia.org/wiki/AJAX)技术来实现“好评”和“差评”。Ajax是Asynchronous Javascript And XML的缩写 , 简单的说,使用Ajax技术可以在不重新加载整个页面的情况下对页面进行局部刷新。
|
|
|
|
|
|
|
|
-显示老师信息的模板页。
|
|
|
|
|
|
|
+对于传统的Web应用,每次页面上需要加载新的内容都需要重新请求服务器并刷新整个页面,如果服务器短时间内无法给予响应或者网络状况并不理想,那么可能会造成浏览器长时间的空白并使得用户处于等待状态,在这个期间用户什么都做不了,如下图所示。很显然,这样的Web应用并不能带来很好的用户体验。
|
|
|
|
|
|
|
|
-```HTML
|
|
|
|
|
-<!DOCTYPE html>
|
|
|
|
|
-{% load static %}
|
|
|
|
|
-<html lang="en">
|
|
|
|
|
-<head>
|
|
|
|
|
- <meta charset="UTF-8">
|
|
|
|
|
- <title>老师</title>
|
|
|
|
|
- <style>/* 此处略去了层叠样式表的选择器 */</style>
|
|
|
|
|
-</head>
|
|
|
|
|
-<body>
|
|
|
|
|
- <h1>{{ subject.name }}学科老师信息</h1>
|
|
|
|
|
- <hr>
|
|
|
|
|
- {% if teachers %}
|
|
|
|
|
- {% for teacher in teachers %}
|
|
|
|
|
- <div>
|
|
|
|
|
- <div>
|
|
|
|
|
- <img src="{% static teacher.photo %}" alt="">
|
|
|
|
|
- </div>
|
|
|
|
|
- <div>
|
|
|
|
|
- <h3>{{ teacher.name }}</h3>
|
|
|
|
|
- <p>{{ teacher.detail }}</p>
|
|
|
|
|
- <p class="comment">
|
|
|
|
|
- <a href="">好评</a>
|
|
|
|
|
- (<span>{{ teacher.good_count }}</span>)
|
|
|
|
|
- <a href="">差评</a>
|
|
|
|
|
- (<span>{{ teacher.bad_count }}</span>)
|
|
|
|
|
- </p>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- {% endfor %}
|
|
|
|
|
- {% else %}
|
|
|
|
|
- <h3>暂时没有该学科的老师信息</h3>
|
|
|
|
|
- {% endif %}
|
|
|
|
|
- <p>
|
|
|
|
|
- <a href="/">返回首页</a>
|
|
|
|
|
- </p>
|
|
|
|
|
-</body>
|
|
|
|
|
-</html>
|
|
|
|
|
-```
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
-### 加载静态资源
|
|
|
|
|
|
|
+对于使用Ajax技术的Web应用,浏览器可以向服务器发起异步请求来获取数据。异步请求不会中断用户体验,当服务器返回了新的数据,我们可以通过JavaScript代码进行DOM操作来实现对页面的局部刷新,这样就相当于在不刷新整个页面的情况下更新了页面的内容,如下图所示。
|
|
|
|
|
|
|
|
-在上面的模板页面中,我们使用了`<img>`标签来加载老师的照片,其中使用了引用静态资源的模板指令`{% static %}`,要使用该指令,首先要使用`{% load static %}`指令来加载静态资源,我们将这段代码放在了页码开始的位置。在上面的项目中,我们将静态资源置于名为static的文件夹中,在该文件夹下又创建了三个文件夹:css、js和images,分别用来保存外部层叠样式表、外部JavaScript文件和图片资源。为了能够找到保存静态资源的文件夹,我们还需要修改Django项目的配置文件settings.py,如下所示:
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
-```Python
|
|
|
|
|
-# 此处省略上面的代码
|
|
|
|
|
|
|
+在使用Ajax技术时,浏览器跟服务器通常会交换XML或JSON格式的数据,XML是以前使用得非常多的一种数据格式,近年来几乎已经完全被JSON取代,下面是两种数据格式的对比。
|
|
|
|
|
|
|
|
-STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
|
|
|
|
|
-STATIC_URL = '/static/'
|
|
|
|
|
|
|
+XML格式:
|
|
|
|
|
|
|
|
-# 此处省略下面的代码
|
|
|
|
|
|
|
+```XML
|
|
|
|
|
+<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
|
+<message>
|
|
|
|
|
+ <from>Alice</from>
|
|
|
|
|
+ <to>Bob</to>
|
|
|
|
|
+ <content>Dinner is on me!</content>
|
|
|
|
|
+</message>
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-接下来修改urls.py文件,配置用户请求的URL和视图函数的对应关系。
|
|
|
|
|
-
|
|
|
|
|
-```Python
|
|
|
|
|
-from django.contrib import admin
|
|
|
|
|
-from django.urls import path
|
|
|
|
|
-
|
|
|
|
|
-from vote import views
|
|
|
|
|
|
|
+JSON格式:
|
|
|
|
|
|
|
|
-urlpatterns = [
|
|
|
|
|
- path('', views.show_subjects),
|
|
|
|
|
- path('teachers/', views.show_teachers),
|
|
|
|
|
- path('admin/', admin.site.urls),
|
|
|
|
|
-]
|
|
|
|
|
|
|
+```JSON
|
|
|
|
|
+{
|
|
|
|
|
+ "from": "Alice",
|
|
|
|
|
+ "to": "Bob",
|
|
|
|
|
+ "content": "Dinner is on me!"
|
|
|
|
|
+}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-启动服务器运行项目,进入首页查看学科信息。
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-点击学科查看老师信息。
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+通过上面的对比,明显JSON格式的数据要紧凑得多,所以传输效率更高,而且JSON本身也是JavaScript中的一种对象表达式语法,在JavaScript代码中处理JSON格式的数据更加方便。
|
|
|
|
|
|
|
|
-### Ajax请求
|
|
|
|
|
|
|
+### 用Ajax实现投票功能
|
|
|
|
|
|
|
|
-接下来就可以实现“好评”和“差评”的功能了,很明显如果能够在不刷新页面的情况下实现这两个功能会带来更好的用户体验,因此我们考虑使用[Ajax](https://zh.wikipedia.org/wiki/AJAX)技术来实现“好评”和“差评”,Ajax技术我们在Web前端部分已经介绍过了,此处不再赘述。
|
|
|
|
|
-
|
|
|
|
|
-首先修改项目的urls.py文件,为“好评”和“差评”功能映射对应的URL。
|
|
|
|
|
|
|
+下面,我们使用Ajax技术来实现投票的功能,首先修改项目的`urls.py`文件,为“好评”和“差评”功能映射对应的URL。
|
|
|
|
|
|
|
|
```Python
|
|
```Python
|
|
|
from django.contrib import admin
|
|
from django.contrib import admin
|
|
@@ -233,16 +77,18 @@ urlpatterns = [
|
|
|
def praise_or_criticize(request):
|
|
def praise_or_criticize(request):
|
|
|
"""好评"""
|
|
"""好评"""
|
|
|
try:
|
|
try:
|
|
|
- tno = int(request.GET['tno'])
|
|
|
|
|
|
|
+ tno = int(request.GET.get('tno'))
|
|
|
teacher = Teacher.objects.get(no=tno)
|
|
teacher = Teacher.objects.get(no=tno)
|
|
|
if request.path.startswith('/praise'):
|
|
if request.path.startswith('/praise'):
|
|
|
teacher.good_count += 1
|
|
teacher.good_count += 1
|
|
|
|
|
+ count = teacher.good_count
|
|
|
else:
|
|
else:
|
|
|
teacher.bad_count += 1
|
|
teacher.bad_count += 1
|
|
|
|
|
+ count = teacher.bad_count
|
|
|
teacher.save()
|
|
teacher.save()
|
|
|
- data = {'code': 200, 'hint': '操作成功'}
|
|
|
|
|
- except (KeyError, ValueError, Teacher.DoseNotExist):
|
|
|
|
|
- data = {'code': 404, 'hint': '操作失败'}
|
|
|
|
|
|
|
+ data = {'code': 20000, 'mesg': '操作成功', 'count': count}
|
|
|
|
|
+ except (ValueError, Teacher.DoseNotExist):
|
|
|
|
|
+ data = {'code': 20001, 'mesg': '操作失败'}
|
|
|
return JsonResponse(data)
|
|
return JsonResponse(data)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
@@ -250,54 +96,90 @@ def praise_or_criticize(request):
|
|
|
|
|
|
|
|
```HTML
|
|
```HTML
|
|
|
<!DOCTYPE html>
|
|
<!DOCTYPE html>
|
|
|
-{% load static %}
|
|
|
|
|
<html lang="en">
|
|
<html lang="en">
|
|
|
<head>
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
<meta charset="UTF-8">
|
|
|
- <title>老师</title>
|
|
|
|
|
- <style>/* 此处略去了层叠样式表的选择器 */</style>
|
|
|
|
|
|
|
+ <title>老师信息</title>
|
|
|
|
|
+ <style>
|
|
|
|
|
+ #container {
|
|
|
|
|
+ width: 80%;
|
|
|
|
|
+ margin: 10px auto;
|
|
|
|
|
+ }
|
|
|
|
|
+ .teacher {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ margin: 0 auto;
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+ border-bottom: 1px dashed gray;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+ .teacher>div {
|
|
|
|
|
+ float: left;
|
|
|
|
|
+ }
|
|
|
|
|
+ .photo {
|
|
|
|
|
+ height: 140px;
|
|
|
|
|
+ border-radius: 75px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ margin-left: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .info {
|
|
|
|
|
+ width: 75%;
|
|
|
|
|
+ margin-left: 30px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .info div {
|
|
|
|
|
+ clear: both;
|
|
|
|
|
+ margin: 5px 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .info span {
|
|
|
|
|
+ margin-right: 25px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .info a {
|
|
|
|
|
+ text-decoration: none;
|
|
|
|
|
+ color: darkcyan;
|
|
|
|
|
+ }
|
|
|
|
|
+ </style>
|
|
|
</head>
|
|
</head>
|
|
|
<body>
|
|
<body>
|
|
|
- <h1>{{ subject.name }}学科老师信息</h1>
|
|
|
|
|
- <hr>
|
|
|
|
|
- {% if teachers %}
|
|
|
|
|
- {% for teacher in teachers %}
|
|
|
|
|
- <div class="teacher">
|
|
|
|
|
- <div class="photo">
|
|
|
|
|
- <img src="{% static teacher.photo %}" height="140" alt="">
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="info">
|
|
|
|
|
- <h3>{{ teacher.name }}</h3>
|
|
|
|
|
- <p>{{ teacher.detail }}</p>
|
|
|
|
|
- <p class="comment">
|
|
|
|
|
- <a href="/praise/?tno={{ teacher.no }}">好评</a>
|
|
|
|
|
- (<span>{{ teacher.good_count }}</span>)
|
|
|
|
|
-
|
|
|
|
|
- <a href="/criticize/?tno={{ teacher.no }}">差评</a>
|
|
|
|
|
- (<span>{{ teacher.bad_count }}</span>)
|
|
|
|
|
- </p>
|
|
|
|
|
|
|
+ <div id="container">
|
|
|
|
|
+ <h1>{{ subject.name }}学科的老师信息</h1>
|
|
|
|
|
+ <hr>
|
|
|
|
|
+ {% if not teachers %}
|
|
|
|
|
+ <h2>暂无该学科老师信息</h2>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ {% for teacher in teachers %}
|
|
|
|
|
+ <div class="teacher">
|
|
|
|
|
+ <div class="photo">
|
|
|
|
|
+ <img src="/static/images/{{ teacher.photo }}" height="140" alt="">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="info">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span><strong>姓名:{{ teacher.name }}</strong></span>
|
|
|
|
|
+ <span>性别:{{ teacher.sex | yesno:'男,女' }}</span>
|
|
|
|
|
+ <span>出生日期:{{ teacher.birth }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="intro">{{ teacher.intro }}</div>
|
|
|
|
|
+ <div class="comment">
|
|
|
|
|
+ <a href="/praise/?tno={{ teacher.no }}">好评</a>
|
|
|
|
|
+ (<strong>{{ teacher.good_count }}</strong>)
|
|
|
|
|
+
|
|
|
|
|
+ <a href="/criticize/?tno={{ teacher.no }}">差评</a>
|
|
|
|
|
+ (<strong>{{ teacher.bad_count }}</strong>)
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- {% endfor %}
|
|
|
|
|
- {% else %}
|
|
|
|
|
- <h3>暂时没有该学科的老师信息</h3>
|
|
|
|
|
- {% endif %}
|
|
|
|
|
- <p>
|
|
|
|
|
|
|
+ {% endfor %}
|
|
|
<a href="/">返回首页</a>
|
|
<a href="/">返回首页</a>
|
|
|
- </p>
|
|
|
|
|
|
|
+ </div>
|
|
|
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
|
|
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
|
|
|
<script>
|
|
<script>
|
|
|
$(() => {
|
|
$(() => {
|
|
|
$('.comment>a').on('click', (evt) => {
|
|
$('.comment>a').on('click', (evt) => {
|
|
|
evt.preventDefault()
|
|
evt.preventDefault()
|
|
|
- let anchor = $(evt.target)
|
|
|
|
|
- let url = anchor.attr('href')
|
|
|
|
|
|
|
+ let url = $(evt.target).attr('href')
|
|
|
$.getJSON(url, (json) => {
|
|
$.getJSON(url, (json) => {
|
|
|
- if (json.code == 10001) {
|
|
|
|
|
- let span = anchor.next()
|
|
|
|
|
- span.text(parseInt(span.text()) + 1)
|
|
|
|
|
|
|
+ if (json.code == 20000) {
|
|
|
|
|
+ $(evt.target).next().text(json.count)
|
|
|
} else {
|
|
} else {
|
|
|
- alert(json.hint)
|
|
|
|
|
|
|
+ alert(json.mesg)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
@@ -307,6 +189,8 @@ def praise_or_criticize(request):
|
|
|
</html>
|
|
</html>
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+上面的前端代码中,使用了jQuery库封装的`getJSON`方法向服务器发送异步请求,如果不熟悉前端的jQuery库,可以参考[《jQuery API手册》](https://www.runoob.com/manual/jquery/)。
|
|
|
|
|
+
|
|
|
### 小结
|
|
### 小结
|
|
|
|
|
|
|
|
到此为止,这个投票项目的核心功能已然完成,在下面的章节中我们会要求用户必须登录才能投票,没有账号的用户可以通过注册功能注册一个账号。
|
|
到此为止,这个投票项目的核心功能已然完成,在下面的章节中我们会要求用户必须登录才能投票,没有账号的用户可以通过注册功能注册一个账号。
|