前后端交互-简单示例

在实现了第一个页面,也就是登录页面之后,如果只是简单的根据请求来发送HTML(包含CSS和JS),也可以做出一个不错的静态网站出来,比如展示图片,轮播等,HTML内跳转的链接也可以用Django来捕获然后返回相应的HTML文件.然而不包含交互的页面,谈不上是web应用.而且编写静态网站,也无需使用Django,只需要编写一系列HTML文件即可.使用Django的关键是实现前后端交互,以此来开发web站点.

接着昨天的第一个登陆页面,里边有form表单,form表单可以指定提交的url,正好这个url可以用django来接受,基于此,来编写一个登陆的简单后端处理,实现第一次前后端交互.

首先,必须重新看一下HTML文件里的form表单,给其action属性设置一个url地址,然后指定请求为post.之后还需要给input标签加上name属性(应该猜到了,是要向后端返回一个字典)

<form class="form-horizontal" action="/auth/" method="post">
<input type="email" class="form-control" id="email" placeholder="Enter email" name="username">
<input type="password" class="form-control" id="pwd" placeholder="Enter password" name="password">

然后,由于url是/auth/,还需要在Django里给这个url编写一个函数,并且在urlpatterns里新增对应关系:

def auth(request):
    # 获取用户提交的数据
    print(request.POST)
    return HttpResponse("testing....")

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', index_html),
    url(r'^auth/', auth),
]

由于每个函数都会将response作为参数,而我们知道response对象里保存着收到的所有HTTP请求,这个时候先来打印一下POST看看.
刷新页面,填入用户名和密码然后按提交.可以看到返回了testing…字样给前端,后端里打印出了form提交的内容:

<QueryDict: {'username': ['jenny@sina.com'], 'password': ['conyli']}>

这是一个字典(其实是一个django.http.request.QueryDict对象,但是和字典有类似的操作),试着来取出用户名和密码的内容,修改一下auth函数:

def auth(request):
    # 获取用户提交的数据
    username = request.POST.get("username", None)
    password = request.POST.get("password", None)
    print(username, password)
    return HttpResponse("I get the data")

通过查看结果,发现这个get方法和原生字典还是有一些区别的,直接拿到了用户名和密码对应的字符串.剩下就是判断了,继续修改auth函数:

def auth(request):
    # 获取用户提交的数据
    username = request.POST.get("username", None)
    password = request.POST.get("password", None)
    # 这里其实应该到数据库里去验证用户名和密码,但是目前为了简便,先写最简单的逻辑
    if username == "jenny@sina.com" and password == "conyli":
        return HttpResponse("登录成功")
    else:
        return HttpResponse("登录失败")

刷新页面再尝试登陆,可以发现,用指定的用户名和密码可以得到登录成功的返回消息,而其他的都登录失败.当然,平时看到的登录界面无论成功还是失败以后,不会像我们这样返回一个简单的提示.但是不管如何,已经实现了一个最简单的前后端交互.在继续往下之前,先要了解一下GET请求和POST请求.

GET和POST请求

GET请求:从服务器请求HTML页面,搜索引擎检索.
POST请求:是先向服务器发送一些数据,然后等待服务器的响应,比如登陆,注册,提交表单内容.

前后端交互-整合登录功能

知道了请求之后,思路就可以进一步扩展.目前我们的auth是用来验证post请求,而login是用来返回登陆界面,有没有可能将所有的登录功能以较低的耦合程度集中到一个函数内.答案是只需要判断请求的种类,就可以根据请求的种类返回不同类型的内容.重新整理一下函数和对应关系:

def login(request):
    if request.method == "GET":
        return render(request, 'test.html')
    elif request.method == "POST":
        username = request.POST.get("username", None)
        password = request.POST.get("password", None)
        print(username, password)
        if username == "jenny@sina.com" and password == "conyli":
            return HttpResponse("登录成功")
        else:
            return HttpResponse("登录失败")


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', login),
]

注意,form表单的action属性如果留空,默认就是往当前url提交,所以这里也无需再弄一个auth出来,只需要修改form表单的action属性使其留空,就让post请求往/login/提交.之后可以发现,刷新页面出来的始终是登录页面.而填入内容提交之后,就显示登录成功或者失败.已经接近了正常的登录页面效果.

render字符串替换

继续分析这个页面,在登录成功的时候,要做的动作暂时不考虑,但是登录失败的时候返回一个字符串看来不妥,应该提示用户用户名或者密码错误,然后还是在页面内进行操作.
所以在登录失败的时候依然返回这个登录页面,但是需在在某个地方用一个元素来提醒用户输入错误才行,这个时候就用到了render的其他功能,也就是后边跟参数,配合在HTML里留下的特殊占位变量来替换内容.
先修改HTML文件,在最下边增加一个P标签,居中,内容设置为特殊的占位符号

