django_关键点
安装
打开命令提示符,然后输入pip3 install django
创建优先级
- 配置模板的路径:templates
- 配置静态目录的路径:static
相关命令
python manage.py runserver 127.0.0.1:8001 #运行服务器(这个需要命令提示符进入到对应django目录)
django-admin startproject app_name #创建django项目名(这个需要命令提示符进入到对应django目录C:\Python36\Scripts下运行)
python manage.py startapp app_name #创建django的APP(这个需要命令提示符进入到对应django目录下运行)
python manage.py makemigrations #(这个需要命令提示符进入到对应django目录下运行)
python manage.py migrate #(这个需要命令提示符进入到对应django目录下运行)
python manage.py createsuperuser # 创建超级管理员(这个需要命令提示符进入到对应django目录下运行)
目录相关信息:
mysite -mysite # 对整个程序进行配置 -init -settings # 配置文件 -url # url对应关系 -wsgi # 是一个接口,遵循WSGI规范,上线的时候一般不使用默认的,使用第三方的uwsgi + nginx -managy.py # 管理django程序: app: migrations # 数据库变化的记录(修改表结构) admin # django为我们提供的后台管理 apps # 配置当前APP的 models # ORM,写指定的类,通过命令可以创建数据库结构 tests # 单元测试 views # 跟APP相关的业务,写业务代码
CVB:class base view
url.py : urlpatterns = [ url(r'^home/', views.Home.as_view()), ] view.py class Home(View): def get(self,request): '''执行get方法''' return redirect('/cmdb/index/') def post(self,request): '''执行post方法''' return redirect('/cmdb/index/')
from django.views import View class Home(View): def dispatch(self, request, *args, **kwargs): '''一般都是先执行该函数,继承了之后,可以对请求之后进行操作''' print('before') result = super(Home,self).dispatch(request, *args, **kwargs) print('after') return result def get(self,request): '''执行get方法''' return redirect('/cmdb/index/') def post(self,request): '''执行post方法''' return redirect('/cmdb/index/')
FBV:function base view(默认的使用方式)
两者没有哪个好,哪个不好,两者都可以使用
URL
from django.conf.urls import url
url(r'^asssssddd/',views.index,name = 'indexx')
url(r'^asssssddd/(\d+)/',views.index,name = 'indexx')
url(r'^user_detail-(?P<nid>\d+)/', views.user_detail), # 正则表达式,对应的函数必须要有参数,这里是一个,所以参数是一个
name:访问的url可以指定
对URL路由关系进行命名,以后可以根据此名称生成自己想要的URL url(r'^asssssddd/',views.index,name = 'indexx') 模板语言: {% url "indexx" %} url(r'^asssssddd/(\d+)/',views.index,name = 'indexx') 模板语言(需要增加对应的参数): {% url "indexx" 3 %}
reverse:可以name指定的url转换为绝对URL
url(r'^asssssddd/(\d+)/',views.index,name = 'indexx') # 反转,根据name的路径转换出对应路径,这里是一个参数,所以args是一个数, from django.urls import reverse v = reverse("indexx",args=(80,))
url(r'^asssssddd/(?P<nid>\d+)/(?P<uid>\d+)/',views.index,name = 'indexx') # 反转,根据name的路径转换出对应路径 from django.urls import reverse v = reverse("indexx",kwargs={'nid':1,'uid':'99'}) 模板语言 {% url "indexx" nid=1 uid=3 %}
include
# 路由会自动切换到app对应目录下的urls.py文件 from django.conf.urls import url,include urlpatterns = [ url(r'^cmdb/',include('app01.urls')), url(r'^monitor/',include('app02.urls')), ] # 对应的app里面建立urls.py文件
HttpResponse,render,redirect
导入:from django.shortcuts import render,redirect,HttpResponse
HttpResponse:返回字符串到前端
def index(request): return HttpResponse('ok')
redirect:跳转到其他路径,重定向,
def index(request): return redirect('/crm/king_admin/')
render:自动打开对应文件,读取并将内容返回给前端
def index(request): print(king_admin.enabled_admins) return render(request, 'king_admin/table_index.html', {'table_list':king_admin.enabled_admins})
ocals()可以直接将函数中所有的变量全部传给模板
return render(request,'user_audit.html',locals())
settings
如果APP需要导入settings文件,可以这样写
from django.conf import settings
templates:TEMPLATES 里面的‘DIRS’,加上os.path.join(BASE_DIR,'templates')
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], #设定HTML默认路径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', ], }, }, ]
static:静态文件放置路径
STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), ] #备注:django2.0以后必须要使用[]才能获取对应的静态文件路径
注册APP:对应app写上,django才能找到对应的models.py,对里面的类进行操作
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'PublicInfo.apps.PublicinfoConfig', # 将对应app写上,django才能找到对应的models.py,对里面的类进行操作 ]
数据库:默认为sqllite,若需要修改为mysql,如下修改
# 参考文档 https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'PerfectSet', 'USER': 'root', 'PASSWORD': 'Choice123', 'HOST': '', 'PORT': '3306', }, }
session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)这个相当于写cookie的时候不写超时时间 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)True的时候,每次操作,都让session失效时间刷新,例如,无操作20秒后session自动失效
中间件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # CSRF 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
request
包含了客户端用户发过来的所有信息(要想知道该对象包含了哪些方法,可以用type(request)获取对应的类,然后进入类查看)
request.environ :封装了所有用户请求信息,字典(根据请求头的信息,可以分辨到是电脑端还是手机端,需要先导入from django.core.handlers.wsgi import WSGIRequest)
request.method:提交方式,访问的时候是get,form表单提交的时候是post(获取的是类似字典的信息)
request.POST:获取post获取的信息:eg:reqeust.POST.get('user',None)
request.GET:获取get方式的信息
request.GET.getlist('str') #获取前端发过来的多个值,如CheckBox等多选的内容
request.META #
request.FILES #获取前端发送过来的文件所有信息
obj = request.FILES.get('str') obj.name # 文件名 obj.size # 文件代销 obj.chunks() # 获取的一段文件信息
# django处理文件一般是用request.FILES,request.FILES.get是获取文件信息,默认返回是文件名称 obj = request.FILES.get('file') print(obj,type(obj)) import os file_path = os.path.join('upload',obj.name) f = open(file_path,mode='wb') # chunks 相当于一段段获取文件信息 for i in obj.chunks(): f.write(i) f.close()
request.path_info # 当前url的访问路径
模板语言
循环:
{% for row in user_list %}
{{forloop.counter}} # 只要是for循环都有一个forloop,这里是计数,从1开始 {% endfor %}
counter # 从1开始计数 counter0 # 从0开始计数 revcounter # 倒序,最后为1 revcounter0 # 倒序,最后为0 last # 是否是最后一个,是的为True first # 是否是第一个循环,是未True parentloop # 多循环中,获取上一层循环的内容
if。。。else
{% if true%}... {% else %}... {% endif %}
获取后端信息
row.username
获取后端的字典
# 获取key
{% for k in user_dict.keys %}
{% endfor %}
# 获取value
{% for v in user_dict.values %}
{% endfor %}
#获取key、values
{% for k,v in user_dict.items %}
{% endfor %}
ORM
参考:https://www.cnblogs.com/wupeiqi/articles/6216618.html
创建类
1.根据类自动创建数据库表( APP目录下的models.py):默认为app名称_类名,eg:app01_userinfo
2.根据类对数据库表中的数据进行各种操作
导入模块:from django.db import models
类需要继承models.Model
from django.db import models # Create your models here. class Servers(models.Model): ''' 服务器信息,ip地址 服务器管理账号 管理密码 远程端口 is_used:是否启用 ''' ip = models.GenericIPAddressField() user = models.CharField(max_length=64) pwd = models.CharField(max_length=256) port = models.IntegerField(default=3306) system = models.ManyToManyField('SystemInfo',null=True,blank=True) loca_id = models.ForeignKey('Location',on_delete=models.CASCADE) is_used = models.BooleanField(default=True)
数据库操作
增:
# (推荐使用第一种) models.UserInfo.objects.create(username='root',password='123') 或者: obj= models.UserInfo(username='root',password='123')
# 这个函数会自动帮忙验证,出错就抛出异常
obj.full_clean()
obj.save()
或者: dic = {'username':'eric','password':'666'} models.UserInfo.objects.create(**dic')
查:
# 获取所有数据,得到django提供的QuerySet类型数据:[obj1对象(id,username.password),obj2,obj3] result = models.UserInfo.objects.all()
# 也是QuerySet数据,查找特定的列,类似select id,caption from business,内部元素返回的是字典
v2 = models.Business.objects.all().values('id','caption')
# 也是QuerySet数据,查询获取的结果,内部元素返回的是元组
v3 = models.Business.objects.all().values_list('id','caption')
result = models.UserInfo.objects.all().query() # 可以将执行的sql语句显示
# 条件查询,得到django提供的QuerySet类型数据[] result = models.UserInfo.objects.filter(id=3) result = models.UserInfo.objects.filter(username='root',password='123')
# 获取单个
result = models.UserInfo.objects.filter(id=3).first()
# 获取个数
result = models.UserInfo.objects.filter(id=3).count()
删:
# 删除所有 models.UserInfo.objects.all().delete() # 删除单个,或者删除所有username叫alex的 models.UserInfo.objects.filter(id=4).delete() models.UserInfo.objects.filter(username=alex).delete()
改(更新):
# 将所有的password改为666 models.UserInfo.objects.all().update(password=666) # id为3 的那个password改为666 models.UserInfo.objects.filter(id=3).update(password=666)
# 批量插入
bulk_create(self,objs,batch_size=None):
优化:
elect_related
# 仅仅对用户表中的数据查询,关联表的数据没有查询 users = models.User.objects.all() for row in users: print(row,user) # 查询关联表数据,会再进行数据库操作 pring(row.ut.name) # 这样已经对跨表进行操作,进行一次数据库查询,但是获取的是字典 users = models.User.objects.all().values('user','ut__name') 优化: # 一次操作就将关联的表所有数据都取到,但是数据太多 users = models.User.objects.all().select_related() 再优化: # 这样就关联了对应外键的数据 users = models.User.objects.all().select_related('ut')
prefetch_related
# 不会做连表查询,会做两次sql请求 users = models.User.objects.filter(id__gt=30).prefetch_related('ut') # 执行 # select * from users where id > 30 # 获取上一步骤中所有的ut_id = [1,2] # select * from user_type where id in [1,2] for row in users: print(row,user) # 查询关联表数据,不会再进行数据库操作 pring(row.ut.name)
Django admin
写在APP下的admin.py文件里面,必须导入 from django.contrib import admin
models.py里面的类创建字段时候的参数
primary_key = True # 是否主键 null=True # db是否可以为空 default='默认值' # 默认值 db_coloumn = '列名' # 列名 unique=True # 是否唯一 db_index=True # 索引 unique_for_date unique_for_month unique_for_year auto_now=True # 创建时,自动生成 auto_now_add=True # 更新时,自动更新为当前时间 choices # django admin中显示下拉框;避免联表查询 blank=True # django admin中是否可以为空 verbose_name='中文名' # django admin中显示字段中文 editable=False # django admin中不可以编辑 error_messages={‘required’:'请输入密码'} # 错误信息显示中文 help_test=‘帮助信息’ # 在旁边一个提示信息 validators # django form的自定义验证机制
system_type_choices = ( (0,'windows 2008'), (1,'windows 2012') ) system_type = models.SmallIntegerField(choices=system_type_choices,verbose_name='操作系统类型',default=0)
外键
to_field 跟表的哪个字段进行关联,不写的情况,默认是主键
on_delete:若删除表的时候,操作的方式
user_id = models.ForeignKey('UserInfo',verbose_name='班级',on_delete=models.CASCADE)
创建数据 models.UserInfo.objects.create( username = 'alex', password = '123', email = '123@qq.com', user_group = models.UserGroup.objects.filter(id=1).first() ) # 这里只需要对数据库操作一次,所以建议使用这个 models.UserInfo.objects.create( username = 'alex', password = '123', email = '123@qq.com', user_group_id = 1(id=1).first() )
通过点跨表获取数据 :'.'
v2 = models.Business.objects.all().values('nid', 'hostname', 'b__id', 'b__caption') # print(type(v2)) for row in v2: print(row['nid'], row['hostname'], row['b_id'], row['b__caption']) print(row.nid,row.b.caption)
字符里面通过双下杠获取数据库数据:'__' 只要是object后面写的,都是用爽下划线跨表
# # 因为引号里面是字符串,所以使用双下杠__引用对应对象下的字段,得到结果为元组,下标安装下方的写的参数从0排序+1 v3 = models.Business.objects.all().values_list('nid', 'hostname', 'b_id', 'b__caption')
当数据库字段为外键的时候,b对应是对象的情况
h = request.POST.get('hostname') i = request.POST.get('ip') p = request.POST.get('port') b = request.POST.get('b_id') models.Host.objects.create(hostname = h, ip = i, port = p, # b = models.Business.objects.get(id=b) b本身是对象,所以这里应该也是对象,但是这样做的话,导致数据库二次操作 b_id = b # 所以,应该是对应id,写成这样 )
多对多
自定义关系表:多个外键的一对多组成
class Host(models.Model): # django会自动生成id,但是也能自己写,需要加上primary_key=True,AutoField整形自增 nid = models.AutoField(primary_key=True) # 主机名,字符串,最大长度32位;db_index=True代表创建索引 hostname = models.CharField(max_length=32,db_index=True) # ip,字符串类型,但是django会自动帮我验证,protocol='both'意思是ipv4以及ipv6都支持 ip = models.GenericIPAddressField(protocol='both',db_index=True) # 端口,数字类型 port = models.IntegerField() # 与表Business关联,to是关联的表,to_field是关联的字段 b = models.ForeignKey(to="Business",to_field='id',on_delete=models.CASCADE) class Application(models.Model): ''' 应用 ''' name = models.CharField(max_length=32) # class HostToApp(models.Model): ''' host app关联 ''' hobj = models.ForeignKey(to=Host,to_field='nid',on_delete=models.CASCADE) aobj = models.ForeignKey(to=Application,to_field='id',on_delete=models.CASCADE)
自动创建关系表(最多三列):ManyToManyField
class Host(models.Model): # django会自动生成id,但是也能自己写,需要加上primary_key=True,AutoField整形自增 nid = models.AutoField(primary_key=True) # 主机名,字符串,最大长度32位;db_index=True代表创建索引 hostname = models.CharField(max_length=32,db_index=True) # ip,字符串类型,但是django会自动帮我验证,protocol='both'意思是ipv4以及ipv6都支持 ip = models.GenericIPAddressField(protocol='both',db_index=True) # 端口,数字类型 port = models.IntegerField() # 与表Business关联,to是关联的表,to_field是关联的字段 b = models.ForeignKey(to="Business",to_field='id',on_delete=models.CASCADE) class Application(models.Model): ''' 应用 ''' name = models.CharField(max_length=32) # 第三张表自动生成 r = models.ManyToManyField('Host')
建议:以后使用,两种皆可用
使用:
增:
obj = Application.objects.get(id=1) # 增加 obj.r.add(1) # 增加对应关系1:1 obj.r.add(2) # 增加对应关系1:2 obj.r.add(2,3,4) obj.r.add(*[1,2,3,4]) # 增加对应关系 1:1,1:2,1:3,1:4
删:
obj.r.remove(1) # 删除对应关系1:1 obj.r.remove(2,3,4) #删除对应关系1:2,1:3,1:4 obj.r.remove(*[1,2,3,4]) # 删除对应关系1:1,1:2,1:3,1:4 obj.r.clear() # 删除所有1:?的对应关系
改:
obj.r.set([3,5,7]) # 只剩下对应关系1:3,1:5,1:7
查:
# 查(Application.objects后面能加什么,这里就能加什么) obj.r.all() # 查询所有
前端获取信息
{% for host in app.r.all %} <span >{{ host.hostname }}</span> {% endfor %}
Ajax
本质上,是执行下面代码
obj = XMLHttpRequest()
obj.open()
obj.send()
建议:永远让服务器端返回一个字典
使用 return HttpResponse(json.dumps(字典))
HTML页面,基于jQuery的前提下实现
$.ajax({ url:'/host', type:'POST', data:{'k1':'v1','k2':v2} # 发送的数据 {# 允许发送列表,不写这个,发送列表的情况会报错#} traditional:true, {# 写上这句就能让其自动反序列化,所以传递进入函数的就变成了对象#} dataType:"JSON", success:function(data){ # 等到服务器返回值后,自动执行,若上面写了dataType:"JSON",data就是对象,否则需要用JSON.parse(data) } {# 若是拿去数据不成功的时候执行的函数#} error:function () { } })
EG1:
def test_ajax(request): # request.GET.get('pwd',sep='\t')获取pwd的值,并且隔开 # print(request.method,request.GET.get('user'),request.GET.get('pwd',sep='\t')) ret = {'status': True, 'error': None, 'data': None} try: h = request.POST.get('hostname') i = request.POST.get('ip') p = request.POST.get('port') b = request.POST.get('b_id') print(h) print(i) print(p) print(b) if h and len(h) > 5: models.Host.objects.create(hostname=h, ip=i, port=p, b_id=b ) else: ret['status'] = False ret['error'] = '太短了' except Exception as e: ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret))
$('#ajax_submit').click(function () { $.ajax({ url:'/test_ajax', type:'POST', {# data:{'user':'root','pwd':'123'},#} {# 获取输入值传递到服务器#} data : {'hostname':$('#hostname').val(),'ip':$('#ip').val(),'port':$('#port').val(),'b_id':$('sel').val()}, {# 服务器返回信息后,传递到参数data,才执行下面的函数#} success:function (data) { {# 反序列化服务器端发过来的json数据#} var obj = JSON.parse(data); {#如果通过验证#} if(obj.status){ {#刷新#} location.reload() }else { {#不通过验证的情况下,将错误信息显示到页面#} $('#error_msg').text(obj.error); } {# if(data == 'ok'){#} {# 刷新页面#} {# location.reload()#} {# }#} {# else {#} {# alert(data)#} {# alert(data)#} } }) });
伪Ajax
iframe变更
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <input type="text" id="url" /><input type="button" value="发送iframe请求" onclick="ifrClick();" />#} {# <iframe id="ifm" src="http://wwww.baidu.com" style="width: 500px;height: 500px;">#} {##} {# </iframe>#} <form method="post" action="/ajax_json/" target="ifm1"> {% csrf_token %} <iframe name="ifm1"></iframe> <input type="text" name="username"/> <input type="text" name="email"/> <input type="submit" value="Form提交"/> </form> <script type="text/javascript" src="/static/jquery-1.12.4.js"></script> <script> function ifrClick() { var url = $('#url').val(); $('#ifm').attr('src',url) } </script> </body> </html>
使用的时机:如果发送的是【普通的数据】 ->Jquery,XMLHttpRequest,->iframe
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <input type="text" id="url" /><input type="button" value="发送iframe请求" onclick="ifrClick();" />#} {# <iframe id="ifm" src="http://wwww.baidu.com" style="width: 500px;height: 500px;">#} {##} {# </iframe>#} <form method="post" action="/ajax_json/" target="ifm1"> {% csrf_token %} <iframe name="ifm1" id="ifm1"></iframe> <input type="text" name="username"/> <input type="text" name="email"/> <input type="submit" onclick="sumbitForm();" value="Form提交"/> </form> <script type="text/javascript" src="/static/jquery-1.12.4.js"></script> <script> function ifrClick() { var url = $('#url').val(); $('#ifm').attr('src',url) } function sumbitForm() { $('#ifm1').load(function () { var text = $('#ifm1').contents().find('body').text(); console.log(text); var obj = JSON.parse(text) }) } </script> </body> </html>
def ajax_jason(request): ret = {'status':400,'data':'dsdsd'} import json return HttpResponse(json.dumps(ret))
基于ajax的文件上传
(参考https://www.cnblogs.com/cheng662540/p/9351284.html)
带iframe的,一般用于文件以及图片上传;
如果要显示图片,只需要将图片的路径返回到前端
可以利用onchange事件,选中文件就自动上传
模板
模板继承(可以多个)
{% extends 'HTML页面名称'%} # 声明继承哪个模板
{% extends 'master.html'%}
上一级模板:
{% block 名字 %} {% endblock %}
eg:
{% block content %} {% endblock %}
如果继承的模板直接写上上面的代码,则表示当前页面使用上一级页面的代码
如果代码块里面写了其他地方,则当前页面使用当前代码
模板导入
{% include 'xx.html'%}
eg:以下为例子 xx.html <form> <input type="text" /> <input type="submit" /> {{name}} # 这个在被导入的过程也是能被渲染的 </form> index.html {% include xx.html%} {% include xx.html%} # 可以导入多次
自定义 simple_tag
自带的,HTML文件里面写
{{item.event_start|date:"Y-m-d H:i:s"}} # 将传递过来的日期格式化
{{name|truncatewords:'30'}} # 截取前30个字符
自定义 simple_tag
1.APP下创建目录templatetags(名字只能是这个)
2.目录下创建任意.py文件,如xxoo.py
3.创建template对象,必须为register;代码如下:
from django import template from django.utils.safestring import mark_safe # 装饰器,名字不能修改,必须为register register = template.Library() # filter最多传递两个值,这个做法就是类似自带的那种写法 @register.filter def filter_test(a1,a2): return a1 + a2 @register.simple_tag def houyafan(a1,a2): return a1 + a2
4.settings文件中注册app
5.在HTML顶部写上:{% load xxoo %}(如果有{% extends 'xx.html'%},则写在这个下面)
6.{% 函数名 参数1 参数2 %}
{% houyafan 2 2 %} # 参数任意,但是不能用于if
{% a1|filter_test:a2 %} # 这个可以用于if写成
{% if a1|filter_test:a2 %}
{% endif %}
cookie:客户端浏览器上的一个文件,是保存在用户浏览器端的键值对,不适合放敏感信息
res = redirect('/index/')
# 设定cookie user111 为u,关闭浏览器失效 res.set_cookie('user111',u) res.set_cookie('key','value') return res
# 获取当前已经登陆的用户信息,因为写了装饰器,所以不需要了
v = request.COOKIES.get('user111')
cookie参数
参数: key, 键 value='', 值 max_age=None, 超时时间,单位为秒 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.),到指定日期时间就会失效 path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问 domain=None, Cookie生效的域名 secure=False, https传输 httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
eg:
res = redirect('/index/') # 设定cookie user111 为u res.set_cookie('user111',u) res.set_cookie('key','value') import datetime # 获取当前时区当前时间 current_date = datetime.datetime.utcnow() # 获取当前时间+多少秒 current_date = current_date + datetime.timedelta(seconds=10) # cookie保持10秒时间 res.set_cookie('key', 'value',max_age=10) res.set_cookie('key', 'value',expires=current_date) return res
jQuery有个cookie的插件,可以使用这个,读取cookie信息(jquery.cookie.js)
$.cookie('k1',v) # 设置cookie
$.cookie('k1') # 获取cookie
$.cookie('k1',v,{'path':'/user_list/'}) # 设置cookie,user_list/路径下的才生效
加密设置、获取cookie
obj = HttpResponse('ok') # 加密设置cookie obj.set_signed_cookie('k2','v2',salt='ddddswe') # 加密获取cookie request.get_signed_cookie('k2',salt='ddddswe')
基于cookie的装饰器,用于验证用户是否登陆
def auth(func): ''' 装饰器,如果发现cookie存储的不是用户名,则需要用户登录 :param func: :return: ''' def inner(request,*args,**kwargs): # 获取当前已经登陆的用户信息 v = request.COOKIES.get('user111') if not v: return redirect('/login/') return func(request,*args,**kwargs) return inner @auth def index(request): # 获取当前已经登陆的用户信息,因为写了装饰器,所以不需要了 v = request.COOKIES.get('user111') # if not v: # return redirect('/login/') return render(request,'index.html',{'name':v})
def auth(func): ''' 装饰器,如果发现cookie存储的不是用户名,则需要用户登录 :param func: :return: ''' def inner(request,*args,**kwargs): # 获取当前已经登陆的用户信息 v = request.COOKIES.get('user111') if not v: return redirect('/login/') return func(request,*args,**kwargs) return inner from django import views from django.utils.decorators import method_decorator # 在类上面写上这个@method_decorator(auth,name="dispatch")。相当于,在下写了第一个继承的函数,写了这个后,不用写下面的dispatch方法 @method_decorator(auth,name="dispatch") class Order(views.View): # 这里写一个之后,因为所有函数都先执行dispatch的,所以就类里面的所有函数都需要验证 @method_decorator(auth) def dispatch(self, request, *args, **kwargs): return super(Order,self).dispatch(request, *args, **kwargs) # 写在方法上,是单独对这个方法使用验证 # @method_decorator(auth) def get(self,request): # 获取当前已经登陆的用户信息 v = request.COOKIES.get('user111') return render(request, 'index.html', {'name': v}) def post(self,request): # 获取当前已经登陆的用户信息 v = request.COOKIES.get('user111') return render(request, 'index.html', {'name': v})
session:是保存在服务器端的键值对
session的原理:就是服务器端保存一个大字典,将字典的key发送到客户端保存,需要什么的时候,根据这个key就可以查询对应的信息
session默认情况下是保存在数据库里面的;默认2周有效;session依赖于cookie
''' 利用session,需要做四件事 1.生成随机字符串 2.写到用户浏览器cookie 3.保存到session中 4.在随机字符串对应的字段中设置相关内容 ''' # 这样一句话就完成上面四个操作 request.session['username'] = 'user'
def index(request): # 获取当前用户的随机字符串 # 根据随机字符串获取对应的信息 # request.session.get('is_login',None)意思是获取is_login信息,没有的时候为None if request.session.get('is_login',None): return render(request,'index.html',{'username':request.session['username']}) else: return HttpResponse('gun')
# 用于注销,只删除当前的cookie request.session.clear()
# session保存的时间,单位为秒 # 人为设置超时时间 request.session.set_expiry(3)
数据库session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) b. 使用 def index(request): # 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
缓存session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
文件session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
缓存+数据库session
数据库用于做持久化,缓存用于提高效率 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
加密cookiesession
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
CSRF :可以增强网站的安全性
若要使用这个,全局设置需要再settings文件里面的MIDDLEWARE加上
'django.middleware.csrf.CsrfViewMiddleware',
html里面的form表单需要加上
{% csrf_token %}
<form action="/login/" method="post"> <input type="text" name="user" /> <input type="password" name="pwd" /> <input type="checkbox" name="rmb" value="1" />3秒钟免登陆 <input type="submit" value="提交"/> <input id="btn1" type="button" value="按钮1"/> <input id="btn2" type="button" value="按钮2"/> {% csrf_token %} </form>
$(function () {
{# 全局变量,对函数内所有ajax有效#}
$.ajaxSetup({
beforeSend:function (xhr,settings) {
{# 设置请求头#}
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'));
}
});
$('#btn1').click(function () {
{# var csrftoken = $.cookie('csrftoken');#}
$.ajax({
url:'/login/',
type:'POST',
data:{'user':'root','pwd':'123'},
{# 发送post请求的时候,需要加上才放置csrf阻止,由于上面$.ajaxSetup已经配置,所以不需要单独配置#}
{# headers:{'X-CSRFtoken':$.cookie('csrftoken')},#}
success:function (data) {
console.log(1)
}
});
$('#btn2').click(function () {
{# var csrftoken = $.cookie('csrftoken');#}
$.ajax({
url:'/login/',
type:'POST',
data:{'user':'root','pwd':'123'},
{# 发送post请求的时候,需要加上才放置csrf阻止#}
{# headers:{'X-CSRFtoken':$.cookie('csrftoken')},#}
success:function (data) {
console.log(2)
}
})
})
});
函数前面加上@csrf_protect,则表示,这个函数需要做crsf认证
如果前面加上@csrf_exempt,则表示这个函数不需要做csrf认证
from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_exempt def index(request): # 获取当前用户的随机字符串 # 根据随机字符串获取对应的信息 # request.session.get('is_login',None)意思是获取is_login信息,没有的时候为None if request.session.get('is_login',None): return render(request,'index.html',{'username':request.session['username']}) else: return HttpResponse('gun')
信号
自带信号
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created def callback(sender, **kwargs): print("xxoo_callback") print(sender,kwargs) def callback1(sender, **kwargs): print("xxoo_callback") print(sender,kwargs) # 注册多个的情况下,按照顺序执行 xxoo.connect(callback) xxoo.connect(callback1) # xxoo指上述导入的内容 ;eg:request_finished.connect(callback) # 假设是一个根目录下简历一个xxoo.py文件,输入以上代码,若要使用,在目录文件名下的init导入该文件即可:import xxoo
自定义信号(可以做日志)
1.定义信号:
import django.dispatch # "toppings", "size"是要传递的两个参数 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
2.注册信号
def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback)
3.触发信号
# views.py里面使用
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456)
Form表单验证
1.帮用户验证
2.生成HTML代码
3.保留上次提交的信息内容
导入
from django import forms from django.forms import fields
建立类
class FM(forms.Form):
# 将获取到的数据传递到类FM obj = FM()
例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/fm/" method="post"> {% csrf_token %} {# <p><input type="text" name="user" />{{ obj.errors.user.0 }}</p>#} {# <p><input type="password" name="pwd" />{{ obj.errors.pwd.0 }}</p>#} {# <p><input type="text" name="email" />{{ obj.errors.email.0 }}</p>#} {# 可以生产HTML页面,提交错误的时候,提交的值也会在#} <p>{{ obj.user }}{{ obj.errors.user.0 }}</p> <p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p> <p>{{ obj.email }}{{ obj.errors.email.0 }}</p> <p>{{ obj.city1 }}{{ obj.errors.city.0 }}</p> <p>{{ obj.city2 }}{{ obj.errors.city.0 }}</p> <p><input type="submit" value="确定" /> </form> </body> </html>
from django import forms from django.forms import fields from app01 import models class FM(forms.Form): # 这里的变量与前端form里面的name一致才能获取对应的值 # user = forms.CharField() # 可以自定义对应错误信息 user = forms.CharField(error_messages={'required':'用户名不能为空。'}) # pwd = forms.CharField() pwd = forms.CharField( max_length=12, min_length=6, error_messages={'required':'密码不能为空。','min_length':'密码长度不能少于6','max_length':'密码长度不能大于12'} ) # 单选下拉 city1 = fields.ChoiceField( choices=[(0,'上海'),(1,'广州'),(2,'东莞')] ) # 多选下拉 city2 = fields.MultipleChoiceField( choices=[(0, '上海'), (1, '广州'), (2, '东莞')] ) # email = forms.EmailField() email = forms.EmailField(error_messages={'required':'邮箱不能为空。','invalid':'邮箱格式错误'}) def fm(request): if request.method == 'GET': # 从数据库中获取到数据 dic = { 'user':'root', 'pwd':'123123', 'email':'sada@111', 'city1':1, 'city2':[1,2] } # obj = FM() # 将获取到的数据传递到类FM obj = FM(initial=dic) return render(request,'fm.html',{'obj':obj}) elif request.method == 'POST': # 获取用户所有数据 # 每条数据请求的验证 # 成功后,获取所有的正确的信息 # 如果失败,显示错误信息 # 这次请求是post里面的数据,所以传入request.POST obj = FM(request.POST) # 去验证 r1= obj.is_valid() print(r1) if r1: # cleaned_data 是返回正确的信息,是个字典 print(obj.cleaned_data) # 这样就能创建对应的数据 models.User.objects.create(**obj.cleaned_data) else: # obj.errors是字典,是错误的信息 print(obj.errors) print(obj.errors.as_json()) print(obj.errors['user']) print(obj.errors['user'][0]) return render(request,'fm.html',{'obj':obj}) return redirect('/fm/')
HTML页面能够生成对应的标签(自定制性差):
django内置字段
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型
自定义验证规则
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定义验证规则 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '标题不能为空', 'min_length': '标题最少为5个字符', 'max_length': '标题最多为20个字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '标题5-20个字符'})) # 使用自定义验证规则 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = fields.EmailField(required=False, error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
from django import forms from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError from django.core.validators import RegexValidator class FInfo(forms.Form): username = fields.CharField(max_length=5, validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], ) email = fields.EmailField() def clean_username(self): """ Form中字段中定义的格式匹配完之后,执行此方法进行验证 :return: """ value = self.cleaned_data['username'] if "666" in value: raise ValidationError('666已经被玩烂了...', 'invalid') return value
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator ############## 自定义字段 ############## class PhoneField(fields.MultiValueField): def __init__(self, *args, **kwargs): # Define one message for all fields. error_messages = { 'incomplete': 'Enter a country calling code and a phone number.', } # Or define a different message for each field. f = ( fields.CharField( error_messages={'incomplete': 'Enter a country calling code.'}, validators=[ RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'), ], ), fields.CharField( error_messages={'incomplete': 'Enter a phone number.'}, validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')], ), fields.CharField( validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')], required=False, ), ) super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args, **kwargs) def compress(self, data_list): """ 当用户验证都通过后,该值返回给用户 :param data_list: :return: """ return data_list ############## 自定义插件 ############## class SplitPhoneWidget(widgets.MultiWidget): def __init__(self): ws = ( widgets.TextInput(), widgets.TextInput(), widgets.TextInput(), ) super(SplitPhoneWidget, self).__init__(ws) def decompress(self, value): """ 处理初始值,当初始值initial不是列表时,调用该方法 :param value: :return: """ if value: return value.split(',') return [None, None, None]
读取数据库信息,初始化数据:
# 从数据库中获取到数据 dic = { 'user':'root', 'pwd':'123123', 'email':'sada@111', 'city1':1, 'city2':[1,2] } # obj = FM() # 将获取到的数据传递到类FM obj = FM(initial=dic)
model
参考:https://www.cnblogs.com/wupeiqi/articles/6216618.html
自定义认证:
参考:https://docs.djangoproject.com/zh-hans/2.1/topics/auth/customizing/
# 以下为自定义认证使用 class UserProfileManager(BaseUserManager): def create_user(self, email, name, password=None): """ Creates and saves a User with the given email, date of birth and password. """ if not email: raise ValueError('Users must have an email address') # 相当于创建一条用户记录 user = self.model( email=self.normalize_email(email), name=name, ) # 设置密码 user.set_password(password) # 先存放到一个临时地方,再放到数据库 user.save(using=self._db) return user def create_superuser(self, email,name, password): """ Creates and saves a superuser with the given email, date of birth and password. """ user = self.create_user( email, password=password, name=name, ) user.is_admin = True user.save(using=self._db) return user class UserProfile(AbstractBaseUser): email = models.EmailField( verbose_name='email address', max_length=255, unique=True, ) name = models.CharField(max_length=64) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) bind_hosts = models.ManyToManyField('BindHost',blank=True) host_groups = models.ManyToManyField('HostGroup',blank=True) is_staff = models.BooleanField( ('staff status'), default=False, help_text=('Designate 帮助信息') ) objects = UserProfileManager() # 用户名字段是上面的email字段 USERNAME_FIELD = 'email' # 必要的字段 # REQUIRED_FIELDS = ['date_of_birth'] REQUIRED_FIELDS = ['name'] def __str__(self): return self.email def has_perm(self, perm, obj=None): "Does the user have a specific permission?" # Simplest possible answer: Yes, always return True def has_module_perms(self, app_label): "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always return True # @property # def is_staff(self): # "Is the user a member of staff?" # # Simplest possible answer: All admins are staff # return self.is_admin
settings文件需要配置:
# 使用自定义认证的时候需要设定这个,app.class AUTH_USER_MODEL = 'web.UserProfile'
用户认证
# 登陆验证导入 from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate,login,logout
def acc_login(request): '''登陆''' if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username,password=password) if user: login(request,user) return redirect('/') else: error_msg = '错误的账号密码!' return render(request,'login.html',{'error_msg':error_msg}) return render(request,'login.html')
def acc_logout(request): '''注销''' logout(request) return redirect('/login/')
需要认证后才能访问的页面
# 使用@login_required后,settings文件需要修改配置LOGIN_URL = '/login/' @login_required def dashboard(request): return render(request,'index.html')
ModelForm
参考:https://www.cnblogs.com/wupeiqi/articles/6229414.html
form可以提供验证、错误信息、正确信息
is_valid-->每一个字段进行正则(字段内置正则)+clean_字段 -->clean(__all__) -->-post_clean
Form验证:
UserInfoForm --> Form -> BaseForm(is_va...)
ModelForm验证
UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm
ModelForm a. class Meta: model, # 对应Model的 fields=None, # 字段 exclude=None, # 排除字段 labels=None, # 提示信息 help_texts=None, # 帮助提示信息 widgets=None, # 自定义插件 error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) field_classes=None # 自定义字段类 (也可以自定义字段) localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 如: 数据库中 2016-12-27 04:10:57 setting中的配置 TIME_ZONE = 'Asia/Shanghai' USE_TZ = True 则显示: 2016-12-27 12:10:57 b. 验证执行过程 is_valid -> full_clean -> 钩子 -> 整体错误 c. 字典字段验证 def clean_字段名(self): # 可以抛出异常 # from django.core.exceptions import ValidationError return "新值" d. 用于验证 model_form_obj = XXOOModelForm() model_form_obj.is_valid() model_form_obj.errors.as_json() model_form_obj.clean() model_form_obj.cleaned_data e. 用于创建 model_form_obj = XXOOModelForm(request.POST) #### 页面显示,并提交 ##### # 默认保存多对多 obj = form.save(commit=True) # 不做任何操作,内部定义 save_m2m(用于保存多对多) obj = form.save(commit=False) obj.save() # 保存单表信息 obj.save_m2m() # 保存关联多对多信息 f. 用于更新和初始化 obj = model.tb.objects.get(id=1) model_form_obj = XXOOModelForm(request.POST,instance=obj) ... PS: 单纯初始化 model_form_obj = XXOOModelForm(initial={...})
例子:(day24)
from django.db import models # Create your models here. class UserType(models.Model): caption = models.CharField(max_length=32) class UserGroup(models.Model): name = models.CharField(max_length=32) class UserInfo(models.Model): username = models.CharField(max_length=32,verbose_name='用户') email = models.EmailField(verbose_name='邮箱') user_type = models.ForeignKey(to='UserType',to_field='id',on_delete=models.CASCADE,verbose_name='用户类型') u2g = models.ManyToManyField(UserGroup)
from django.shortcuts import render from app01 import models from django import forms from django.forms import fields as Ffields from django.forms import widgets as Fwidgets class UserInfoModelForm(forms.ModelForm): is_rmb = Ffields.CharField( #建立额外不保存数据库的字段 widget=Fwidgets.CheckboxInput(), label='是否保存用户信息' ) class Meta: model = models.UserInfo fields = '__all__' #显示所有 # fields = ['username','email'] #显示部分 # exclude = ['username'] #排除这个外的显示 labels = { # 显示名称,比models里面的优先级高 'username':'用户', } help_texts = { 'username':'后面显示的帮助信息' } widgets = { # 自定义插件,需要使用form里面的widgets 'username':Fwidgets.Textarea(attrs={'class':'c1'}) } error_messages ={ 'email':{ 'required':'邮箱不能为空', 'invalid':'邮箱格式不正确', } } # field_classes = { # 将验证方式从email改为URL # 'email':Ffields.URLField # } # Create your views here. def index(request): if request.method == 'GET': obj = UserInfoModelForm() return render(request, 'index.html', {'obj': obj}) elif request.method == 'POST': obj = UserInfoModelForm(request.POST) print(obj.is_valid()) print(obj.cleaned_data) print(obj.errors) if obj.is_valid(): # 保存到数据库,这里一句save相当于下面三句 # obj.save() instance = obj.sava(False) instance.save() obj.save_m2m() return render(request, 'index.html', {'obj': obj}) # render(request,'index.html') def user_list(request): # 跨表操作不能操邹ManyToMany类型,只能是ForeignerKey li = models.UserInfo.objects.all().select_related('user_type') return render(request,'user_list.html',{'li':li}) def user_edit(request,nid): if request.method == 'GET': obj = models.UserInfo.objects.filter(id=nid).first() # instance=obj将初始值传入 mf = UserInfoModelForm(instance=obj) return render(request,'user_edit.html',{'mf':mf}) elif request.method == 'POST': obj = models.UserInfo.objects.filter(id=nid).first() # 传入instance=obj,才能确定是修改这个数据,而不是增加 mf = UserInfoModelForm(request.POST,instance=obj) if mf.is_valid(): mf.save() else: print(mf.errors) return render(request, 'user_edit.html', {'mf': mf})
编辑插件:CKEditor,UEEditor,TinyEditor,KindEditor
集合文件上传、多文件上传、文件空间管理
XSS攻击(过滤的函数或类)
logging:
参考:https://www.cnblogs.com/wupeiqi/articles/5501365.html
单文件日志:
import logging logging.basicConfig(filename='log.log', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', level=10) logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical') logging.log(10,'log')
日志等级
CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0
注:只有【当前写等级】大于【日志等级】时,日志文件才被记录。
多文件日志:
# 定义文件 file_1_1 = logging.FileHandler('l1_1.log', 'a', encoding='utf-8') fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s") file_1_1.setFormatter(fmt) file_1_2 = logging.FileHandler('l1_2.log', 'a', encoding='utf-8') fmt = logging.Formatter() file_1_2.setFormatter(fmt) # 定义日志 logger1 = logging.Logger('s1', level=logging.ERROR) logger1.addHandler(file_1_1) logger1.addHandler(file_1_2) # 写日志 logger1.critical('1111') 日志一
# 定义文件 file_2_1 = logging.FileHandler('l2_1.log', 'a') fmt = logging.Formatter() file_2_1.setFormatter(fmt) # 定义日志 logger2 = logging.Logger('s2', level=logging.INFO) logger2.addHandler(file_2_1)
注意:
-
url上面,写的访问路径没有/ 则form表单上的action写的url也不需要有/;
如果有/,则action也需要有/
url(r'^login/',views.login, <form action='/login/'>
-
前端上传文件的时候,需要再form表单里加上 enctype="multipart/form-data
<form method="post" action="/index/" enctype="multipart/form-data"> </form>
-
修改models.py里面的数据库表结构之后(如果需要使用session,也要执行这两个命令),需要执行命令python manage.py makemigrations;python manage.py migrate
-
django不会自动创建数据库,所以需要先建立数据库
-
django使用mysql,必须要在project同名目录下的init.py文件加上pymysql
import pymysql pymysql.install_as_MySQLdb()
-
更新时候能触发auto_now = True更新时间的操作
更新时候能触发auto_now = True更新时间的操作 obj = UserGroup.objects.filter(id=1).first() obj.caption = 'CEO' obj.save()
-
避免XSSl攻击,后端生成的HTML代码,需要加上
# 若想要将对应代码变成想要的效果,可以在HTML页面输出的后端信息,加上|safe
{{page_str|safe}}
# 后端的话,需要导入 from django.utils.safestring import mark_safe page_str = mark_safe(page_str)