简单入门和梳理
1、WEB框架
MVC
Model View Controller 数据库 模板文件 业务处理 MTV Model Template View 数据库 模板文件 业务处理 ############## WEB:MVC、MTV2、Django
#安装
pip3 install django # 创建Django工程 django-admin startproject 【工程名称】 mysite - mysite # 对整个程序进行配置 - init - settings # 配置文件 - url # URL对应关系 - wsgi # 遵循WSIG规范,uwsgi + nginx - manage.py # 管理Django程序: - python manage.py - python manage.py startapp xx - python manage.py makemigrations - python manage.py migrate # 运行Django功能 python manage.py runserver 127.0.0.1:8001 # 创建app python manage.py startapp cmdb python manage.py startapp openstack python manage.py startapp xxoo....# app目录结构
app: migrations 数据修改表结构 admin Django为我们提供的后台管理 apps 配置当前app models ORM,写指定的类 通过命令可以创建数据库结构 tests 单元测试 views 业务代码1、配置模板的路径
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, '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', ], }, },]2、配置静态目录static( 最后一行添加)
STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'),)
3、app下views.py
def func(request): # request.method GET / POST # http://127.0.0.1:8009/home?nid=123&name=alex # request.GET.get('',None) # 获取请求发来的而数据 # request.POST.get('',None) # return HttpResponse("字符串") # return render(request, "HTML模板的路径") # return redirect('/只能填URL')
4、模板渲染
-- { { 变量名 }}def func(request): return render(request, "index.html", {'current_user': "wangxihao"})
..{ {current_user}}
-- For循环
def func(request): return render(request, "index.html", {'current_user': "w1", 'user_list': ['w1','w2']})
..{ {current_user}}
- {% for row in user_list %} {% if row == "w1" %}
- { { row }} {% endif %} {% endfor %}
#####索引#####
# viewsdef func(request): return render(request, "index.html", {'current_user': "alex", 'user_list': ['alex','eric'], 'user_dict': {'k1': 'v1', 'k2': 'v2'}})
..{ {current_user}}{ { user_list.1 }} { { user_dict.k1 }} { { user_dict.k2 }}
第二课
1、Django请求生命周期
-> URL对应关系(匹配) -> 视图函数 -> 返回用户字符串 -> URL对应关系(匹配) -> 视图函数 -> 打开一个HTML文件,读取内容2、路由系统
- url(r'^index/', views.index), url(r'^home/', views.Home.as_view()),
- url(r'^detail-(\d+).html', views.detail),
- url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)
实战1:
url(r'^detail-(\d+)-(\d+).html', views.detail),def func(request, nid, uid): passdef func(request, *args): args = (2,9)def func(request, *args, **kwargs): args = (2,9)
实战2:
url(r'^detail-(?P\d+)-(?P \d+).html', views.detail)def func(request, nid, uid): pass def funct(request, **kwargs): kwargs = {'nid': 1, 'uid': 3} def func(request, *args, **kwargs): args = (2,9)
- name 对URL路由关系进行命名, ***** 以后可以根据此名称生成自己想要的URL *****
url(r'^asdfasdfasdf/', views.index, name='i1'),url(r'^yug/(\d+)/(\d+)/', views.index, name='i2'),url(r'^buy/(?P
\d+)/(?P \d+)/', views.index, name='i3'), def func(request, *args, **kwargs): from django.urls import reverse url1 = reverse('i1') # asdfasdfasdf/ url2 = reverse('i2', args=(1,2,)) # yug/1/2/ url3 = reverse('i3', kwargs={'pid': 1, "nid": 9}) # buy/1/9/
{% url "i1" %} # asdfasdfasdf/{% url "i2" 1 2 %} # yug/1/2/{% url "i3" pid=1 nid=9 %} # buy/1/9/
PS:当前的URL----request.path_info
-
默认值
# urlsurl(r'^index/', views.index, {'name': 'root'}), # viewsdef index(request,name): print(name) return HttpResponse('OK')
**
一种传递参数的方式。呵呵这里没有完全弄明白~ -
命名空间
/admin/ include('app01.urls',namespace='m1')/crm/ include('app01.urls',namespace='m2') app01.urls/index/(?P
\d+) name = 'n1' # 通过namespace:name,反向生成URLreverser('m1:n1' kwargs={'arg1':11}) # /admin/index/11reverser('m2:n1') # /crm/index# 模板语言下的reverser{% url 'm1:n1' arg1=11 %} # /admin/index/11
使用reverser的方法,解决调用同一views函数时,路径的区分问题。基本用不到。呵呵**这里没有完全弄明白~
3、视图
1、获取用户请求数据
request.body # 所有内容的原始值 request.GET request.POST request.FILES request.xxxx.getlist request.meta # 请求头相关的内容 request.COOKIES request.path_info request.method
PS:
GET:获取数据 POST:提交数据 2、checkbox等多选的内容 request.POST.getlist() 3、上传文件# 上传文件,form标签做特殊设置obj = request.FILES.get('fafafa')obj.name //取文件名obj.size //取文件大小//obj.chunks() //取文件f = open(obj.name, mode='wb')for item in obj.chunks(): f.write(item)f.close()
HTML中表单的特殊设置
4、FBV & CBV function base view
def index(request,*args,**kwargs): if request.method == "POST": pass else request.method == "GET": pass
class base view
from django.view import Viewclass Home(View): def get(self,reqeust,*args,**kwargs): pass
建议:两者都用
订制CBVfrom django.view import ViewClass Foo(View): # 每次在执行Foo 方法时,可以自定义,之前和之后的操作 def dispatch(self,request): print("before") result = super(Foo,self).dispatch(request,*args,**kwargs) print("after") return result def get(self,request): print("get get get") return Httprespone("get OK") def post(self,request): print("post post post") return Httprespone("post OK")5、装饰器
FBV的装饰器
def auth(func): def inner(reqeust,*args,**kwargs): v = reqeust.COOKIES.get('username111') if not v: return redirect('/login/') return func(reqeust, *args,**kwargs) return inner@authdef home(request): u = request.COOKIES.get("username") psw = request.COOKIES.get("psw") return render(reqeust,'index.html',{'current_user': v})
CBV的装饰器
from django import viewsfrom django.utils.decorators import method_decorator@method_decorator(auth,name='dispatch') # 同2、给这个class的所有的方法增加装饰器的另一种方法class Order(views.View):# @method_decorator(auth) # 2、给这个class中所有的方法增加装饰器# def dispatch(self, request, *args, **kwargs):# return super(Order,self).dispatch(request, *args, **kwargs)# @method_decorator(auth) # 1、单独给一个方法提供装饰器def get(self,reqeust): v = reqeust.COOKIES.get('username111') return render(reqeust,'index.html',{'current_user': v})def post(self,reqeust): v = reqeust.COOKIES.get('username111') return render(reqeust,'index.html',{'current_user': v})# 回顾一下,CBV的书写方式。# urls.py# url('/order/',views.order.as_views)
6、请求的其他信息
# viewsdef index(request): print(request.environ) # environ 存放所有的请求信息 # 除了request.environ以外所有的子方法,都是django封装的。 print(request.POST) print(request.GET) print(request.METHOD) print(request.FILE) for v,k in request.environ.items(): print(v,k) # 查看environ所有的信息 # 查看访问的客户端信息 print(request.environ['HTTP_USER_AGENT'])
3、模板
3.1、模板的继承(母版block)Document {% block pir_css %}{% endblock %} {% block someCode %}{% endblock %} {% block pir_js %}{% endblock %}
{% extends '母版.html' %} {% block pir_css %} {% endblock %}{% block someCode %}block后面的 someCode 要对应母版的名字。
{ { someCode }} {% endblock %}{% block pir_js %}{% endblock %}
# urls.pyfrom django.conf.urls import url,from cmdb import viewsurlpatterns = [ url(r'^cmdb/$', views.index1), url(r'^cmdb/$', views.index2),]# viewsdef index1(request): return render(request,"子版1.html",{"someCode":"HELLO WORLD!!","OTHER":['other1','other2','other3']})def index2(request): return render(request,"子版2.html",{"someCode":"HELLO PYTHON!!","OTHER":[111,222,333]})
3.2、模板的导入
{% extends '母版.html' %} {% block pir_css %} {% endblock %}{% block someCode %}block后面的 someCode 要对应母版的名字。
{ { someCode }} {% include 'tag.html '%}{% endblock %}{% block pir_js %}{% endblock %}
{ { title }}
- {% for x in other %}
- { {x}} {% endfor %}
3.3、自定义模板
3.3.1、simple_tag
a. app下创建templatetags目录b. 任意xxoo.py文件c. 创建template对象 registerd. @register.simple_tag def func(a1,a2,a3....) return "asdfasd"e. settings中注册APPf. 顶部 {% load xxoo %}g. {% 函数名 arg1 arg2 %}缺点: 不能作为if条件优点: 参数任意
3.3.2、filter
a. app下创建templatetags目录b. 任意xxoo.py文件c. 创建template对象 registerd. @register.filter def func(a1,a2) return "asdfasd"e. settings中注册APPf. 顶部 {% load xxoo %}g. { { 参数1|函数名:"参数二,参数三" }} { { 参数1|函数名:数字 }}缺点: 最多两个参数,不能加空格优点: 能作为if条件
3.3.3、分页
1、XSS攻击,默认DJango后台传递不安全。解决办法: 1.1,前端模板使用{ {str | safe}}
1.2、后端使用
from django.utils.safestring import make_safedef index(request): str = "haha
" str = make_safe(str) return render(request,"index.html",{"str1":str1})
2、具体rerr代码
from django.utils.safestring import mark_safeclass Page: def __init__(self, current_page, data_count, per_page_count=10, pager_num=7): self.current_page = current_page self.data_count = data_count self.per_page_count = per_page_count self.pager_num = pager_num @property def start(self): return (self.current_page - 1) * self.per_page_count @property def end(self): return self.current_page * self.per_page_count @property def total_count(self): v, y = divmod(self.data_count, self.per_page_count) if y: v += 1 return v def page_str(self, base_url): page_list = [] if self.total_count < self.pager_num: start_index = 1 end_index = self.total_count + 1 else: if self.current_page <= (self.pager_num + 1) / 2: start_index = 1 end_index = self.pager_num + 1 else: start_index = self.current_page - (self.pager_num - 1) / 2 end_index = self.current_page + (self.pager_num + 1) / 2 if (self.current_page + (self.pager_num - 1) / 2) > self.total_count: end_index = self.total_count + 1 start_index = self.total_count - self.pager_num + 1 if self.current_page == 1: prev = '上一页' else: prev = '上一页' % (base_url, self.current_page - 1,) page_list.append(prev) for i in range(int(start_index), int(end_index)): if i == self.current_page: temp = '%s' % (base_url, i, i) else: temp = '%s' % (base_url, i, i) page_list.append(temp) if self.current_page == self.total_count: nex = '下一页' else: nex = '下一页' % (base_url, self.current_page + 1,) page_list.append(nex) jump = """ GO """ % (base_url,) page_list.append(jump) page_str = mark_safe("".join(page_list)) return page_str
3、具体分页调用方法
from utils import pagination # 导入上面的代码LIST = [] # 模拟数据for i in range(500): # 模拟数据 LIST.append(i) # 模拟数据def user_list(request): current_page = request.GET.get('p', 1) current_page = int(current_page) page_obj = pagination.Page(current_page,len(LIST),val) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str("/user_list/") return render(request, 'user_list.html', {'li': data,'page_str': page_str})
模板语言的取值:
render( request, "xxx.html", { 'obj':1234, 'k1':[1,2,3,4], 'k2':{'name':'www','age':77} })
HTML模板的取值:
{obj}
{k1.3}列表
{k2.name}字典
{% for i in k1 %}{
{i}}{% endfor %}{% for row in k2,keys %}{
{row}}{% endfor %}{% for row in k2.value %}{
{row}}{% endfor %}{% for k,v in k2.items %} {% forloop.counter # 正序循环次数的计数器从1开始 %} {% forloop.counter0 # 正序循环次数的计数器从0开始 %} {% forloop.revcounter # 倒叙循环次数的计数器从开始 %} {% forloop.revcounter0 # 倒叙循环次数的计数器从0开始 %} {% forloop.frist # 判断是否是第一次循环 %} {% forloop.last # 判断是否是最后一次循环 %} {% forloop.parentloop# 显示父循环的次数,需要有父层循环 %}{
{k}}---{ {v}}{% endfor %}
4、ORM操作
select * from tb where id > 1# 对应关系models.tb.objects.filter(id__gt=1)models.tb.objects.filter(id=1)models.tb.objects.filter(id__lt=1)
4.1、创建类
----先写类:from django.db import models# app01_userinfoclass UserInfo(models.Model): # id列,自增,主键 # 用户名列,字符串类型,指定长度 username = models.CharField(max_length=32) password = models.CharField(max_length=64)
----注册APP(settings.py)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01',]
----执行命令
python manage.py makemigrationspython manage.py migrate
********** 注意 ***********
Django默认使用MySQLdb模块链接MySQL 主动修改为pymysql,在project同名文件夹下的__init__文件中添加如下代码即可:pip install pymysql
import pymysqlpymysql.install_as_MySQLdb()
4.2、字段
AutoField(Field) - int自增列,必须填入参数 primary_key=TrueBigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32)SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807BooleanField(Field) - 布尔值类型NullBooleanField(Field): - 可以为空的布尔值CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度TextField(Field) - 文本类型EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URLSlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorageImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串)DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DDTimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]]DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型FloatField(Field) - 浮点型DecimalField(Field) - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度BinaryField(Field) - 二进制类型
自定义无符号整数字段 class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED' PS: 返回值为字段在数据库中的属性,Django字段默认的值为: 'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', 'UUIDField': 'char(32)',
# 注意事项 1.触发Model中的验证和错误提示有两种方式: a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息 b. 使用ModelForm c. 调用Model对象的 clean_fields 方法,如: # models.py class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py def index(request): obj = models.UserInfo(username='11234', email='uu') try: print(obj.clean_fields()) except Exception as e: print(e) return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 2.Admin中修改错误提示 # admin.py from django.contrib import admin from model_club import models from django import forms class UserInfoForm(forms.ModelForm): age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta: model = models.UserInfo # fields = ('username',) fields = "__all__" exclude = ['title'] labels = { 'name':'Writer', } help_texts = {'name':'some useful help text.',} error_messages={ 'name':{'max_length':"this writer name is too long"} } widgets={'name':Textarea(attrs={'cols':80,'rows':20})} class UserInfoAdmin(admin.ModelAdmin): form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin)
4.3、字段的参数
null 数据库中字段是否可以为空 db_column 数据库中字段的列名 default 数据库中字段的默认值 primary_key 数据库中字段是否为主键 db_index 数据库中字段是否可以建立索引 unique 数据库中字段是否可以建立唯一索引 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称 blank Admin中是否允许用户输入为空 editable Admin中是否可以编辑 help_text Admin中该字段的提示信息 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] )
元信息
class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "table_name" # 联合索引 # 遵循最左前缀模式 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引(固定值都搜索时) unique_together = (("driver", "restaurant"),) # admin中显示的表名称+s verbose_name # verbose_name 不加5 verbose_name_plural
4.4、操作ORM
# 增models.User.objects.create(name='qianxiaohu',age=18)dic = {'name': 'xx', 'age': 19}models.User.objects.create(**dic)obj = models.User(name='qianxiaohu',age=18)obj.save()
# 删models.User.objects.filter(id=1).delete()
# 改models.User.objects.filter(id__gt=1).update(name='xxx',age=84)dic = {'name': 'xx', 'age': 19}models.User.objects.filter(id__gt=1).update(**dic)obj = models.Tb1.objects.get(id=1)obj.c1 = '111'obj.save()
# 查models.User.objects.filter(id=1,name='root')models.User.objects.filter(id__gt=1,name='root')models.User.objects.filter(id__lt=1)models.User.objects.filter(id__gte=1)models.User.objects.filter(id__lte=1)models.User.objects.filter(id=1,name='root')dic = {'name': 'xx', 'age__gt': 19}models.User.objects.filter(**dic)v1 = models.Business.objects.all() # QuerySet ,内部元素都是对象v2 = models.Business.objects.all().values('id','caption') # QuerySet ,内部元素都是字典v3 = models.Business.objects.all().values_list('id','caption') # QuerySet ,内部元素都是元组models.Business.objects.get(id=1) # 获取到的一个对象,如果不存在就报错models.Business.objects.filter(id=1).first() # 获得对象或者None,不存在也不会报错
# 获取个数models.Tb1.objects.filter(name='seven').count()# 大于,小于models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值# inmodels.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in# isnullEntry.objects.filter(pub_date__isnull=True)# contains --类似sql的like操作models.Tb1.objects.filter(name__contains="ven")models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感models.Tb1.objects.exclude(name__icontains="ven")# rangemodels.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and# 其他类似startswith,istartswith, endswith, iendswith,# order bymodels.Tb1.objects.filter(name='seven').order_by('id') # ascmodels.Tb1.objects.filter(name='seven').order_by('-id') # desc# group byfrom django.db.models import Count, Min, Max, Summodels.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"# limit 、offsetmodels.Tb1.objects.all()[10:20]# regex正则匹配,iregex 不区分大小写Entry.objects.get(title__regex=r'^(An?|The) +')Entry.objects.get(title__iregex=r'^(an?|the) +')# dateEntry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))# yearEntry.objects.filter(pub_date__year=2005)Entry.objects.filter(pub_date__year__gte=2005)# monthEntry.objects.filter(pub_date__month=12)Entry.objects.filter(pub_date__month__gte=6)# dayEntry.objects.filter(pub_date__day=3)Entry.objects.filter(pub_date__day__gte=3)# week_dayEntry.objects.filter(pub_date__week_day=2)Entry.objects.filter(pub_date__week_day__gte=2)# hourEvent.objects.filter(timestamp__hour=23)Event.objects.filter(time__hour=5)Event.objects.filter(timestamp__hour__gte=12)# minuteEvent.objects.filter(timestamp__minute=29)Event.objects.filter(time__minute=46)Event.objects.filter(timestamp__minute__gte=29)# secondEvent.objects.filter(timestamp__second=31)Event.objects.filter(time__second=2)Event.objects.filter(timestamp__second__gte=31)
# 高级操作# extraextra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])# Ffrom django.db.models import Fmodels.Tb1.objects.update(num=F('num')+1)# Q# 方式一:Q(nid__gt=10)Q(nid=8) | Q(nid__gt=10)Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')# 方式二:con = Q()q1 = Q()q1.connector = 'OR'q1.children.append(('id', 1))q1.children.append(('id', 10))q1.children.append(('id', 9))q2 = Q()q2.connector = 'OR'q2.children.append(('c1', 1))q2.children.append(('c1', 10))q2.children.append(('c1', 9))con.add(q1, 'AND')con.add(q2, 'AND')models.Tb1.objects.filter(con)# 执行原生SQLfrom django.db import connection, connectionscursor = connection.cursor() # cursor = connections['default'].cursor()cursor.execute("""SELECT * from auth_user where id = %s""", [1])row = cursor.fetchone()
################################################################### PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET ###################################################################def all(self) # 获取所有的数据对象def filter(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Qdef exclude(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Qdef select_related(self, *fields) 性能相关:表之间进行join连表操作,一次性获取关联的数据。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外键字段') model.tb.objects.all().select_related('外键字段__外键字段')def prefetch_related(self, *lookups) 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 # 获取所有用户表 # 获取用户类型表where id in (用户表中的查到的所有用户ID) models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=CharField(), )) ) students = Student.objects.all().annotate(num_excused_absences=models.Sum( models.Case( models.When(absence__type='Excused', then=1), default=0, output_field=models.IntegerField() )))def annotate(self, *args, **kwargs) # 用于实现聚合group by查询 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1def distinct(self, *field_names) # 用于distinct去重 models.UserInfo.objects.values('nid').distinct() # select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重def order_by(self, *field_names) # 用于排序 models.UserInfo.objects.all().order_by('-id','age')def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) def reverse(self): # 倒序 models.UserInfo.objects.all().order_by('-nid').reverse() # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 def defer(self, *fields): models.UserInfo.objects.defer('username','id') 或 models.UserInfo.objects.filter(...).defer('username','id') #映射中排除某列数据 def only(self, *fields): #仅取某个表中的数据 models.UserInfo.objects.only('username','id') 或 models.UserInfo.objects.filter(...).only('username','id') def using(self, alias): 指定使用的数据库,参数为别名(setting中的设置)################################################### PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS ###################################################def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名 models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库 models.UserInfo.objects.raw('select * from userinfo', using="default") ################### 原生SQL ################### from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() # fetchall()/fetchmany(..)def values(self, *fields): # 获取每行数据为字典格式def values_list(self, *fields, **kwargs): # 获取每行数据为元祖def dates(self, field_name, kind, order='ASC'): # 根据时间进行某一部分进行去重查找并截取指定内容 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) # order只能是:"ASC" "DESC" # 并获取转换后的时间 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC')def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 # kind只能是 "year", "month", "day", "hour", "minute", "second" # order只能是:"ASC" "DESC" # tzinfo时区对象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """def none(self): # 空QuerySet对象##################################### METHODS THAT DO DATABASE QUERIES #####################################def aggregate(self, *args, **kwargs): # 聚合函数,获取字典类型聚合结果 from django.db.models import Count, Avg, Max, Min, Sum result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4}def count(self): # 获取个数def get(self, *args, **kwargs): # 获取单个对象def create(self, **kwargs): # 创建对象def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的个数 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10)def get_or_create(self, defaults=None, **kwargs): # 如果存在,则获取,否则,创建 # defaults 指定创建时,其他字段的值 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})def update_or_create(self, defaults=None, **kwargs): # 如果存在,则更新,否则,创建 # defaults 指定创建时或更新时的其他字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})def first(self): # 获取第一个def last(self): # 获取最后一个def in_bulk(self, id_list=None): # 根据主键ID进行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list)def delete(self): # 删除def update(self, **kwargs): # 更新def exists(self): # 是否有结果
数据库操作--QuerySet中方法:
- QuerySet中的方法: - 返回QuerySet类型(select_related,prefetch_related) select_related # 一次完成参数内的跨表查询,如果需要跨其他表,则再发起一次请求。 users = models.User.objects.all().select_related('ut') for row in users: print(row.user,row.pwd,row.ut_id) print(row.ut.name) print(row.tu.name) # 再发起一次SQL请求 prefetch_related users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu') # select * from users where id > 30 # 获取上一步骤中所有的ut_id=[1,2] # select * from user_type where id in [1,2] # select * from user_type where id in [1,2] for row in users: print(row.user,row.pwd,row.ut_id) print(row.ut.name)
多表关系以及参数
ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 - models.CASCADE,删除关联数据,与之关联也删除 - models.DO_NOTHING,删除关联数据,引发错误IntegrityError - models.PROTECT,删除关联数据,引发错误ProtectedError - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) - models.SET,删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id" on_delete=models.SET(func),) related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') db_constraint=True # 是否在数据库中创建外键约束 parent_link=False # 在Admin中是否显示关联数据OneToOneField(ForeignKey) to, # 要进行关联的表名 to_field=None # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 ###### 对于一对一 ###### # 1. 一对一其实就是 一对多 + 唯一索引 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 # 如下会在A表中额外增加一个c_ptr_id列且唯一: class C(models.Model): nid = models.AutoField(primary_key=True) part = models.CharField(max_length=12) class A(C): id = models.AutoField(primary_key=True) code = models.CharField(max_length=1)ManyToManyField(RelatedField) to, # 要进行关联的表名 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 # 做如下操作时,不同的symmetrical会有不同的可选字段 models.BB.objects.filter(...) # 可选字段有:code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True) # 可选字段有: bb, code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定义第三张表时,使用字段用于指定关系表 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64) db_constraint=True, # 是否在数据库中创建外键约束 db_table=None, # 默认创建第三张表时,数据库中表的名称
# 外键:class UserType(models.Model): caption = models.CharField(max_length=32)# id caption# 1,普通用户# 2,VIP用户# 3, 游客class User(models.Model): age = models.IntergerFiled() name = models.CharField(max_length=10)#字符长度 # user_type_id = models.IntergerFiled() # 约束, user_type = models.ForeignKey("UserType",to_field='id') # 约束,# name age user_type_id# 张扬 18 3# 张A扬 18 2# 张B扬 18 2#外键:v = models.User.objects.filter(nid__gt=0)v[0].user_type.caption ----> 通过.进行跨表
4.5、AJAX
$.ajax({ url: '/host', type: "POST", data: {'k1': 123,'k2': "root"}, //data:{'k1':123,'list':[1,2,3,4]}, //traditional:true, //这个属性表示,可以传递列表 //processData:false, //JQ上传文件时,设置不做特殊处理。 //contentType:false, //JQ上传文件时,设置不做特殊处理。 //后台取值的时候使用 request.POST.getlist("list") //格式,["1","2","3","4"] success: function(data){ // data是服务器端返回的字符串 var obj = JSON.parse(data); }})
建议:永远让服务器端返回一个字典
# views return HttpResponse(json.dumps(字典))
4.6、一对多 and 正向操作 and 反向操作
def func(): # User类中,on_delete=models.SET(func)的回调函数 return 5class UserType(models.Model): name = models.CharField(max_length=32) class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) .... ForiegnKey(to="UserType",to_field='id',on_delete=models.SET(func))# delete from user where id=1# delete from UserType where id=1 # 报错,因为User表中有数据正在使用UserType,所以会报错。# UserType.objects.filter(id=1).delete() # 在新版django中,不再报错,直接删除,并连带User表中的关联数据一起删除 # 参考model创建表时的,on_delete方法进行设置。# 正向 ---从User表中操作UserType# v = User.objects.all()# for item in v:# item.user# item.pwd# item.ut.name# User.objects.all().values('user','ut__name')# 反向 ---从UserType表中操作User# v = UserType.objects.all()# for item in v:# item.name# item.id# item.user_set.all() # 如果在foreignKey(related_name=b),则 item.b.all() # 如果在foreignKey(related-query_name=a),则 item.a_set.all() # models.UserType.objects.all().values('name','user__pwd')# 如果在foreignKey(related_name=b)# models.UserType.objects.all().values('b')# 如果在foreignKey(related-query_name=a)# models.UserType.objects.all().values('name','a__pwd')
4.7、创建多对多
方式一:自定义关系表
class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32,db_index=True) ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='id')# 10class Application(models.Model): name = models.CharField(max_length=32)# 2class HostToApp(models.Model): hobj = models.ForeignKey(to='Host',to_field='nid') aobj = models.ForeignKey(to='Application',to_field='id')# HostToApp.objects.create(hobj_id=1,aobj_id=2)
方式二:自动创建关系表
class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32,db_index=True) ip = models.GenericIPAddressField(protocol="ipv4",db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='id')# 10class Application(models.Model): name = models.CharField(max_length=32) r = models.ManyToManyField("Host") #无法直接对第三张表进行操作obj = Application.objects.get(id=1)obj.name# 第三张表操作obj.r.add(1)obj.r.add(2)obj.r.add(2,3,4)obj.r.add(*[1,2,3,4])obj.r.remove(1)obj.r.remove(2,4)obj.r.remove(*[1,2,3])obj.r.clear()obj.r.set([3,5,7])# 所有相关的主机对象“列表” QuerySetobj.r.all()# 筛选,筛选出主机名为c1的主机obj.r.filter(hostname="c1")
4.8、数据验证(偏弱)
# 这句话执行了,email就保存了1234156,尽管不合规。models.tb.objects.create(user=name,email="1234156")# 这句话执行了,email就保存了1234156,尽管不合规。obj = models.tb(user=name,email="1234156")obj.save()# 这里就出现了验证功能。obj = models.tb(user=name,email="1234156")obj.full_clean() # 执行这句的时候,验证正则规则。如果不合规,就报错了。错误类型是ValidationErrorobj.save()# ValidationError 引申一下。# 这里有一个钩子,虽然不知道有什么用。# xx.full_clean()中有一个方法叫做self.clean(),里面是代码是pass。而这个clean就是所谓的钩子。# 在执行正则验证之后,都会再执行一次clean方法。尽管现在是空的,但我们可以做些东西。#models.pyclass UserInfo(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def clean(self): # 这里有进行了一次数据库操作,当然可以自定义其他操作。 c = UserInfo.objects.filter(name=self.name).count() if c: from django.core.exceptions import ValidationError rasie ValidationError(message="用户名已存在",code="exist") # 只能是ValidationError错误哦# 尽管验证功能,还是让验证模块去做最好了。所以只是知道一下就好了。
5、cookie
5.1、cookie 是什么?
cookie,是客户端上的浏览器储存一个文件。字典格式的。浏览器访问时会将本地的cookie发送的服务器。 接收方式是request.COOKIESrequest.COOKIES.get("username")request.COOKIES["username"]# 加密传输cookierequest.get_signed_cookie("username", "kangbazi",salt="基于salt加密")
设置cookie的方式
rep = HttpResponse(...) 或 rep = render(request, ...)rep['name'] = "WXH" # 给响应头中添加数据(ResponseHeader)rep.set_cookie(key,value,...)rep.set_signed_cookie(key,value,salt='加密盐',...) 参数: key, 键 value='', 值 max_age=None, 超时时间 -- 参数分钟秒,倒计时 expires=None, 超时时间(datetime格式) path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问 domain=None, Cookie生效的域名 secure=False, https传输 httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
JS也可以操作cookie
6、session
6.1、session的原理
session是保存的在服务器端的键值对 session是基于cookie的6.2、Session配置(缺少cache)
# settings.py# 配置文件中设置默认操作(通用配置):SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)ESSION_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,默认修改之后才保存(默认)
6.3、session的5种类型。
6.3.1、数据库(默认)
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失效策略。
6.3.2、缓存
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,默认修改之后才保存 b. 使用 同上
6.3.3、文件
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,默认修改之后才保存 b. 使用 同上
6.3.4、缓存+数据库
数据库用于做持久化,缓存用于提高效率 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 b. 使用 同上
6.3.5、加密cookie
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 b. 使用 同上
6.4、 Session用户验证
def login(func): def wrap(request, *args, **kwargs): # 如果未登陆,跳转到指定页面 if request.path == '/test/': return redirect('http://www.baidu.com') return func(request, *args, **kwargs) return wrap
7、CSRF-跨站请求伪造
7.1、CSRF原理
7.1.0、盗取Cookie,伪装登录,为所欲为。 7.1.1、form添加 {% csrf_token %} 7.1.2、django会在加载HTML时生成一个随机字符串,保存到cookie中。 7.1.3、在页面POST提交时,同时会把这个随机字符串提交到服务器。或者在cookie中让django获取。 7.1.4、获取的随机字符串与django本地的随机字符串做比较,来判断是否阻止页面提交。达到防止跨站请求伪造。 7.2、Form提交(CSRF)7.3、Ajax提交(CSRF)
CSRF请求头 X-CSRFToken{% csrf_token %}
7.4、CSRF的全局与个例
# 全局:# 在settings.py 中 注释或者不注释-中间件 django.middleware.csrf.CsrfViewMiddleware# 局部:# 在单独的views中的方法,添加装饰器。from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。 @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
8、中间件
中间件,也叫管道。在 setting.py 中的 MIDDLEWARE_CLASSES 声明。
中间件的作用是,请求触发之后,现在进入中间件,在进入url.py。离开时,最后执行中间件。
中间件是一个类,继承 middlewareMixin。比如CsrfViewMiddleware(MiddlewareMixin)
值得一说的是,MiddlewareMixin方法中有两个重要的方法。process_request 和 process_response
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response
如何写一个中间件?
1、创建一个中间件的类,并继承MiddlewaveMixin
from django.utils.deprecation import MiddlewareMixinclass MiddlewaveRow1(MiddlewareMixin): # process_request:访问时触发 def process_request(self,request): print('MiddlewaveRow1--process_reques') # process_response:离开(返回)时触发 def process_response(self,request,response): print('MiddlewaveRow1--process_response') return responseclass MiddlewaveRow2(MiddlewareMixin): # process_request:访问时触发 def process_request(self,request): print('MiddlewaveRow2--process_reques') # process_response:离开(返回)时触发 def process_response(self,request,response): print('MiddlewaveRow2--process_response') return response
2、将创建的中间件加载到中间件池子中。
MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'Middlewave_pool.MiddlewavePool.MiddlewaveRow1', 'Middlewave_pool.MiddlewavePool.MiddlewaveRow2',]
3、随便访问一个页面吧,再回到IDE看控制台输出的信息。
4、中间件引申一下,如果在中间件process_request方法中,添加return方法。则,直接从当前中间件的process_response方法向出站方向执行。
不会进入下一个中间件,也不会进入url.py5、中间件的另外3种方法:
process_view(self,request,xx_view,xx_args,xx_kwargs) 此方法是在所有process_request执行完成后,读取url列表,获取view(不执行) 注:xx_view 是url指向的view方法。 注:xx_args 是url指向的view方法的默认参数。 例如,url(r'test/(\d+)',views.test) ,xx_args就是(\d+) 注:xx_kwargs 是url指向的view方法的指名参数。 例如,url(r'test/(?P<nid>(\d+))',views.test) ,xx_kwargs就是{nid:(\d+)}process_exception(self,request,excepton)
此方法默认不执行,只有在views执行报错时在触发。 注:excepton中存放的就是views的错误信息。process_template_response(self,request,response)
此方法默认不执行,只有在返回时,使用render方法时才会触发。 注:然并卵……6、中间件的声明周期
补充:
#中间件中可以定义四个方法,分别是:# 第一次进来时,只有request,穿过所有中间层,取得views方法和views参数后,离开。process_request(self,request)# 第二次带着views方法和参数,再走一遍中间件。中间可以处理一些事情了。process_view(self, request, callback, callback_args, callback_kwargs)# 到这里,process_request和process_view不能有return,否则直接跳转到process_response#只有在离开时使用render()方法,才会触发。process_template_response(self,request,response)# 页面报错时,在这里处理错误。process_exception(self, request, exception)# 页面正常返回时,在这里定制需求。process_response(self, request, response)
9、缓存
Django中提供了5种缓存方式:
1、开发调试
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func
2、内存
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } # 注:其他配置同开发调试版本
3、文件
# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } # 注:其他配置同开发调试版本
4、数据库
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } } # 注:执行创建表命令 python manage.py createcachetable
5、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
6、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
缓存的应用三种应用:
1、全站
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
2、单独视图
方式一: from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
3、局部模板
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %}
10、内置信号和自定义信号
内置信号:
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 # 创建数据库连接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
from django.core.signals import request_finishedfrom django.core.signals import request_startedfrom django.core.signals import got_request_exceptionfrom django.db.models.signals import class_preparedfrom django.db.models.signals import pre_init, post_initfrom django.db.models.signals import pre_save, post_savefrom django.db.models.signals import pre_delete, post_deletefrom django.db.models.signals import m2m_changedfrom django.db.models.signals import pre_migrate, post_migratefrom django.test.signals import setting_changedfrom django.test.signals import template_renderedfrom django.db.backends.signals import connection_createddef callback(sender, **kwargs): print("xxoo_callback") print(sender,kwargs)xxoo.connect(callback)# xxoo指上述导入的内容
from django.core.signals import request_finishedfrom django.dispatch import receiver@receiver(request_finished)def my_callback(sender, **kwargs): print("Request finished!")
自定义信号:
1、定义信号
import django.dispatchpizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
2、注册信号
def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback)
3、触发信号
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456)
11、FORM操作
Django的Form主要具有一下几大功能:
- 生成HTML标签
- 验证用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
- 初始化页面显示内容
11.1、小试牛刀
创建formfrom django.forms import Formfrom django.forms import widgetsfrom django.forms import fields class MyForm(Form): user = fields.CharField( widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'}) ) gender = fields.ChoiceField( choices=((1, '男'), (2, '女'),), initial=2, widget=widgets.RadioSelect ) city = fields.CharField( initial=2, widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) ) pwd = fields.CharField( widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )
views
from django.shortcuts import render, redirectfrom .forms import MyForm def index(request): if request.method == "GET": obj = MyForm() return render(request, 'index.html', {'form': obj}) elif request.method == "POST": obj = MyForm(request.POST, request.FILES) if obj.is_valid(): values = obj.clean() print(values) else: errors = obj.errors print(errors) return render(request, 'index.html', {'form': obj}) else: return redirect('http://www.google.com')
HTML
其他form标签
11.2、Form类
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成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-01TimeField(BaseTemporalField) 格式:11:12DateTimeField(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类型 ...
注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串
Django内置插件:
# from django.forms import widgets# class MyForm(Form):# user = fields.CharField(# widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})# )# widgets.xxoo xxoo就是对应以下HTML标签的生成器。TextInput(Input) # type="text"NumberInput(TextInput) # type="number"EmailInput(TextInput) # type="email"URLInput(TextInput) # type="url"PasswordInput(TextInput) # type="password"HiddenInput(TextInput) # type="hidden",隐藏的输入Textarea(Widget) # 文本域,textarea标签DateInput(DateTimeBaseInput) # type="text",带有可选参数format('%Y年%m月%d日,%b缩写月,%B全拼月')DateTimeInput(DateTimeBaseInput) # type="text",带有可选参数format('%y两位年-%Y-%m-%d %H:%M:%S.%f')TimeInput(DateTimeBaseInput) # type="text",带有可选参数format('%H:%M:%S.%f')CheckboxInput # type="checkbox"Select # 下拉菜单,select标签,内涵choices方法NullBooleanSelect # 选项为‘Unknown’、‘Yes’ 和‘No’。SelectMultiple #
11.3、常用选择插件
# 单radio,值为字符串user = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))) # 单radio,值为字符串user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.RadioSelect) # 单select,值为字符串user = fields.CharField( initial=2, widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))) # 单select,值为字符串user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select) # 多选select,值为列表user = fields.MultipleChoiceField( choices=((1,'上海'),(2,'北京'),), initial=[1,], widget=widgets.SelectMultiple) # 单checkboxuser = fields.CharField( widget=widgets.CheckboxInput()) # 多选checkbox,值为列表user = fields.MultipleChoiceField( initial=[2, ], choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxSelectMultiple)
拓展: 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom django.core.validators import RegexValidator class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) # 因为CharField没有choiecs属性,所以无法user.choiecs user2 = fields.CharField(widget=widgets.select(choices=[])) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) # self.fields['user'].choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption') # user2的创建方式是CharField , user的创建方式是choiceField self.fields['user2'].widget.choices = models.XXX.objects.all().value_list('id','caption')
方式二: 使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
from django import formsfrom django.forms import fieldsfrom django.forms import widgetsfrom django.forms import models as form_modelfrom django.core.exceptions import ValidationErrorfrom django.core.validators import RegexValidator class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())# model中的NNewTpye类方法,必须设置__str__(),指定返回的内容。否则authors中存入的是 "NNewType objects"
11.4、自定义验证规则
方式一:
from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
方式二:
import refrom django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom 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 Formfrom django.forms import widgetsfrom 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]
11.5、初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
Form
from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField() city = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), widget=widgets.Select )
Views
from django.shortcuts import render, redirectfrom .forms import MyForm def index(request): if request.method == "GET": values = {'user': 'root', 'city': 2} obj = MyForm(values) return render(request, 'index.html', {'form': obj}) elif request.method == "POST": return redirect('http://www.google.com') else: return redirect('http://www.google.com')
HTML
11.6、动态操作select数据
# viewsfrom django.shortcuts import renderfrom app01.forms import UserInfoFormdef index(request): obj = UserInfoForm() # 实例化form对象 # obj = UserInfoForm({'字段':"默认值"}) # 初始化Form # 每个用到usertype的地方,都要写一句这个太麻烦,所以写在构造函数中,一劳永逸。 # obj.fields['user_type'].choices = models.UserType.objects.values_list('id','name') return render(request, 'index.html', {'obj': obj})
# forms.pyfrom django import formsfrom django.forms import fieldsfrom django.forms import widgetsfrom app01 import modelsclass UserInfoFrom(forms.Form): user = fields.CharField( required=False, widget=widgets.Textarea(attrs={"class": "c1"}) ) pwd = fields.CharField( max_length=12, widget=widgets.PasswordInput(attrs={"class": "c1"}) ) user_type = fields.ChoiceField( # choices=[("0", "游客"), ("1", "普通用户"), ("2", "超级用户")], # 此为默认方法 choices=models.UserType.objects.values_list("id", "name"), # 此方法为-数据库读取 # 上面这个方法存在的问题是,当修改数据库后,只有服务器重启后,页面才刷新 # 所以使用构造函数的方法 __init__() widget=widgets.Select() ) def __init__(self, *args, **kwargs): super(UserInfoFrom, self).__init__(*args, **kwargs) # 每次刷新页面时,会重新读取数据库。 self.fields['UserType'].choices = models.UserType.objects.values_list("id", "name") # 下面使用的是CharField方法时。 self.fields['UserType'].widgets.choices = models.UserType.objects.values_list("id", "name")
Form验证----内置钩子
# views.pydef register(request): from app01.forms import ResisterForm obj = ResisterForm(request.POST) if obj.is_valid(): return render(request, 'register.html', {'obj': obj})
# forms.pyclass RegisterForm(forms.Form): user = fields.CharField() pwd = fields.CharField() # form 的第一类钩子,字段的钩子 def clean_user(self): # 此钩子是单纯只处理user字段的验证,其他字段可单独再次验证。 # 比如,def clean_pwd(self): ... 是单独处理pwd字段。 c = models.UserInfo.objects.filter(name=self.cleaned_data["user"]).count() if not c: return self.cleaned_data['user'] else: from django.core.exceptions import ValidationError raise ValidationError("用户名已存在", code="xx") #form 的第二类钩子,整体的钩子 def clean(self): c = models.UserInfo.objects.filter(name=self.cleaned_data['user'],pwd=self.cleaned_data['pwd']).count() if c: # 这里返回的是整体的数据,而不是分字段中的 self.cleaned_data['user']。 return self.cleaned_data else: from django.core.exceptions import ValidationError raise ValidationError("用户名密码错误") # form 的第三类钩子 def _post_clean(self): pass
from错误信息
# views.pydef register(request): from app01.forms import ResisterForm from django.core.exceptions import NON_FIELD_ERRORS obj = ResisterForm(request.POST) if obj.is_valid(): # obj.cleaned_data return render(request, 'register.html', {'obj': obj}) else: # obj.errors """ { "__all__":[], # 整体错误信息 "NON_FIELD_ERRORS":[], # 整体错误信息的另一个方式 "user":[{"code":"required","message":"xxxxx"}], "pwd":[{"code":"required","message":"xxxxx"}], } """ return render(request, 'register.html', {'obj_error': obj.errors})
form序列化错误信息
# viewsimport jsonfrom django.shortcuts import HttpResponsefrom django.core.exceptions import ValidationErrorclass JsonCustomEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, ValidationError): return {'code': o.code, 'messages': o.messages} else: return json.JSONEncoder.default(self, o)def login(request): if request.method == "GET": return render(request, "login.html") if request.method == "POST": ret = {'status': True, 'error': None, 'data': None} obj = LoginForm(request.POST) if obj.is_valid: print(obj.cleaned_data) else: ret["status"] = False ret['error'] = obj.errors.as_data() # json.dumps(str, cls=xx) cls参数是指定每次转码时触发的函数 result = json.dumps(ret, cls=JsonCustomEncoder) return HttpResponse(result)
form序列化操作总结---- 万变不离其宗
# views# --------ErrorDict--------# 自定义 encoderimport jsonfrom django.core.exceptions import ValidationErrorclass JsonCustomEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, ValidationError): return {'code': o.code, 'messages': o.messages} else: return json.JSONEncoder.default(self, o)# --------QuerySet--------# 第一种方式:serializers 模块from django.core import serializersv = models.tb.objects.all() # v是一个queryset类型data = serializers.serialize("json", v) # 进行序列化~~但格式是django指定的格式# 第二种方式:import jsonfrom datetime import datefrom datetime import datetimefrom django.shortcuts import HttpResponseclass JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): # 还记得strftime方法吗?按照指定格式输出字符串 return field.strftime("%Y-%m-%d %H:%M:%S") elif isinstance(field, date): return field.strftime("%Y-%m-%d") else: # 如果是其他类型,则按照默认方式转码 return json.JSONEncoder.default(self, field)def index(request): v = models.tb.objects.values("id", "name", "ctime") # 这里的ctime 是时间格式的呦~ v = list(v) v = json.dumps(v, cls=JsonCustomEncoder) return HttpResponse(v)
12、ModelForm---(不建议使用,应该使用Form和Model分离的方式)
1、数据库操作
2、数据验证 适合小项目。或者,自定制adminModelForm a. class Meta: model, # 对应Model的 fields=None, # 加载Model中指定的字段,全选则fields="__all__" exclude=None, # 加载Model中没有指定的字段 labels=None, # 提示信息,类似Model的verbose_name help_texts=None, # 帮助提示信息 widgets=None, # 自定义插件 django.forms import widgets as Fwidget |widgets={"指定的fields":fwidgets.Textarea} error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) # 错误信息例子:error_messages = {"name":{"required":"名字不能为空"}} 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={...})
14、Ajax操作
1、原生ajax