…
绪论 Django 3 官方参考文档 点击进入
Django 2.2 官方参考文档 点击进入
Django 2.2 其他参考文档 点击进入
Django 1.11 官方参考文档 点击进入(英文)
Django 1.11 中文参考文档 点击进入
Django 2 及后续版本不再支持 Python 2;
Django 3 及后续版本不再支持 Python 3.5 及以下版本。
Django 1 与 2 的区别 主要区别如下:
url 用法:Django 1 主要使用 url 来配置,参数部分使用()
做匹配;Django 2 使用 path 来配置,参数部使用<>
做匹配,不支持传统的正则表达式。这里Django 2 兼容 Django 1 ,可以用re_path来做Django 1中url的操作。
路由分发 include。
ORM 外键:Django 2 的外键必须加on_delete属性
参考文章一 参考文章二
Django 3.0 新特性(2019年12月 推出)
仅支持 Python 3.6以上版本。
支持使用 MariaDB 10.1 或更高版本的数据库。
开始将新增对 ASGI 的支持。这意味着 Django 3 可以支持异步操作,消除阻塞操作对程序的影响。
新增枚举类型 TextChoices 和 IntegerChoices 类。
枚举示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Student (models.Model): FRESHMAN = 'FR' SOPHOMORE = 'SO' JUNIOR = 'JR' SENIOR = 'SR' GRADUATE = 'GR' YEAR_IN_SCHOOL_CHOICES = [ (FRESHMAN, 'Freshman' ), (SOPHOMORE, 'Sophomore' ), (JUNIOR, 'Junior' ), (SENIOR, 'Senior' ), (GRADUATE, 'Graduate' ), ] year_in_school = models.CharField( max_length=2 , choices=YEAR_IN_SCHOOL_CHOICES, default=FRESHMAN, )
Django 基本操作 django-admin 基本命令 1 2 3 4 5 6 7 8 9 10 django-admin startproject django-admin startapp django-admin check django-admin test django-admin runserver django-admin shell django-admin makemigrations django-admin migrate django-admin dumpdata django-admin loaddata
目录结构 项目结构 A (本次使用)
Project # 项目目录
manage.py # 项目管理文件
project_name # 项目配置目录
asgi.py # Django 3.0 新增文件
settings.py # 项目配置
urls.py # 项目路由
wsgi.py # Web 与 Django 交互入口
my_app # 创建的Django应用目录
migrations # 数据库迁移文件目录
static # 静态文件目录
templates # 模板目录
templatetags # 自定义标签过滤器目录
urls.py # 应用路由
apps.py # 应用声明
models.py # 应用模型
test.py # 单元测试
admin.py # Admin模块
views.py # 应用视图
创建过程如下:
1 2 3 4 5 6 7 django-admin startproject project_name cd project_namedjango-admin startapp my_app cd my_appmkdir templatesmkdir templatetagsmkdir static
项目结构 B 部分Django项目也会用到这种结构,即将模板,过滤器,静态文件等目录放在应用的外部。
Project
manage.py
project_name
settings.py
urls.py
wsgi.py ( Web 与 Django 交互入口)
templates
templatetags # 自定义标签过滤器
my_app
migrations
urls.py
apps.py
models.py
test.py
admin.py
views.py
配置过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 INSTALLED_APPS=[ ... 'my_app' , 'my_app.apps.MyAppConfig' ] DATABASES=[ 'default' : { 'ENGINE' : 'django.db.backends.sqlite3' , 'NAME' : os.path.join(BASE_DIR, 'db.sqlite3' ), } ] DATABASES=[ 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'NAME' : '数据库名称' , 'USER' : 'root' , 'PASSWORD' : '123456' , 'HOST' : 'localhost' , 'PORT' : 3306 , } ] TEMPLATES = [ ... 'DIR' = [os.path.join(BASE_DIR, 'templates' )] ... ] LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' DEBUG = False ALLOWED_HOSTS = ['*' ]
配置完成后,在应用my_app下的views.py中添加一个简易的视图:
1 2 3 4 5 6 from django.shortcuts import renderfrom django.http import HttpResponsedef hello (request ): return HttpResponse("Hello World" )
同时应该配置路由文件,在my_app中创建并配置urls.py。
1 2 3 4 5 6 7 from django.urls import path, re_path, includeimport my_app.viewsurlpatterns = [ path('hello/' , my_app.views.hello), ]
1 2 3 4 5 6 7 8 9 10 from django.conf.urls import url, includeimport my_app.viewsurlpatterns = [ url('hello' , my_app.views.hello), url(r'^hello/$' , my_app.views.hello), ]
之后再配置项目路由器:
1 2 3 4 5 6 7 from django.contrib import adminfrom django.urls import path, includeurlpatterns = [ path('admin/' , admin.site.urls), path('my_app/' , include('my_app.urls' )) ]
配置到这里就可以运行项目看效果了。
1 python manage.py runserver 8080
进入如下地址,出现Hello World
就算配置成功了。http://127.0.0.1:8080/my_app/hello/
Django 模型 模型(Models)是用于对接数据库的接口,在所有的MVC应用中都是如此。Django的模型的定义如下:
数据类型
整型:IntegerField
定长文本:CharField
不定长文本:TextField
日期:DataField
时间:TimeField
日期时间:DateTimeField
自增ID:AutoField
(可以不定义,自动生成)
布尔:BooleanField
Null型布尔:NullBooleanField
十进制浮点数:DecimalField
(精度更高,例如钱数)
浮点型:FloatField
文件:FileField
图片:ImageField
主要属性
主键:primary_key
长度:max_length
默认值:default
唯一性:unique(不允许重复出现)
索引:db_index
自定义字段名称:db_column
是否允许为空:null
是否允许空白:blank
十进制浮点数:
数字数:max_digits
小数点数:decimal_places
日期(二者只能用一个)
更新时间:auto_now
创建时间:auto_now_add
外键:’其他表’ (赋值时直接写该对象,2.0以上版本还要求on_delete属性)
创建模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from django.db import modelsclass MyBookModel (models.Model): title = models.CharField(max_length=20 ) publish_date = models.DateTimeField(auto_now=True ) author = models.ForeignKey('AuthorModel' , on_delete=models.CASCADE) def __str__ (self ): return self.title class AuthorModel (models.Model): name = models.CharField(max_length=20 ) age = models.IntegerField(default=0 ) def __str__ (self ): return self.name
完成后,迁移数据库到sqlite:
1 2 python manage.py makemigrations python manage.py migrate
使用模型 简单查询操作
get:返回模型对象;查到多条或是未查到都会抛出异常。
all:返回查询集;返回所有数据
filter:返回查询集;返回满足条件的数据
exclude:返回查询集;返回不满足条件的数据
order_by:返回查询集;对查询结果排序
对于 get,filter,exclude 这三个查询操作,可以填写查询条件,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 MyBookModel.objects.get(id =1 ) MyBookModel.objects.get(title__exact="First Book" ) MyBookModel.objects.filter (title__contains="First" ) MyBookModel.objects.filter (title__startswith="First" ) MyBookModel.objects.filter (title__endswith="Book" ) MyBookModel.objects.filter (title__isnull=False ) MyBookModel.objects.filter (id__in=[1 , 3 , 5 ]) MyBookModel.objects.filter (id__gt=2 ) MyBookModel.objects.filter (id__lt=2 ) MyBookModel.objects.filter (id__gte=2 ) MyBookModel.objects.filter (id__lte=2 ) MyBookModel.objects.filter (publish_date__year=1990 ) MyBookModel.objects.filter (publish_date__month=2 ) MyBookModel.objects.filter (publish_date__day=2 ) MyBookModel.objects.filter (publish_date__gt=date(1980 ,3 ,2 )) MyBookModel.objects.all ().order_by('id' , 'title' ) MyBookModel.objects.all ().order_by('-id' ) m = MyBookModel.objects.filter (id =1 ) m.exists()
也就是说,其参数格式为模型属性名__条件名=值
。(注意是双下划线)
高级查询操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from django.db.models import F, QMyBookModel.objects.filter (id__gt=2 , title__contains="First" ) MyBookModel.objects.filter (Q(id__gt=2 ) & Q(title__contains="First" )) MyBookModel.objects.filter (Q(id__gt=2 ) | Q(title__contains="First" )) MyBookModel.objects.filter (~Q(id =2 )) MyBookModel.objects.filter (publish_date=F('publish_date' )) MyBookModel.objects.filter (publish_date=F('publish_date' ) / 3 ) from django.db.models import Sum, Count, Avg, Max, MinMyBookModel.objects.all ().aggregate(Count('id' )) MyBookModel.objects.aggregate(Count('id' )) MyBookModel.objects.filter (id__gt=2 ).count()
查询集:
惰性查询:只有需要具体数据的时候才发生查询。
缓存:第一次查询到的查询集数据会缓存下来,第二次再访问这个查询集的时候就会使用缓存的内容。
切片:对一个查询集切片会产生新的查询集,且切片参数不可为负数。
模型关系 模型(数据表)关系分为:
关系属性:
多对多:ManyToManyField(‘表名’),可以任意定义到其中一个模型中。
一对一:OneToOneField(‘表名’),可以任意定义到其中一个模型中。
多对一:ForeignKey(‘表名’),定义在多的模型中。
关联查询(一对多) 1 2 3 4 5 6 7 8 9 10 11 一表.objects.filter (多表__属性__条件='...' ) AuthorModel.objects.filter (mybookmodel__title__contains='First' ) 多表.objects.filter (外键__属性__条件='...' ) MyBookModel.objects.filter (author__name__contains='W' ) 查询集x = 一表.objects.get(id =1 ) 查询集y = 查询集.多表_set .all () x = AuthorModel.objects.get(id =1 ) y = x.mybookmodel_set.all ()
插入,更新与删除 使用举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from my_app.models import AuthorModel, MyBookModelfrom datetime import datea = AuthorModel() a.name = "Wang" a.age = 80 a.save() a = AuthorModel.objects.create(name='Li' , age=10 ) m = MyBookModel() m.title = "First Book" m.date = date(1999 ,1 ,1 ) m.author = a m.save() m = MyBookModel.objects.get(id =1 ) m.title = "Second Book" m.save() m = MyBookModel.objects.get(id =1 ) m.delete()
自关联 自关联是一种特殊的一对多关系,例如“省->市->县”的关系。 设计这种关系,一种方法是设计三张表,利用外键关联三个表;另外一种方法是将他们设计到一张表中,利用一个字段指向其父级ID,这样就形成了自关联的关系。
1 2 3 4 5 6 class AreaModel (models.Model): title = models.CharField(max_length=100 ) parent = models.ForeignKey('self' , null=True , blank=True , on_delete=False ) def __str__ (self ): return self.title
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值。
CASCADE:此值设置,是级联删除。 PROTECT:此值设置,是会报完整性错误。 SET_NULL:此值设置,会把外键设置为null,前提是允许为null。 SET_DEFAULT:此值设置,会把设置为外键的默认值。 SET():此值设置,会调用外面的值,可以是一个函数。
管理器 objects 就是MyBookModel.objects
的objects
。自制管理器的优势有:
自制管理器的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class AuthorModelManager (models.Manager): def all (self ): a = super ().all () return a.filter (age__gt=10 ) def create_author (self, name, age ): a = self.model() a.name = name a.age = age a.save() return a class AuthorModel (models.Model): ... objects = AuthorModelManager() ... @classmethod def create (cls, name, age ): a = cls() a.name = name a.age = age a.save() return a
一旦自制了管理器,原来的管理器就自动失效了(就算名字不是objects,原objects也会失效)。
元选项 在数据库中,数据表的命名是项目名_模型名
,但是一旦项目名发生变化,所有的表的命名都会受到影响。如果消除这种影响,可以在模型类里面定义一个元类:
1 2 3 4 5 class AuthorModel (models.Model): ... class Meta : db_table = 'author_table'
在某些版本的SQLite中不支持数据表改名,所以这一步操作要注意。
导入导出数据 1 2 python manage.py dumpdata > data.json python manage.py loaddata data.json
Django 视图 静态视图 在my_app下创建templates文件夹,在该文件夹下创建index.html,编辑index.html。
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > Hello</h1 > </body > </html >
编辑视图函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 from django.http import HttpResponsefrom django.shortcuts import renderdef hello (request ): return HttpResponse('Hello' ) def index (request ): return render(request, 'index.html' )
修改后记着修改对应的urls:
1 2 3 4 5 6 7 8 from django.urls import path, includeimport my_app.viewsurlpatterns = [ path('hello' , my_app.views.hello), path('index' , my_app.views.index) ]
动态视图 模板系统基本语法:
1 2 3 变量标签:{{ 变量 }} for循环标签:{% for x in list %}, {% endfor %} if-else标签:{% if %}, {% else %}, {% endif %}
首先编辑好前端页面,label_list是要输出的标签。这里我们创建books.html文件,编辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > {% for x in label_list %} <h1 > {{ x.title }}</h1 > {% endfor %} </body > </html >
模板渲染: 后台给要输出的标签赋值:
1 2 3 4 5 def get_them (request ): get_all = MyBookModel.objects.all () return render(request, 'books.html' , { 'label_list' : get_all })
修改后,别忘了修改urls.py文件。
1 2 3 4 urlpatterns = [ path('index/' , my_app.views.index), path('get_books/' , my_app.views.get_books), ]
如果想自己做一个渲染器,可以这样做:
1 2 3 4 5 6 7 from django.template import loader, RequestContextdef my_render (request, template, args ): temp = loader.get_template(template) context = RequestContext(request, args) res_html = temp.render(context) return HttpResponse(res_html)
http://127.0.0.1:8080/my_app/index/ http://127.0.0.1:8080/my_app/get_books/
依据ID进行路由跳转 首先建立模板one_book.html:
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > {{ item.title }}</h1 > <h2 > {{ item.author }}</h2 > </body > </html >
在my_app的urls里面修改:
1 path('get_the_book/<int:bid>' , my_app.views.get_the_book)
相应修改views.py:
1 2 3 4 5 6 def get_the_book (request, bid ): bid = int (bid) get_one = MyBookModel.objects.get(id =bid) return render(request, 'one_book.html' , { 'item' : get_one })
页面重定向跳转方法:
1 2 3 4 5 6 7 8 9 def create (request ): a = AuthorModel.objects.get(id =1 ) m = MyBookModel() m.title = "Second Book" m.author = a m.save() return HttpResponseRedirect('/my_app/index' )
自定义 404 等错误页面 在项目urls.py中配置:
1 2 3 4 import django.conf.urlsdjango.conf.urls.handler404 = 'my_app.views.error404' django.conf.urls.handler500 = 'my_app.views.error500'
同时设计相应的错误页面。要查看效果,就要关闭DEBUG模式才可以。
1 2 3 4 5 6 def error404 (request ): return HttpResponse('Error handler content' , status=404 ) def error500 (request ): return HttpResponse('Error handler content' , status=500 )
管理静态文件 通常一些静态文件,如网站logo等资源需要单独存放到一个固定的位置,一般是存放到静态文件目录下。 配置静态文件目录过程为:
确保 INSTALLED_APPS 包含了 django.contrib.staticfiles。
在配置文件中,定义 STATIC_URL,例子:STATIC_URL = '/static/'
在模板中,用 static 模板标签基于配置 STATICFILES_STORAGE 位给定的相对路径构建 URL。1 2 {% load static %} <img src ="{% static 'image/example.jpg' %}" alt ="My image" >
将你的静态文件保存至程序中名为 static 的目录中。例如 my_app/static/image/example.jpg。
表单 表单的提交常见有两种方式:GET与POST。 在Request中包含了浏览器的请求信息,Request的属性包括:
POST:POST请求参数,查询字典(QueryDict)类型
GET:GET请求参数,查询字典(QueryDict)类型
FILES:上传的文件,类似于字典的对象
COOKIES:客户端的cookies,一个Python字典
path:表示请求路径,不包括域名与参数
method:表示请求方式
encoding:提交数据的编码,默认utf8
session:服务端session
用法如下
1 2 3 4 5 6 7 8 9 def set_author (request ): args = request.POST name = args.get('name' ) name = args.get('name' , "Anonymous" ) name = args.getlist('name' ) name = args['name' ] return HttpResponseRedirect('/my_app/index' )
Ajax 请求 1 2 3 4 5 6 7 8 9 from django.http import JsonResponsedef get_author (request ): a = AuthorModel.objects.get(id =1 ) j = { 'name' : 'Wang' 'age' : 20 } return JsonResponse(j)
Cookie 与 Session Cookie:保存在客户端,由服务器生成,客户端访问服务器时会附带Cookies。另外Cookies是会过期的,如果不指定,则有效期为关闭浏览器时。 Session:保存在服务端,也是由服务器生成,依赖于Cookie,因为客户标识码SessionID存储在Cookie里面。Session存储位置在数据库中。
Cookie
1 2 3 4 5 6 7 8 9 10 response = HttpResponse('' ) response.set_cookie('num' , 1 , max_age=7 *24 *3600 ) response.set_cookie('num' , 1 , expires=timedelta(days=7 )+datetime.now()) return responseif 'num' in request.COOKIES['num' ]: num = request.COOKIES['num' ] else : num = 0
Session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 request.session['num' ] = 1 return HttpResponse('...' )if 'num' in request.session['num' ]: num = request.session['num' ] else : num = 0 num = request.session.get('num' , '0' ) request.session.clear() request.session.flush() del request.session['key' ]request.session.set_expiry(24 *3600 )
设计分页 我们也可以使用index?page=1的方式传递GET参数,分页也一般采用这种方式查看当前访问的是第几页。通过GET参数获取请求的分页,为了获取GET参数,可以使用如下:
1 2 3 4 5 6 page = request.GET.get('page' ) if page: page = int (page) else : page = 1
Django自带了分页组件。分页组件及其常用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from django.core.paginator improt Paginatorp = Paginator(one_list, 3 ) p.num_pages p.page_range page = p.page(1 ) page.number page.object_list page.paginator page.has_next() page.has_previous() page.previous_page_number page.next_page_number
Django 路由 Path 语法 Django Path默认支持五个转化器:
1 2 3 4 5 str:匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式 int:匹配正整数,包含0。 slug:匹配字母、数字以及横杠、下划线组成的字符串。 uuid:匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。 path:匹配任何非空字符串,包含了路径分隔符
具体用法例如:
1 2 3 4 5 6 urlpatterns = [ path('articles/2003/' , views.special_case_2003), path('articles/<int:year>/' , views.year_archive), path('articles/<int:year>/<int:month>/' , views.month_archive), path('articles/<int:year>/<int:month>/<slug>/' , views.article_detail), ]
用户也可以自定义转化器,自定义的转化器需要使用实现转化器接口。实现方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class IntConverter : regex = '[0-9]+' def to_python (self, value ): return int (value) def to_url (self, value ): return str (value) class StringConverter : regex = '[^/]+' def to_python (self, value ): return value def to_url (self, value ): return value class FourDigitYearConverter : regex = '[0-9]{4}' def to_python (self, value ): return int (value) def to_url (self, value ): return '%04d' % value
定义完成后,将其注册到配置中。在需要的urls.py中添加:
1 2 3 4 5 6 7 8 from django.urls import register_converterfrom . import converters register_converter(converters.FourDigitYearConverter, 'yyyy' ) urlpatterns = [ path('articles/<yyyy:year>/' , views.year_archive), ]
如果嫌自制转化器太繁琐,可以使用兼容Django 1中的正则表达式的方式直接匹配。
1 2 3 4 5 6 urlpatterns = [ path('articles/2003/' , views.special_case_2003), re_path('articles/(?P<year>[0-9]{4})/' , views.year_archive), re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/' , views.month_archive), re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[^/]+)/' , views.article_detail), ]
Django 模板 模板加载顺序 加载一个模板,首先是查找配置的模板目录,如果找不到,再去INSTALLED_APPS下的templates查找。这一过程是Django自动的。
模板变量 模板变量,不能以下划线开头。
下面两种情况,有两种解析顺序。
其解析顺序为:
作为字典,取键值
作为对象,取属性
作为对象,当作对象的方法
都无法匹配,则替换为空字符串
其解析顺序为:
作为字典,取键值
作为列表,取下标
都无法匹配,则替换为空字符串
模板标签 后端给标签变量赋值可以使用render函数。
1 2 3 render(request, 'index.html' , { 'author_list' : AuthorModel.objects.all () })
前端的模板标签主要如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for循环标签 {% for x in author_list %} 列表不为空时 {{ forloop.counter }} 记录循环第几次 {% empty %} 列表为空时 {% endfor %} if标签 {% if 条件 %} 操作符旁边必须有空格 {% elif %} {% else %} {% endif %} 注释 {# 单行注释 #} {% comment %} 多行注释 {% endcomment %}
模板过滤器 过滤器是用在前端的标签函数,用于对模板变量做操作。
1 2 3 4 5 6 7 8 9 10 过滤器格式为 {{ 变量|过滤器:参数 }} 改变日期的显示格式 {{ book.publish_date|date:"Y年-m月-d日" }} 求长度 {{ book.title|length }} 设置默认值 {{ book.title|default:"No Title" }} 自定义过滤器(是否是奇数) {{ author.age|mod:1 }}
自定义过滤器的定义应在my_app目录下的templatetags下。templatetags应该有一个__init__.py
文件,保证该目录可以被Python识别。
这里创建一个filters.py
文件用于开发自定义过滤器。自定义标签也可以写到这里。filters.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from django import templateregister = template.Library() def mod (value, arg ): return value % arg register.filter ('mod' , mod) @register.filter(name='lower' ) def lower (value ): return value.lower()
在需要使用的模板上加载过滤器。
模板继承 网页往往会有很多重复的内容,因此我们可以制作一个父页面,子页面继承主页面显示以减少重复代码。
父页面base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > Hello</h1 > {% block topics %} 默认显示内容 {% endblock topics %} </body > </html >
子页面sub_page.html
1 2 3 4 5 6 7 8 9 {% extends 'base.html' %} {% block topics %} 新内容 获取父模板的内容 {{ block.super }} {% endblock topics %}
模板转义 默认情况下,模板上下文(由后端传递过来)中的html标记会被转义显示,即模板中的<>
会被转化为<>
。因此要关闭转义显示,可以使用标签
1 2 3 4 5 6 7 方式 1 {{ 变量|safe }} 方式 2 {% autoescape off %} 模板语言代码 {% endautoescape %}
Django 用户登录 登录装饰器 有些页面是用户登录之后才可以访问的,例如修改密码,修改昵称等,也就是这些页面首先要进行用户登录的判断,否则让用户跳转回登录页面。
我们可以通过函数装饰器的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def login_required (view_func ): def wrapper (request, *view_args, **view_kwargs ) if true: return view_func(request, *view_args, **view_kwargs) else : return redirect('/login' ) pass return wrapper @login_required def change_pwd (request ): return HttpResponse('Change Password' )
CSRF 攻击 CSRF 攻击即跨站请求伪造攻击。我们在访问某一网站时,例如银行网站,在访问的结束后再去访问其他的网站,就会致使我们所有保存在浏览器上的数据包都会暴露给第三方网站,如果第三方网站上有某些攻击脚本,例如在用户不知情的情况下,再次利用刚才的数据包(SessionID)访问银行网站进行一些危险的操作,我们的数据就会产生泄露甚至丢失的危险。
由于伪造的网站与真实的网站的IP或主机名是不一样的,所以根据这一特性,我们可以也防止这种跨站请求伪造攻击。
Django 默认是启用这种 CSRF 攻击保护的(只针对POST),但同时也带来了不便,因为我们有时自己的网站也会被防护,导致自己的网站都无法正常浏览。
解决这一问题,可以在模板中的表单里添加如下内容即可。
内置表单 Django中内置了表单。用户可以通过Django内置的表单生成器自动生成表单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from django import formsclass LoginForm (forms.Form): username = forms.TextField() password = forms.TextField(widget=forms.PasswordInput) def login_handler (request ): if request.method == "POST" : login_form = LoginForm(request.POST) if login_form.is_valid(): user = login_form.cleaned_data['username' ] pswd = login_form.cleaned_data['password' ] if user: return HttpResponse('成功登录' ) else : return HttpResponse('登录失败' ) else : return HttpResponse("输入不合法" ) def login (request ): login_form = LoginForm() return render(request,'login.html' , {"forms" :login_form})
1 2 3 4 5 <form action ="." method ="post" > {% csrf_token %} {{ forms }} <input type ="submit" value ="Login" > </form >
验证码 django-simple-captcha 官方文档 首先按照验证码库:
1 pip install django-simple-captcha
在项目settings.py中配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 INSTALLED_APPS = [ ... "captcha" , ] CAPTCHA_IMAGE_SIZE = (80 , 45 ) CAPTCHA_LENGTH = 4 CAPTCHA_TIMEOUT = 1 CAPTCHA_OUTPUT_FORMAT = '%(image)s %(text_field)s %(hidden_field)s ' CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_null' , 'captcha.helpers.noise_arcs' , 'captcha.helpers.noise_dots' , ) CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge' CAPTCHA_TIMEOUT = 1
在项目urls.py中配置:
1 path('captcha/' , include('captcha.urls' ))
完成后迁移数据库
1 2 python manage.py makemigration python manage.py migrate
创建登录表单,验证码在登录时由表单自动完成验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from django import formsfrom captcha.fields import CaptchaFieldclass LoginForm (forms.Form): username = forms.TextField() password = forms.TextField(widget=forms.PasswordInput) captcha = CaptchaField() def login_handler (request ): if request.method == "POST" : login_form = LoginForm(request.POST) if login_form.is_valid(): user = login_form.username if user: '''用户登陆后,Django会自动调用默认的session应用,将用户的id存至session中''' return HttpResponse('成功登录' ) else : return HttpResponse('登录失败' ) else : return HttpResponse("输入不合法" ) def login (request ): login_form = LoginForm() return render(request,'login.html' , {"forms" :login_form})
如果想要点击验证码实现验证码更新,则可以使用如下操作(需要jQuery)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from captcha.helpers import captcha_image_urlfrom captcha.models import CaptchaStoreimport jsondef captcha_refresh (request ): """ Return json with new captcha for ajax refresh request """ if not request.is_ajax(): raise Http404 new_key = CaptchaStore.generate_key() to_json_response = { 'key' : new_key, 'image_url' : captcha_image_url(new_key), } return HttpResponse(json.dumps(to_json_response), content_type='application/json' )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <script > $(function(){ # 改变鼠标箭头 $('.captcha').css({ 'cursor': 'pointer' }) # ajax 刷新 $('.captcha').click(function(){ console.log('click'); $.getJSON("/captcha/refresh/", function(result){ $('.captcha').attr('src', result['image_url']); $('#id_captcha_0').val(result['key']) });}); # ajax动态验证 $('#id_captcha_1').blur(function(){ // #id_captcha_1为输入框的id,当该输入框失去焦点是触发函数 json_data={ 'response':$('#id_captcha_1').val(), // 获取输入框和隐藏字段id_captcha_0的数值 'hashkey':$('#id_captcha_0').val() } $.getJSON('/ajax_val', json_data, function(data){ //ajax发送 $('#captcha_status').remove() if(data['status']){ //status返回1为验证码正确, status返回0为验证码错误, 在输入框的后面写入提示信息 $('#id_captcha_1').after('<span id ="captcha_status" > *验证码正确</span > ') }else{ $('#id_captcha_1').after('<span id ="captcha_status" > *验证码错误</span > ') } }); }); }) </script >
当然,高级玩家可以自己画验证码。 下面自制验证码:
安装Pillow包
定义一个验证码生成函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from PIL import Image, ImageDraw, ImageFontfrom django.utils.six import BytesIOdef verify_code (request ): import random bgcolor = (random.randrange(20 , 100 ), random.randrange(20 , 100 ), 255 ) width = 100 height = 25 img = Image.new('RGB' , (width, height), bgcolor) draw = ImageDraw.Draw(img) for i in range (0 , 100 ): xy = (random.randrange(0 , width), random.randrange(0 , height)) fill = (random.randrange(0 , 255 ), 255 , random.randrange(0 , 255 )) draw.point(xy, fill=fill) str_back = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' rand_str = '' for i in range (0 , 4 ): rand_str += str_back[random.randrange(0 , len (str_back))] font = ImageFont.truetype('FreeMono.ttf' , 23 ) fontcolor = (255 , random.randrange(0 , 255 ), random.randrange(0 ,255 )) for i in range (0 , 4 ): draw.text((5 + 24 *i, 2 ), rand_str[i], font=font, fill=fontcolor) del draw request.session['verify_code' ] = rand_str buf = BytesIO() im.save(buf, 'png' ) return HttpResponse(buf.getvalue(), 'image/png' )
URL 反向解析 在模板里面,可以将链接到其他页面的超链接写成动态的,这样可以保证修改链接后自动修改所有链接到某页的路径。
在项目urls.py中,添加namespace属性:
1 2 3 4 url_patterns = [ ... path('my_app/' , include('my_app.urls' , namespace='my_app' )) ]
在应用urls.py配置中,添加name属性:
1 2 3 4 url_patterns = [ ... path('index_renamed/' , views.index, name='index' ) ]
在模板中:
1 2 3 4 5 用法:url 'namespace:name' 参数 <a href ="{% url 'my_app:index' %}" > 首页</a > <a href ="{% url 'my_app:index' arg1 arg2 %}" > 带位置参数的首页</a > <a href ="{% url 'my_app:index' a=arg1 b=arg2 %}" > 带关键字参数的首页</a >
在视图中使用反向解析:
1 2 3 4 5 6 7 from django.core.urlresolvers import reversedef test_redirect (request ): url = reverse('my_app:index' , args=('arg1' , 'arg2' )) url = reverse('my_app:index' , kwargs={'a' ='arg1' , 'b' ='arg2' }) return redirect(url)
中间件 是Django预留的函数接口,允许我们干预请求和应答。例如对客户端进行过滤,防止DDoS攻击等。
中间件可以允许我们在执行视图函数之前自动执行中间件。中间件的执行流程如下:
1 2 3 4 5 6 7 8 9 10 11 st=>start: 请求到达服务器 op1=>operation: 产生Request对象 op2=>operation: 调用process_request op3=>operation: 匹配URL op4=>operation: 调用process_view op5=>operation: 调用视图函数 op6=>operation: 调用process_response op6=>operation: 返回给浏览器 e=>end st->op1->op2->op3->op4->op5->op6->e
在setting.py中注册中间件,注册顺序与执行顺序相反。
1 2 3 4 MIDDLEWARE = [ ... 'my_app.middleware.Block_Middleware' ]
在my_app目录下建立middleware.py
文件,编辑此文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Block_Middleware (object ): def process_view (self, request, view_func, *view_args, **view_kwargs ): user_ip = request.META('REMOTE_ADDR' ) if user_ip in ['127.0.0.1' ]: return HttpRequest('Go Back' ) def __init__ (self ): pass def process_request (self, request ): pass def process_response (self, request, response ): return response def process_exception (self, request, exception ): pass
注意:如果在中间件的任意一个函数返回response,后续的过程将不会执行,而是直接将结果交给process_response,再返回浏览器。
Django Shell 就是带Django相关功能的Python Shell。可以方便开发者调试代码。 例如,使用Django Shell添加一条数据库的记录。首先进入Shell
进入后,可以执行如下常用操作:
1 2 3 4 5 from my_app.models import AuthorModel, MyBookModelfrom datetime import dateprint (a.mybookmodel_set.all ()[0 ])
Django Admin 模块 Django标配的后台管理工具,使用方便,可以快速编辑很多内容。
首先创建用户:
1 python manage.py createsuperuser
填写用户名与密码,这里可能要求密码长度大于8位且不能为纯数字。
之后运行查看效果:
1 python manage.py runserver 8080
http://127.0.0.1:8080/admin/
进入后台后,可以看到管理页面中出现 Groups 与 Users,编辑这两项添加用户与用户组。 如果想将my_app的模型MyModel也加入其中,可以到my_app下的admin.py中编辑:
1 2 3 from my_app.models import MyBookModel, AuthorModeladmin.site.register(MyBookModel) admin.site.register(AuthorModel)
也可以使用自定义管理页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class MyBookModelAdmin (admin.ModelAdmin): list_display = ['id' , 'title' , 'title_func' ] list_per_page = 10 actions_on_bottom = True actions_on_top = False list_filter = [ 'title' ] search_fields = [ 'title' ] fields = [ 'title' , 'id' ] fieldsets = [ ('Base' , {'fields' : ['title' , 'id' ]}), ('Advance' , {'fields' : []}) ] inlines = [ AreaStackedInline, AreaTabularInline ] class AreaStackedInline (admin.StackedInline): model = AreaInfo extra = 2 class AreaTabularInline (admin.TabularInline): model = AreaInfo extra = 2 admin.site.register(MyBookModel, MyBookModelAdmin) admin.site.register(AuthorModel)
如果要重写模板,可以在templates下建立base_site.html
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 {% extends "admin/base.html" %} {# 标题 #} {% block title %} {{ title }} | {{ site_title|default:_('Django site admin') }} {% endblock %} {# 展框 #} {% block branding %} <h1 id ="site-name" > <a href ="{% url 'admin:index' %}" > {{ site_header|default:_('Django administrator') }} </a > </h1 > {% endblock %} {# 导航栏 #} {% block nav-global %} {% endblock %}
Django 上传 配置settings.py文件:
1 2 MEDIA_URL = '/static/media' MEDIA_ROOT = os.path.join(BASE_DIR, 'my_app/static/media' )
模板上,上传图片的表单配置如下:
1 2 3 4 5 <form method ="post" action ="/my_app/upload_action" enctype ="multipart/form-data" > {% csrf_token %} <input type ="file" name ="pic" /> <br /> <input type ="submit" value ="upload file" /> </form >
视图中,获取文件并保存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def upload_handle (request ): image = request.FILES['pic' ] save_path = '%s/my_book_model/%s' %(settings.MEDIA_ROOT, image.name) with open (save_path, 'wb' ) as f: for chk in image.chunks(): f.write(chk) m = MyBookModel.objects.get(id =1 ) m.picture = 'my_book_model/%s' %image.name m.save()
WebSocket Django-channels 开发流程总结 需求分析 网站设计 数据库设计 URL设计
URL
视图
模板文件
/login
login
login.html
创建项目 模型编辑 视图编辑 路由配置 其他 虚环境 虚环境的安装 1 2 sudo pip install virtualenv sudo pip install virtualenvwrapper
虚环境的常用命令 1 2 3 4 mkvirtualenv -p python3 name deactivate workon name rmvirtualenv name
CMD下进入虚环境:
1 ./venv/Scripts/activate.bat
PowerShell下进入虚环境:
1 2 3 4 Set-ExecutionPolicy RemoteSigned ./venv/Scripts/activate.psl
查看虚环境下已安装的包 1 2 pip list pip freeze > requirements.txt
MySQL 基本操作 开启日志文件,需要修改mysql.conf文件。
1 2 # 实时查看日志文件 tail -f mysql.log
数据可视化 pyecharts xlrd Excel 操作