【Django】 初步学习

这个系列(或者成不了一个系列。。)预计会全程参考Vamei様的Django系列,膜一发。说句题外话,其实更加崇拜像Vamei那样的能够玩转生活、各个领域都能取得不小成就的人。

【Django】

■  概述

  Django久闻大名,是Python中最为有名的Web框架之一了。相比于其他框架,D的特点就是提供了各种各样的组件,重量级,可以解决很多很多问题。让Web编程一简再简。之前一直都学习使用Flask,不否认Flask有其有点,但是一个很大的不方便的地方在于,Flask的扩展没有统一的标准而且开放,所有人都可以写自己的Flask扩展。虽然说是具有开源精神,但是一些比较常见的功能出现多种实现还是会让人有些困扰。下面简单说说Django的使用

  安装Django依然使用pip:pip install Django,总的包大小大概6.8M。

  下载完成后可以在Python shell中运行以下命令:

>>>import django
>>>print django.VERSION
(1, 11, 6, u'final', 0)

  说明安装成功。

  ●  构建一个Django项目目录框架的快捷方法

  让我第一次感受到Django的重型和周到是相比较于Flask构建项目时要自己一个个文件建立,Django可以一键帮助生成一个较为完整的项目框架等待你填充内容。在pip安装完成之后,如果是windows环境则在$PYTHON_HOME的scripts,如果是Linux环境则是在/usr/bin这些目录下,里面会有一个django-admin[.exe]可执行程序。运行这个程序,后接上参数startproject <项目名>可以在当前工作目录下生成Django项目的目录框架。得到的框架是这样的:

【Django】 初步学习

  这些也不全是空文件,像manage.py,settings.py等文件都是带有默认内容的。

  考虑到一般开发肯定是在windows上用IDE比如Pycharm,Pycharm也可以一键生成Django项目框架,而且比django-admin生成的多一个templates文件夹用来盛放模板文件。

【Django】 初步学习

  构建完成框架之后可以python manage.py runserver 8000来运行起这个server,内容由Django内置好。看到的界面是类似于这样的:

【Django】 初步学习

  ●  第一次为请求返回HTTP内容

  Django框架采用MVC架构,Flask框架中对于路由的响应通过装饰器来绑定响应函数完成。而Django的路由设置统一放在urls.py这个文件中。下面将修改一下urls.py(因为之前存在一些内容了)

from django.conf.urls import url
from django.contrib import admin #####下面这行是新加的#####
from DjangoTest.views import first_page urlpatterns = [
url(r'^admin/', admin.site.urls), #####下面这行是新加的#####
url(r'^$', first_page)
]

  先来解释一下urlpatterns这个列表,维护了整个项目的url和响应函数的对应关系。这里比较NB的一点在于支持的是正则表达式,也就是说可以为一批URL绑定相同的响应函数。这一个和Flask还是比较不同的。Flask如果需要正则路由匹配的支持,则需要自己到werkzeug.routing中自己实现一个支持正则的Convertor对象。

  默认的自带了Django服务器后面的URI如果是/admin/的话,那么路由到Django-Admin界面,而下面我们添加的那条,则是说明了当请求URL为空(即访问的URI是/时),则路由到first_page这个函数中去。那么first_page在哪里定义呢,看上面的import语句,是DjangoTest目录下的views文件。这个文件是我们自己建的并且要往里面写内容的。比如下面这样:

from django.http import HttpResponse