<p class="text-center text-danger">{{ error }}</p>

再修改login函数,使其在登录失败的时候返回登录页面,同时将占位符号替换成提示.由于初始页面并不能有提示,因此在函数开始的地方设置替换变量的内容为0.同时优化一下代码.

def login(request):
    error_hint = ""
    if request.method == "POST":
        username = request.POST.get("username", None)
        password = request.POST.get("password", None)
        print(username, password)
        if username == "jenny@sina.com" and password == "conyli":
            return HttpResponse('登录成功')
        else:
            error_hint = "用户名或密码错误"
    return render(request, 'test.html', {"error": error_hint})

可以发现,在登录错误的时候,render替换p标签内容的页面并且返回,看到的页面就出现了登录提示.

redirect字符串替换

一般在登录成功之后,很多网站都是返回一个登陆之后的页面,比如很多网站是返回首页,很多论坛返回登录前页面或者论坛首页,如何要指定浏览器去访问某个url了,就需要用到redirect模块.比如我们希望在登录成功之后跳转到本站的首页,修改函数如下:

def login(request):
    error_hint = ""
    if request.method == "POST":
        username = request.POST.get("username", None)
        password = request.POST.get("password", None)
        print(username, password)
        if username == "jenny@sina.com" and password == "conyli":
            return redirect("http://conyli.cc")
        else:
            error_hint = "用户名或密码错误"
    return render(request, 'test.html', {"error": error_hint})

通过redirect方法(通过chrome可以看到得到了301响应),就可以重定向浏览器到指定的页面了.

Django项目中的APP

到目前为止,完成了一个简单的登录认证功能,虽然只有表面上的功能,但已经接触了Django用来返回内容的三个组件HttpResponse, render, redirect,而且还关闭了csrf中间件以方便提交表单,只能说开了一个头.如果要进一步编写复杂的网站,需要先搞清楚Django项目的标准结构.

现在我们的Django项目提供了1个页面,这个页面有1个功能,通过login函数来控制.可以发现,login函数写在urls.py文件内部,随着功能的增多,该文件也会增大,而且所有功能都会增加到urls.py文件,还需要反复修改其内部的urlpatterns参数的内容.这里就引入APP的概念,Django可以想象成一个手机,站点提供的每一个交互服务或者说功能,可以想象成一个手机应用.要给站点添加新的功能,只需要编写一个新的APP,然后编写好对应的HTML文件,放到站点内就可以运行了.

我们的login函数就是一个功能,提供了登录的业务逻辑功能,可以将其以APP的形式装入到Django中来.

建立APP

在命令行下建立APP的方法:

python3 manage.py startapp app_name

建立一个名为login的app,之后可以发现,项目目录里出现了叫login的目录,里边有一系列文件.
还需要将APP注册到Django项目中告诉Django有了一个APP,编辑settings.py文件:

# 存放Django项目所有的app
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login.apps.LoginConfig'
]

在列表中增加一项,内容是login目录下apps.py中的LoginConfig类.

在Pycharm中新建项目的时候,在More Settings中的Application name:可以指定一个初始的app名称.

然后,将urls里的login函数剪切到login目录下的views.py内部,做好导入render,redirect等模块的代码.
之后修改urls文件的内容:

from login import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
]

这样,就完成了APP的使用,以后只需要先在settings.py里注册APP,再在urls.py文件里导入APP的模块名称并配置好路径关系,就可以使用其中的功能.APP的具体实现都在对应的模块内部去实现.这样就解耦了Django主项目架构与具体功能的实现.这也是今后编写Django后端的标准结构.

总结

由于一开始不知道Django如何使用,因此就通过先让第一个站点和第一个交互功能来建立了Django站点.
现在再回顾一下Django的架构:

Django建立一个服务器,里边提供各种APP,搭配HTML文件使用,提供各种功能.
各个APP需要编写,主要使用了Httpresponse, render 和 redirect向浏览器返回数据.
在建立Django项目之后,首先要做的事情是:

  1. 检查settings.py文件中的BASE_DIR和templates的目录是否配置正确,涉及到能否正确的返回HTML页面.之后将所有的HTML文件都放入templates目录中
  2. 检查是否配置了静态文件的路径,如果没有,需要手动配置静态文件的存放路径,之后在HTML页面的引用内,采用别名加路径的引用方式
  3. 注释掉settings.py中带有csrf中间件的一行,表示关闭csrf中间件(不是必须,因为目前还没有学到,之后不需要关闭)
  4. 将需要绑定的IP地址,填入到settings.py

之后便可以编写自己的APP,在settings.py内添加,修改urls的对应关系,然后即可上线运行.