def first_page(request):
return HttpResponse('<h1>Hello World</h1>')

  ●  进行模块化管理

  一个网站可能有很多功能,因此肯定需要进行模块化的代码管理。这一点在Flask中可以用类似于blueprint的结构来实现。而在Django中这个被称为app(默默吐槽,比Flask刚好高了一级)

  运行一个项目中的manage.py比如python manage.py startapp new_app就可以在当前项目中增加一个名为new_app的目录,下面也有admin.py,__init__.py,models.py,tests.py和views.py等文件。

  光添加一个新的目录并没有用,还需要将这个目录所代表的APP和当前项目关联起来。关联的方法就是修改DjangoTest下的settings.py文件。这个文件中有一个INSTALLED_APPS列表,在其中添加'new_app'即可。另外可以看到这个INSTALLED_APP里面已经有一些内容存在了,这些内容代表了Django内置的一些功能比如用户验证,会话管理,显示静态文件等等。Django识别APP是从项目的根目录开始的,所以不用像已有的那些写得比较复杂比如django.contrib.admin,而直接写new_app即可。

  为了访问到一个单独APP中的页面,我们首先在项目总的urls.py中添加new_app的urls的相关信息(这样做有利于不同模块的url映射的各自维护,模块间解耦)。把DjangoTest下的urls.py修改:

from django.conf.urls import url,include
from django.contrib import admin
from DjangoTest.views import first_page urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', first_page)
#####下面这行新增#####
url(r'^new/', include('new_app.urls'))
]

  看到这里其实已经可以大概看出来Django中如何进行url的配置了。往urlpatterns里面可以增加url对象。url对象构建时第一个参数是正则匹配路径,第二个参数可以是一个callable的对象,此时需要在之前import进去;也可以是include方法的返回,include方法的参数是一个字符串,字符串中以endpoint的形式指向另一个urls文件,此时那个urls文件中规定的正则匹配路径在整个项目中应该加上调用其include方法前面的路径整个拿来匹配。

  上面把new_app.urls文件中定义的url映射都include到了根目录下,然而在new_app下目前还没有urls.py文件,所以在这个目录下新建urls.py。文件中的内容参考下面这样子:

from django.conf.urls import url

from .views import new_first_page

urlpatterns = [
url('^$', new_first_page),
]

  这么处理之后访问/new/就会映射到new_first_page这个函数下了。

■  数据库和ORM初步

  一个WEB应用的根基在于数据库中的数据,一个好的web框架必须有很好的和数据库交互的手段。之前在Flask的时候,采用了SQLAlchemy的第三方模块的方法,将flask和数据库的交互做得比较友好。到了Django的场合,Django有一套自己的ORM机制,看起来很像SQLAlchemy(事实上好像就是改造了SQLAlchemy),贴合度更好。

  要进行数据库交互,首先得有数据库。比如我先到虚拟机的mysql中创建一个Django项目用的数据库,并且指定(或者创建)一个用户来管理这个库:

$mysql -u root -p
password:
mysql>CREATE DATABASE Django_Test DEFAULT CHARSET utf8; mysql>GRANT ALL PRIVILEGES ON Django_Test.* TO weiyz@'%';
/*将新建的数据库的操作权限赋给管理用户*/
mysql>FLUSH PRIVILEGES;

  然后在Django项目的settings.py中的数据库相关配置更改:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'Django_Test',
'USER' : 'weiyz',
'PASSWORD' : '',
'HOST' : '192.168.191.112',
'PORT' : '',
}
}

  这样就关联了数据库和Django项目。

  web和数据库的交互形式是一个很重要的问题。如果我们使用MySQLdb这类包装的比较低级的模块来做的话,每一个数据库操作都要写一个SQL语句出来,略显笨拙。ORM的妙处就在于能够把数据库操作封装得像是一段原生的程序。Django采取的抽象数据库操作的方式和SQLAlchemy很像,就是把一张表抽象成一个python类。比如我们在new_app这个APP中的models.py中加入以下内容:

from __future__ import unicode_literals

from django.db import models

class Character(models.Model):
name = models.CharField(max_length=200)
age = models.IntergerField() def __unicode__(self):
return self.name

  此时项目中已经设计好了表,就差把表结构给注入数据库了。然而这个过程如果手动做就没有意义了。Django给出了自动的解决方案。依次运行如下命令:

python manage.py migrate

python manage.py makemigrations

python manage.py migrate

  第一个migrate可以看做是数据库的初始化,运行完第一条之后进入数据库看可以看到Django_Test库中有了下面这些表

+----------------------------+
| Tables_in_Django_Test |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+

  这些表也不是空表,都是有数据的而且比较重要。至于为什么后面再说。

  第二条命令是将我们新建的表(也就是models里面的类)给记录到APP的migrations目录中的一个文件。这个目录在python manage.py startapp的时候自动创建,且里面自带一个__init__.py。这些文件是作为后续数据库版本的升降级操作的依据,所以也不应该擅自删改。此时migrations目录下应该就有了一个0001_initial.py的文件了。

  第三条则是把本地的数据库信息同步到真的数据库中,在操作完这个命令之后,我们可以看到数据库中会多出一张new_app_character的表,其表结构是和我们的Character类定义的相同。

  ●  如何初始化migrations相关数据

  可以看到有django_migrations这个表,说明在数据库升降级的时候并不是单单看migrations目录下面有哪些文件的,而是参考了数据库中的数据。所以说数据库中的那些初始化信息也很重要。这么一来,想要初始化所有数据库数据就不能简单的把migrations下面的文件删光了。比较彻底的做法是把整个Django_Test数据库drop掉,然后把migrations中的除了__init__外所有文件删掉。再重新建库,migrate,makemigrations这样来做。

  当然在正式生产中肯定不能这么做,这就表明,对于数据库初始化出来的数据以及migrations中的文件一定不能轻易修改删除。理想的做法是对表结构做出调整之后先makemigrations再migrate一下。

  这里顺便一提,进行数据库版本升级的时候django做得还是很智能的。比如我一开始没有age字段,migrate的时候添加了age字段但是没指定default,django就提示说如果表里原来有记录的话就不知道新插入这个age字段该取什么值,让我选择时系统自动给一个default值呢还是回models.py中手动指定一个default值。

  通过ORM进行增删查改以后慢慢说,这里只展示一下:

  首先通过后台插入数据:

INSERT INTO new_app_character (name,age) VALUES('Django',50);

  需要注意的是一定要给出(name,age),因为上面定义的时候我们没有给出主键,orm自动为这个表添加一个名为id的字段作为主键,从1开始自然计数。

  然后在程序中比如在views中可以:

from .models import Character
from django.http import HttpResponse def new_first_page(request):
namelist = Character.objects.all()
res = ''
for name in namelist:
res += '<p>%s</p>' % name
return HttpResponse(res)

  这就实现了通过ORM从数据库中取出数据的目的了。

■  Django模板初步

  如上面目录中那样,在项目的根目录级下有一个templates目录,里面存放的自然就是项目的模板文件了。

  在settings.py中,可以看到一个配置项是TEMPLATES,下面有一个DIRS的配置项,这是一个list。其中默认的是那个templates目录,也可以自己手动再增加一些其他的目录,这样就实现了多个模板目录的设置了。至于模板的语法和渲染模板的方法以下面这个例子做个最简单的说明:

####模板文件test.html####
<h1>{{ label }}</h1> ####某个views.py文件中增加内容####
from django.shortcuts import render def template_show(request):
context = {}
context['label'] = 'Hello World'
return render(request, 'test.html', context)

  之后在相应的APP的urls.py中添加对url的路由,访问相关页面就可以看到html为<h1>Hello World</h1>的页面了。这里可以注意一下render方法和flask中render_template的一些不同,为了避免大量变量渲染时引起的参数过长的问题,把所有参数都维护到一个context字典里面可以说是一个非常好的办法。

  Django在渲染模板的时候,首先将上下文数据context传递给模板文件,分析模板文件中需要渲染的部分,具体化之后又自动生成了HTTPResponse返回,所以我们在这里可以直接return render方法的返回。结合上面说的ORM,可以从数据库中取出数据,进行一定程度的处理之后再传递给模板,这就完成了一个非常MVC的流程。

  之前在学习flask的时候就听说过Django的模板和Jinja2不太一样,但是粗粗看了下教程,发现是大同小异的,这里就不再费口舌说一些基本的东西了。主要补充一些据说和jinja2不同的地方(动态更新中...),比如在Django的模板中,无参函数的调用时不用机加括号的,但是jinja2是和原生的Python一样加括号。

  总体而言,Django的模板系统比Jinja2有更多的限制,也更不像是python或者其他的编程语言。比如Jinja2中可能会有{% if name == 'takanashi' %}这样的表达但是据说Django是不行的等等。

  ●  关于宏

  之前似乎没有提到,django的模板系统中是不存在宏这种设定的。也就是说不能直接使用{% macro xxx %}这样的方式来定义宏从而减少编写重复代码的次数。

  不过好在有解决方案就是django-macros。pip install一下之后,在项目的settings中的INSTALLED_APPS中添加'macros',然后再在相关模板中{% load macros %}之后,这个模板文件里就可以*使用macro了。

  ●  关于自动反转义

  如果后端传到模板的字符串中含有一些HTML敏感的字符比如<,>,&等,Django在渲染模板的时候会自动将这些内容进行一个反转义,从而使页面可以正确地显示这些文本。比如

  <div>{{ text }}</div>是模板,然后后端的ctx={'text': 'Hello,<b>World</b>'},渲染出得到的页面会是Hello,<b>World</b>,而不是Hello,World这样加粗字体的。

  有时如果需要反过来,不要他强行自动转义,则可以在模板中使用{% autoescape %}标签,包含在{% autoescape off %}{% endescape %}这个block中的所有待渲染的内容,是不会自动转义,而是保持HTML原有的样式的。

■  Django的表单处理

  任何一个Web框架都少不了对表单的支持。下面演示一个最简单的通过POST方法发送数据给WEB应用然后将数据存入数据库之后返回一个页面的Django的结构。models等一些数据复用了前面提到过的东西:

<!-- 模板页面 -->
<form method="post" action="/new/process/">
{% csrf_token %}
<input type="text" name="name" />
<input type="number" name="age" />
<input type="submit" value="Submit" />
</form> <p>{{ rlt }}</p>
<p>{{ age }}</p>

  在后台的new_app/views.py中:

from django.shortcuts import render
from django.template.context_processors import csrf def process(request):
ctx = {}
ctx.update(csrf(request))
if request.POST:
ctx['rlt'] = request.POST['name']
ctx['age'] = request.POST['age']
return render(request, 'formtest.html', ctx)

  这里需要注意的是在模板中我们就做了csrf的处理,然后在后台也要进行一个csrf的处理,然后根据处理完之后的context再来渲染页面。可以看到,在Django里面默认不做出对某个url的访问方法的限制。所以在urls.py中定义了到这个处理函数的路由(/new/process)后访问这个路由,首先是GET方法获取页面,此时因为context中没有定义rlt和age这两个模板中的变量,所以页面下方是两个空行。然后填完数据表单提交,因为form标签的action指向还是这个路由,所以POST数据到process函数下面。因为是POST,进入分支,context中有了关于rlt和age的值,于是就渲染出有值的页面了。

  如果需要将POST上来的数据根据刚才定义的model存进数据库那么可以:

def process(request):
ctx = {}
ctx.update(csrf(request))
if request.POST:
name,age = request.POST.get('name'),request.POST.get('age')
new_record = Character(name=name,age=age)
new_record.save()
return render(request, 'formtest.html', ctx)

  这也是大概地展示一下如何用ORM进行“增”的数据库操作

  

  在flask中,我们用到了wtforms来进行方便的表单渲染和管理,Django也有类似的功能。而且Django把表单管理的模块也一并整合到了Django这个大模块中,所以使得表单的描述和数据库表结构的描述可以统一起来。而这两者在实际中又常常是互相关联的。比如上面的那个表单,我们可以做以下改造:

from django import forms

class CharacterForm(froms.Form):
name = forms.Charfield(max_length=200,label="Your Name")
age = forms.IntegerField(min_value=18) def process(request):
context = {}
context.update(csrf(request))
if request.POST:
form = CharacterForm(request.POST)
if form.is_valid():
#do something with data
form = CharacterForm()
context['form'] = form
return render(request, 'form_test.html', context) ####在模板中可以这么写####
{{ form.as_p }}
####这样就可以自动地生成一个表单了####

  虽说是自动生成了表单,但是需要注意的是并不是全部要素,只是要填的一些字段和相关的label等等,比如<form> 标签已经submit的input等还是要自己手写的,相当于as_p方法只是放回了纯的变成了p标签形式的表单html代码。

  ●  对于ajax发起post请求的csrf处理

  以上对于表单发起post请求的举例都是通过了<form>这个DOM来实现的。但是在有ajax的时候我们可以不必拘泥于form而采用更加*的ajax发起POST请求。这就引起了一个小问题,在form的时候我们只要在前端模板里面写上{% csrf_token %}就可以自动给我们的表单DOM增加防csrf验证功能。但是在ajax的时候如何将这部分信息和一个特定的ajax请求联系起来。办法有很多种,比如可以在页面中引入额外的一个csrf.js文件来适应csrf验证【参考https://code.ziqiangxuetang.com/django/django-csrf.html】。

  一个更加简单的方法是在全局ajax设置中增肌相关csrf验证的设置:

$.ajaxSetup({
data: {
csrfmiddlewaretoken: '{{ csrf_token }}'
}
});

  这个办法需要注意的是1. 这段代码应该写在模板文件的<script>标签中因为{{ csrf_token }}这个只有在模板里面才能被识别,写在.js中是无法被识别的。2. csrf_token要用{{ }}括起来而不是{% %},具体原因不知道。。总之只有这样才行。

  还有一个不是办法的办法,就是在相关的ajax的POST发向的那个view,from django.views.decorators.csrf import csrf_exempt,然后在这个view函数上面增加装饰器@csrf_exempt来迫使这个view接受到的请求不进行CSRF验证。

■  Django自带的WebApp

  之所以称Django是一个很大的框架,原因在于它自带了很多功能,其中感觉到最神奇的就是这个,自带的一个管理数据库的APP。这个APP通常在settings.py的INSTALLED_APPS中已经预安装好,并且在项目根目录的urls.py中还设置好了url,通常是[site]/admin来访问。

  这个App其实是一个管理数据库模型的一个App,在通过它管理模型之前还需要在相关模型所在的应用中的admin.py下进行模型的注册。做法是:

###admin.py###
from models import Character admin.site.register(Character)

  如果是第一次访问管理界面,那么需要用manage.py工具的createsuperuser命令来建立管理员用户,按照字符界面的提示输入管理员的用户名密码等信息来注册管理员用户。

  登录之后我们可以看到这样的界面:

【Django】 初步学习

  可以看到我们注册的new_app下面的Character模型已展示在页面上了。至于上面的是Django预装的Auth模块,我们以后还可以用它来进行用户的管理。因为用户说到底也只是数据库中的一张表,一个模型而已。另外这个web界面管理真的十分方便,这个Character的模型还略显简单了点,如果是个稍微复杂一点的模型比如:

class Role(models.Model):
role_code = models.IntegerField(primary_key=True)
role_name = models.CharField(max_length=100)
def __unicode__(self):
return self.role_name class User(models.Model):
name = models.CharField(max_length=100,primary_key=True)
age = models.IntegerField()
email = models.EmailField()
role = models.ForeignKey(Role)
def __unicode__(self):
return '%s(%s) Mail:%s' % (self.name,self.age,self.email)

  涉及到了外键的设置,此时如果先点击Role旁边的Add,可以手动为数据库中添加角色信息:
【Django】 初步学习

  当添加完一些信息之后,到添加User的界面中还可以看到刚才添加过的角色信息供选择:

【Django】 初步学习

  需要注意的是添加一条记录后会给出一个提示中有中文,再有就是比如这边角色的几个名字,这些都是根据__unicode__的返回决定的。所以在model类实现的时候需要实现__unicode__方法,并且想好一种表达清晰的返回。在开发阶段进行小批量的数据插入的时候,这个界面显然比到后台用SQL插入数据要友好很多。

  此外如果需要对某些字段进行隐藏处理的话可以修改一下new_app/admin.py中的内容,之前我们直接admin.site.register了所有模型类。如果想要进行字段显示的调整可以在new_app/admin.py中进行:

from django.contrib import admin

from models import User,Role

class UserShow(admin.ModelAdmin):
fields = ('name','age','role') #没加email admin.site.register(User,UserShow)
admin.site.register(Role) #Role表不变,但是User表有所调整

  这样的话在界面上无论是读取User记录的信息还是新增一个User的记录,Email字段就都不会显示出来了。其实这么做的原理显而易见,就是把一个ModelAdmin类的衍生类和我们的模型类关联起来。上面这个ModelAdmin的子类UserShow用到了fields这个属性来设置显示不显示,其实还有更强大的属性比如fieldsets:

class UserShow(admin.ModelAdmin):
fieldsets = (
['基本信息',{
'classes' : ('collapse',), # CSS设置
'fields' : ('name','age')
}],
['更多信息',{
'classes' : ('collapse',),
'fields' : ('email','role')
}]
)

  如果UserShow跟上面这样写的话,得到的界面就是:

【Django】 初步学习

  基本信息和更多信息两个Panel都可以通过点击旁边的hide和show来隐藏显示。

  类似的,设置这个类中的list_display属性可以改变展示界面的字段展示,下面贴出代码和修改前后界面的图:

class UserShow(admin.ModelAdmin):
list_display = ('name','age','email','role')

  改变前:

【Django】 初步学习

  改变后:

【Django】 初步学习

■  用户验证与管理

  之前提到过Django预装了Auth这个用户管理模块,那么要怎么使用。首先在settings.py中的INSTALLED_APPS中一般是有admin.contrib.auth这个APP在了。在http://site/admin这个管理界面上可以在系统自带的那个Users中去添加删除用户,当然这个页面最好不要暴露给一般用户,正确的做法应该是自己设计一个表单收集用户提供的信息,然后根据信息进行后台的相关操作。因为用户的信息也在Django设计的用户系统的框架内所以后台那些操作自然也是Django自身提供的一些封装好的方法。

  在进行下面的说明之前,我们先建立一个名为users的App来单独进行用户的一些操作:python manage.py startapp users

  ●  用户的登录、验证和登出

  前端模板:

<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ request.path }}</title>
</head>
<body>
<form method="post" action="/users/login">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Go!" />
</form> </body>
</html>

  后端views.py:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals from django.shortcuts import render,redirect
from django.template.context_processors import csrf
from django import forms
from django.contrib.auth import authenticate,login
# Create your views here. class LoginForm(forms.Form):
username = forms.CharField(max_length=100,min_length=3)
password = forms.CharField(min_length=8,widget=forms.PasswordInput)
email = forms.EmailField() def user_login(request):
context = {}
context.update(csrf(request))
login_form = LoginForm()
if request.POST:
username = password = ''
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password)
if user is not None and user.is_active:
login(request,user)
return redirect('/')
else:
return redirect('/users/login')
context['form'] = login_form
return render(request,'login.html',context)

  这里比较新鲜的就是contrib.auth中的authentication和login两个方法。当用户在表单中输入信息然后POST上来之后,我们可以直接调用authentication方法来快速验证用户密码的正确性,如果验证通过将返回这个用户的User对象。如果没有通过则是返回None。需要注意的是Django内建的User对象还具有active这个属性即用户是否在有效期内,如果不是的那么也不能将用户放行。另外,自带的这个验证机制似乎不能判别是用户不存在还是密码错误导致的验证没通过。

  login方法则更像是一个状态置活方法,通过验证之后调用这个方法来告诉系统当前用户XXX是登录状态的。在调用过login方法之后就可以保持整个会话过程中用户身份的保持了。

  在其他的视图中,可以通过request.user这个对象来调用当前登录用户相关的一些信息。比如user.get_username()返回用户名,user.set_password('xxx')可以重设密码,user.password则可以看到密码的密文,date_joined和last_login分别可以看到账号创建时间和上次登录时间,不过这两个时间的时区有点诡异,反正都不是东八区。user.check_password('xxx')可以对一段明文是否是密码进行验证,可以用于用户登录之后还需要验证密码的场合比如授权等等。下面是一个在其他视图(非登录相关视图)中调用user信息的示例:

def auth_test(request):
context = {}
if request.user.is_authenticated():
return HttpResponse('<p>%s</p>' % request.user.get_username())
else:
return redirect('/user/login')

  这里用的判断当前是否有用户的方式是用了if 语句判断user.is_authenticated方法返回的状态。其实还有更加方便的方法就是使用装饰器login_required:

from django.contrib.auth.decorators import login_required

@login_required
def auth_test(request):
return HttpResponse('<p>%s</p>' % request.user.get_username())

  这里一个小问题是当用户处于登出状态的时候,如果访问@login_required修饰的路由函数会怎么样。默认情况下,会转送到uri为accounts/login?next=/xxxx(auth_test的路由),如果没有定义过accounts/login的话那么引起的就是404了。其实在使用@login_required的时候可以加上参数(login_url='/users/login')来把页面重定向到我们设计好的/users/login下面去。只不过next这个参数还是没有自带的实现的,所以还需要我们在login的路由函数中再添加对GET参数next的一些处理。如果不希望在后台做判断的话也可以在前台的模板中进行判断,比如添加一条{% if user.is_authenticated %}(Django模板函数不加括号哦),具体就不写出来了。

  说到登出,登出更加简单:

from django.contrib.auth import logout

def user_logout(request):
logout(request)
return redirect('/')

  相比于login,logout只需要request这一个参数。

■  用户注册

  用户注册和用户登录类似,只不过接受表单的数据之后做的处理不太一样。当然我们可以手动写一个表单的html来做,不过对于简单的用户注册,还有更加方便的操作:

from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import redirect,render
from django.template.context_processors import csrf def user_register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return redirect('/') else:
form = UserCreationForm()
context = {'form':form}
context.update(csrf(request))
return render(request, 'register.html', context)

  前端模板:

<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

  尝试了一下,渲染出来的表单还是非常难看的。。

■  自定义用户表

  如果觉得django.contrib.models.User这个模型不能满足你的需求的话,也可以通过继承这个模型来自定义一个用户类。这个自定义的用户类可以适用于之前所有User类可以用的方法和函数。相当于是对django自带的User类进行了一个很好的扩展。

  继承是一种办法,另一种办法就是自定义一个模型类之后,在类中增加一个OneToOneField映射到User这个表上。通过OneToOneField映射的好处就是在调用时可以直接request.user.xxx来调用我们自定义的类,十分方便。

  如果对于定制要求比较高,那么也可以到更底层去,比如从AbstractBaseUser(User类的父类的父类)和AbstractUserManager开始继承。

===============

至此,Django的基本介绍结束,原博主还写了两篇关于用Web容器部署项目以及买服务器部署服务的,这里不太需要就不多说了。

这篇文章顶多是对Django有一个大概的印象,接下来我会看书本,把上面提到的知识细化,分块记录下来。

以上。

上一篇:在页面生命周期执行时 Page 对象在 SaveState 阶段都发生了什么事?


下一篇:js学习--DOM操作详解大全 前奏(认识DOM)