Django框架

Django框架

目录

基本配置

1.安装

pip3 install django

2.创建django project

通过终端创建

1.创建django程序

django-admin startproject <proecjt名>
例如:
django-admin startproject mysite

2.创建project中的app文件目录

python manage.py startapp <app名>
例如:
python manage.py startapp app01

3.进入程序目录

cd mysite

4.启动socket服务端,等待用户发送请求(不写端口默认8000)

python manage.py runserver 127.0.0.1:8080

5.其他命令

python manage.py createsuperuser # 用于django admin创建超级用户
python manage.py makemigrations # 记录对models.py文件的改动
python manage.py migrate # 将改动映射到数据库

通过IDE创建

通过IDE(pycharm等)创建django project,以上命令均自动执行

3.project目录结构

目录结构介绍

- mysite(~/PycharmProjects/mysite)
|- app01/
|- admin.py # Django自带管理配置后台
|- models.py # 写类,根据类创建数据库表
|- test.py # 单元测试
|- views.py # 视图函数
|- apps.py # 配置文件
|- mysite/
|- init.py
|- setting.py
|- urls.py
|- wsgi.py
|- static/
|- templates/
|- utils/
|- manage.py

4.setting.py配置文件

指定template目录

在settings.py 文件中,指定template文件路径

TEMPLATES = [
    {
    	...
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
     },

指定static目录

在settings.py 文件中,添加STATICFILES_DIRS,注意要加逗号

STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),  # 逗号
)

配置数据库

  • Django默认使用的数据库是sqllite3,我们要使用mysql就需要利用第三方工具连接数据库
  • Django内部连接mysql的第三方工具是MySQLDB,但是python3不再支持MySQLDB
  • 所以得修改django默认连接mysql方式为pymysql

1.创建mysql数据库
2.修改配置settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dbname',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': 3306,
    }
}

3.修改项目同名文件下__init__.py文件
pymsql替换内部的myqldb,修改django默认连接mysql第三方工具

import pymysql
pymysql.install_as_MySQLdb()

路由系统

路由系统简介

路由就是在urls.py文件中的url和函数的对应关系

# urls.py文件
from django.conf.urls import url
from django.contrib import admin

# 对应关系
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^login/', login),  # 对应关系
]

其中,

  • 对应关系:/login/, login
  • 请求参数:request
  • 返回方式:HttpResponse\render\redirect

案例

from django.conf.urls import url
from django.contrib import admin

from django.shortcuts import HttpResponse,render,redirect
def login(request):
    print(request.GET)
    if request.method == "GET":
        return render(request,'login.html')
    else:
        # 用户POST提交的数据(请求体)
        u = request.POST.get('user')
        p = request.POST.get('pwd')
        if u == 'root' and p == '123':
            # 登录成功
            return redirect('/index/')
        else:
            # 登录失败
            return render(request,'login.html',{'msg': '用户名或密码错误'})
            
urlpatterns = [
# url(r'^admin/', admin.site.urls),
url(r'^login/', login),
url(r'^index/', index),
]

1.单一路由对应

url(r'^index$', views.index),

2.基于正则的路由

基于正则的路由可以传递参数,根据传递方式可以分为,静态路由和动态路由两种

静态路由

我们都知道url可以通过get方式传参数,这种方式被称作静态路由

url(r'^index/?id=12', views.index),

对应views.py中的def index(request),可以根据request.GET.get('id')获取参数值

动态路由

路由中的url部分其实就是正则表达式,所以可以使用正则表达式替代get传参数,传递方式有两种 传递位置参数 和 传递命名参数

位置参数

位置参数对参数顺序有要求

url(r'^index/(\w+)/', views.index), # (\w+)可以匹配任意字母或数字或下划线或汉字作为位置参数,对参数顺序有要求

匹配的参数会被def index(request,a)中的参数a接收

命名参数

参数通过参数名对应,所以对顺序没要求

url(r'^index/(?P\d+)/(?P\d+)/', views.index), # (?P\d+)可以指定匹配任意数字并且参数名为a1

匹配的参数被def index(request,a1,a2)中的参数a1接收,对参数顺序没有要求

注意:位置传递和命名传递不可混用,所以使用要统一

终止符

因为url就是为正则表达式,所以url前部匹配成功后就会调用函数,所以为了完美匹配可以在url结尾跟/或者$

url(r'^index$', views.index), # 只会匹配index
url(r'^index', views.index), # 会全部匹配index开头字符串

伪静态

SEO(Search Engine Optimization,搜索引擎优化)静态网页的搜索权重高于动态网页,所以通过伪造动态网页为静态网页后缀达到权重增加的目的

url(r'^index/(/w+).html$', views.index), # 表示页面以.html结尾

所以,url正则表达式方式 + 伪静态 = 动态路由
不出现‘?id=..’get传参的写法,可以提高SEO的搜索权重

默认页面

如果用户输入url不存在,可以制定到默认页面

url(r'^', <指定函数名>),

3.添加额外的参数

url(r'^manage/(?P\w*)', views.manage,{'name':'kirs'}),</p> <h3 id="4路由分发">4.路由分发</h3> <p>根据据app对路由规则进行分类</p> <p>一个Django项目中可能会有众多业务线(众多app),如果一都使用同一个路由系统可能会导致url重名问题,所以可以根据每个app建立自己的urls.py文件,通过项目的路由系统来找到各自app的路由对应关系</p> <p>1.在app01中新建urls.py,并import自己的views</p> <pre><code class="language-py">from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index.html$', views.index), ] </code></pre> <p>2.在项目的urls.py中import包include,<br> 在项目的urls.py中写入app01url和include内容</p> <pre><code class="language-py">from django.conf.urls import url,include urlpatterns = [ url(r'^app01/', include('app01.urls')), ] </code></pre> <p>这样,url来了,项目路由会先找对应app,找到后,去对应app中的urls中找对应关系</p> <h3 id="5url关系命名">5.url关系命名</h3> <p>给url与函数关系命名</p> <h4 id="视图函数应用">视图函数应用</h4> <p>将来在视图函数中可以根据别名反生成url</p> <p>- 1.首先,urls.py中<br> url(r'^index/', views.index, name='name1'),</p> <p>- 2.在views.py中import包reverse<br> from django.urls import reverse<br> url = reverse('name1') # 获取到/index/</p> <p>反生成带特定 位置参数的url</p> <p>- 1.urls.py中<br> url(r'^index/(\w+)/', views.index, name='name1'),<br> - 2.在views.py中<br> from django.urls import reverse<br> url = reverse('name1',args=(abc,)) # 获取到/index/abc/</p> <p>反生成带特定 命名参数的url</p> <p>- 1.urls.py中<br> url(r'^index/(?P<b1>\w+)/', views.index, name='name1'),<br> - 2.在views.py中<br> from django.urls import reverse<br> url = reverse('name1',kwargs={'b1':abcd,}) # 获取到/index/abcd/</p> <h4 id="html应用">html应用</h4> <p>将来在html页面中可以根据别名取得url地址</p> <p>- 1.urls.py中<br> url(r'^index/', views.index, name='name1'),</p> <p>- 2.a标签取url<br> - 原来写法<br> <a href="/index/"></a></p> <p>- 现在写法<br> <a href="{% url "name1" %}"></a> # 效果就是/index/</p> <p>动态传递参数怎么办?</p> <p>- 1.urls.py中<br> url(r'^index/(\w+)/(\w+)/', views.index, name='name1'),</p> <p>- 2.a标签取url<br> - 原来写法:<br> <a href="/index/(\w+)/(\w+)/"></a> <br> - 现在写法:<br> <a href="{% url "name1" 参数1 参数2 %}"></a> # 效果就是/index/参数1/参数2<br> # <a href="{% url "name1" x y %}"></a> # 效果就是/index/peter/alex</p> <p>PS:url与函数关系命名可以用于权限管理简化存储内容:数据库存储别名即可,就不用存储很长的url地址</p> <hr> <h2 id="模版引擎">模版引擎</h2> <h3 id="模版的执行">模版的执行</h3> <p>模版的创建过程,对于模版,其实就是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户。</p> <p>其中,返回方式:HttpResponse\render\redirect</p> <pre><code class="language-py">#views.py from django.shortcuts import HttpResponse, render, redirect # 视图函数 def login(request): # return HttpResponse('massage') # 直接获取内容返回用户 return render(request, 'login.html', {'key':'value'}) # 自动找到templates路径下的login.html文件,读取内容并返回给用户 # return redirect('/login/') # 重定向文件 </code></pre> <h4 id="render-返回参数">render 返回参数</h4> <p>返回参数可以是</p> <p>字符串:'name': 'alex',<br> 列表:'users':['tom','kirs'],<br> 字典:'user_dict':{'k1': '123','k2':'234'},<br> 列表套字典:<br> 'user_list_dict': [<br> {'id':1, 'name': 'alex', 'email': 'alex3714@163.com'},<br> {'id':2, 'name': 'alex2', 'email': 'alex3714@1632.com'},<br> {'id':3, 'name': 'alex3', 'email': 'alex3713@1632.com'},<br> ]</p> <p>html 中取值,直接通过.取值</p> <p>{{users.0}} {{user.1}}</p> <p>案例</p> <pre><code class="language-py">def index(request): # return HttpResponse('Index') return render( request, 'index.html', { 'name': 'alex', 'users':['李志','李杰'], 'user_dict':{'k1': 'v1','k2':'v2'}, 'user_list_dict': [ {'id':1, 'name': 'alex', 'email': 'alex3714@163.com'}, {'id':2, 'name': 'alex2', 'email': 'alex3714@1632.com'}, {'id':3, 'name': 'alex3', 'email': 'alex3713@1632.com'}, ] } ) </code></pre> <h3 id="模版语言">模版语言</h3> <h4 id="html-渲染方式">html 渲染方式</h4> <p>{{ }}</p> <p>{% for i in [列表|字典] %}<br> {{i.列1}}<br> {{i.列2}}<br> ...</p> <p>{% if 条件%}<br> ...<br> {% else %}<br> ...</p> <h4 id="母版">母版</h4> <p>之前说过的母版就是将公用内容存到html,其他子板extends即可</p> <p>{% extends "base.html" %}</p> <h4 id="include">include</h4> <p>类似于python中的import<br> 可以自定义html小组件,将小组件倒入其他页面应用,并且一个页面呢可以多次导入</p> <p>{% include 'pub.html' %} # pub.html为组件</p> <pre><code class="language-html">#pub.html <div> <div class='...'>{{ name }}<> <div> </code></pre> <p>PS:并且组件中可以接受后台参数</p> <h4 id="模版引擎-1">模版引擎</h4> <p>在模版引擎中传入函数名,自动执行函数,不用加括号<br> 通过后台传来的字典具有方法<br> keys</p> <p>- 遍历key<br> {% for i in user_list.keys%}<br> {{i}}<br> {% endfor%}<br> values</p> <p>- 遍历value<br> {% for i in user_list.values%}<br> {{i}}<br> {% endfor%}<br> items</p> <p>- 遍历key和value<br> {% for k,v in user_list.items%}<br> {{k}}<br> {{v}}</p> <h4 id="内置方法">内置方法</h4> <p>内置方法通过|方法名执行</p> <p>- 将字符串大写</p> <h3 id="模版自定义函数">模版自定义函数</h3> <h4 id="创建模版自定义函数simple_filter">创建模版自定义函数simple_filter</h4> <p>1.在app创建一个名为templatetags文件(名称不可改)<br> 2.创建任意templatetags/xx.py文件,函数增加装饰器@register.filter</p> <pre><code class="language-py">#xx.py from django import template register = temlate.Library() @register.filter def my_upper(value): return value.upper() </code></pre> <p>3.导入文件:在html文件中导入之前创建的 xx.py 文件名</p> <p>{% load xxx %}<br> 4.使用:</p> <p>{{name|my_upper}}<br> 5.settings.py注册app</p> <p>INSTALLED_APPS = (<br> ...<br> 'app',<br> )</p> <p>创建模版自定义函数simple_filter最多只能有两个参数,但是可以做条件判断</p> <p>- 最多两个参数,方式: {{第一个参数|函数名称:"第二个参数"}}<br> def my_upper(value,arg):<br> return value+arg</p> <p>{{name|my_upper:'abc'}}<br> - 可以做条件判断</p> <h4 id="创建模版自定义函数simple_tag">创建模版自定义函数simple_tag</h4> <p>在以上创建过程中,如果函数增加装饰器@register.simple_tag</p> <pre><code class="language-py"> @register.simple_tag def my_lower(value): return value.lower() </code></pre> <p>使用时参数个数无限制</p> <p>- 参数无限制:{% 函数名 参数 参数 ...%}<br> def my_lower(value,arg1,arg2,...):<br> return value + arg1 + arg2 +...</p> <p>{% my_lower name 'abc' 'bcd' ...%}</p> <hr> <h2 id="视图函数">视图函数</h2> <h3 id="request-请求信息">request 请求信息</h3> <p>request.method - 用户请求方法,GET或者POST<br> request.GET - 获取GET方式提交的数据<br> request.POST — 获取POST方式提交的数据</p> <p>request.GET.get()<br> request.POST.get()<br> request.POST.getlist() - 获取列表</p> <p>其中,客户端POST方式提交,request同样可以获取GET数据,反之不可以</p> <h3 id="视图函数-cbv-fbv">视图函数 CBV FBV</h3> <p>路由系统通过反射方式匹配method(post、get、put等)。</p> <p>用户发来请求,url匹配到类,将method当作参数传入类,通过getattr找到对应方法。</p> <h4 id="fbv-function-based-views">FBV (Function-based views)</h4> <p>路由系统中,url对应函数就是FBV</p> <p>urls.py</p> <p>- 直接跟方法名<br> url(r'^login.html$',views.login),</p> <p>views.py</p> <p>- 类名需要继承View,可以定义get、post等方法<br> def login(request):<br> return render(request, "login.html")</p> <h4 id="cbv-class-based-views">CBV (Class-based views)</h4> <p>同样,url也可以对应类,那么这就是CBV</p> <p>urls.py</p> <p>- 类名后跟.as_view()<br> url(r'^login.html$',views.Login.as_view()),</p> <p>views.py</p> <p>- 类名需要继承View,可以定义get、post等方法<br> from django.views import View</p> <p>class Login(View):<br> def get(self, request):<br> return render(request, "login.html")</p> <p>def post(self, request):<br> print(request.POST.get("user"))<br> return HttpResponse("login.post")</p> <p>from表单只有get、post方法,ajax不仅有post和get方法,还有很多其它方法,如put、delete等<br> PS:约定俗成:get查 post创建 put更新 delete删除</p> <h4 id="dispatch-方法">dispatch 方法</h4> <p>在CBV的get、post方法执行之前View内部会先执行dispatch方法</p> <pre><code class="language-py">def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) </code></pre> <p>所以我们可以重写dispatch方法,起到类似装饰器功能</p> <p>装饰get、post等方法</p> <pre><code class="language-py">def dispatch(self,request,*args,**kwargs): #执行get、post前代码 func = super(Login,self).dispatch(request,*args,**kwargs) #执行get、post后代码 return func </code></pre> <hr> <h2 id="中间件">中间件</h2> <h3 id="原理介绍">原理介绍</h3> <p>django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。</p> <p>请求--><br> 中间件类1方法1--><br> 中间件类2方法1--><br> ...--><br> 路由系系统and视图函数 <br> ...<--<br> 中间件类2方法2<--<br> 中间件类1方法2<-- <br> 响应<--</p> <p>其中,<br> 方法1都是 process_request(),<br> 方法二都是 process_response()</p> <p>在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件</p> <pre><code class="language-py">MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] </code></pre> <p>请求按照MIDDLEWARE中的中间件顺序执行,如果其中一个中间件在process_request()出现错误,就会直接执行这个中间件的process_response(),请求停止继续执行,返回错误信息</p> <p>请求--><br> 中间件类1方法1--><br> 中间件类2方法1-出错<br> |<br> 中间件类2方法2<--<br> 中间件类1方法2<-- <br> 响应<--</p> <h3 id="自定义中间件">自定义中间件</h3> <p>应用环境:于用户认证,替代auth装饰器,省去给全部视图函数添加装饰器</p> <p>1.新建py文件,倒入入MiddlewareMixin<br> 2.类中函数:process_request、process_response<br> 3.注册中间件py文件</p> <pre><code class="language-py"># m1.py from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class M1(MiddlewareMixin): def process_request(self,request): print('m1.process_request') def process_view(self, request, callback, callback_args, callback_kwargs): print('m1.process_view') # response = callback(request,*callback_args,**callback_kwargs) # return response def process_response(self,request,response): print('m1.process_response') return response def process_exception(self, request, exception): print('m1.process_exception') def process_template_response(self,request,response): """ 视图函数的返回值中,如果有render方法,才被调用 :param request: :param response: :return: """ print('m1.process_template_response') return response </code></pre> <p>其中,<br> 1.process_request 不能有return,如果return,中间件不再继续执行,直接返回<br> 2.process_response 必须需要return 返回值<br> 3.proccess_view 匹配到对应函数,并不会执行,<br> 4.proccess_exception 异常处理,只有视图函数错误才会执行,中间件会从最后一个类中的proccess_exception开始向前执行,proccess_exception全部执行结束后,再从最后一个类中的process_response开始向前返回<br> 5.proccess_template_view 视图函数有render,才执行,对所有请求,或一部分请求做批量处理,应用:记录日志功能</p> <pre><code class="language-py"># settings.py MIDDLEWARE = [ ... m1.Middle1, ] </code></pre> <p>Ajax请求返回需要后台通过HttpResponse返回json.dumps字典数据,这个功能可以封装成另一个类调用render,利用中间件中的proccess_template_view实现</p> <pre><code class="language-py"># views.py from django.shortcuts import render,HttpResponse,redirect class JSONResponse: def __init__(self,req,status,msg): self.req = req self.status = status self.msg = msg def render(self): import json ret = { 'status': self.status, 'msg':self.msg } return HttpResponse(json.dumps(ret)) def test(request): # print('test') # return HttpResponse('...') ret = {} return JSONResponse(request,True,"错误信息") </code></pre> <p>PS:在django1.10中,如果process_request中有异常或者return,中间件会从当前类的process_response开始返回</p> <p>在django1.7或者1.8中,如果process_request中有异常或者return,中间件会从最后一个类中的process_response开始向前返回</p> <hr> <h2 id="admin">admin</h2> <h2 id="model">Model</h2> <p>Django对数据库操作使用关系对象映射(Object Relational Mapping,简称ORM),一般不使用原生sql(除非遇到复杂sql)</p> <h3 id="orm操作">ORM操作</h3> <p>Django中通过ORM操作数据库</p> <p>ORM操作表:可以创建表、修改表、删除表(SQLAlchemy却无法修改表)</p> <p>ORM操作行:增、删、改、查</p> <p>Django的ORM默认使用的数据库是sqllite3,我们要使用mysql就需要利用第三方工具连接数据库<br> Django默认连接mysql的第三方工具是MySQLDB,但是python3不再支持MySQLDB<br> 所以得修改django默认连接mysql方式为pymysql</p> <h3 id="创建表">创建表</h3> <h4 id="建表前准备配置文件">建表前准备:配置文件</h4> <p>1.创建数据库<br> 2.修改配置settings.py</p> <p>DATABASES = {<br> 'default': {<br> 'ENGINE': 'django.db.backends.mysql',<br> 'NAME': 'dbname',<br> 'USER': 'root',<br> 'PASSWORD': '',<br> 'HOST': 'localhost',<br> 'PORT': 3306,<br> }<br> }<br> 3.修改项目同名文件下__init__.py文件<br> pymsql替换内部的myqldb,修改django默认连接mysql第三方工具</p> <p>import pymysql<br> pymysql.install_as_MySQLdb()</p> <h4 id="1基本结构">1.基本结构</h4> <h5 id="a建表流程">a.建表流程</h5> <p>1.写model:<br> models.py文件</p> <p>from diango.db import models</p> <p>class UserInfo(models.Model):<br> uid = models.BigAutoField(primary_key=True) # int自增主键,默认不写django自动增加<br> username = models.CharField(max_length=32) #char(32)<br> password = models.CharField(max_length=64)<br> age = models.IntegerField(default=1) # int,默认值为1,也可以null=True</p> <p>2.app写入配置<br> settings.py,将app写入</p> <p>INSTALLED_APPS = [<br> ...<br> 'app01',<br> ]</p> <p>3.执行命令</p> <p>python3 manage.py makemigrations<br> python3 manage.py migrate</p> <p>PS:每次修改表结构和表结构后通过命令更新即可</p> <p>4.更新表<br> models.py文件</p> <p>class UserGroup(Models.Model):<br> title = models.CharField(max_length=32)</p> <p>class UserInfo(models.Model):<br> ...<br> ug = models.ForeignKey("UserGroup", null=True) # 制定外键表名,可以为空,创建外键</p> <h5 id="b数据类型字段">b.数据类型字段</h5> <h6 id="字符串">字符串</h6> <p>CharField(Field)<br> - 字符类型<br> - 必须提供max_length参数, max_length表示字符长度</p> <h6 id="字符串django-admin中使用数据类型的并且带有对应类型验证">字符串(Django Admin中使用数据类型的并且带有对应类型验证)</h6> <p>EmailField(CharField)<br> - email<br> IPAddressField(Field)<br> - ip<br> URLField(CharField)<br> - url<br> SlugField(CharField)<br> - url并提供验证支持 字母、数字、下划线、连接符(减号)<br> UUIDField(Field)<br> - 提供对UUID格式的验证<br> FilePathField(Field)<br> - file并提供读取文件夹下文件的功能<br> - 参数:<br> path, 文件夹路径<br> match=None, 正则匹配<br> recursive=False, 递归下面的文件夹<br> allow_files=True, 允许文件<br> allow_folders=False, 允许文件夹<br> FileField(Field)<br> - 字符串,路径保存在数据库,文件上传到指定目录<br> - 参数:<br> upload_to = "" 上传文件的保存路径<br> storage = None 存储组件,默认django.core.files.storage.FileSystemStorage<br> ImageField(FileField)<br> - 字符串,路径保存在数据库,文件上传到指定目录<br> - 参数:<br> upload_to = "" 上传文件的保存路径<br> storage = None 存储组件,默认django.core.files.storage.FileSystemStorage<br> width_field=None, 上传图片的高度保存的数据库字段名(字符串)<br> height_field=None 上传图片的宽度保存的数据库字段名(字符串)<br> CommaSeparatedIntegerField(CharField)<br> - 字符串类型,格式必须为逗号分割的数字</p> <h6 id="时间">时间</h6> <p>DateTimeField(DateField)<br> - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]<br> DateField(DateTimeCheckMixin, Field)<br> - 日期格式 YYYY-MM-DD</p> <h6 id="数字">数字</h6> <p>IntegerField()<br> - 整数列(有符号的) -2147483648 ~ 2147483647<br> SmallIntegerField(IntegerField)<br> - 小整数 -32768 ~ 32767<br> BigIntegerField(IntegerField)<br> - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807<br> FloatField(Field)<br> - 浮点型<br> DecimalField(Field)<br> - 10进制小数<br> - 参数:<br> max_digits,小数总长度<br> decimal_places,小数位长度</p> <h6 id="布尔">布尔</h6> <p>BooleanField(Field)<br> - 布尔值类型<br> NullBooleanField(Field):<br> - 可以为空的布尔值</p> <h6 id="枚举django">枚举(django)</h6> <p>枚举没有自己的字段,是通过利用其它字段实现枚举功能</p> <p>color_list = (<br> (1,'黑色'),<br> (2,'白色'),<br> (3,'蓝色')<br> )<br> color = models.IntegerField(choices=color_list)</p> <h5 id="c字段参数">c.字段参数</h5> <p>null = True<br> - 为空<br> default = '123'<br> - 默认值<br> primary_key = True<br> - 主键<br> max_length = 12<br> - 最大长度<br> unique_for_date时间创建索引</p> <p>db_index = True<br> - 单列索引<br> unique = True<br> - 单列唯一索引</p> <p>class Meta:<br> # 联合唯一索引<br> unique_together = (<br> ('email','ctime'),<br> )<br> # 联合索引<br> index_together = (<br> ('email','ctime'),<br> )</p> <h4 id="2连表结构">2.连表结构</h4> <ul> <li>一对多:models.ForeignKey(其他表)</li> <li>多对多:models.ManyToManyField(其他表)</li> <li>一对一:models.OneToOneField(其他表)</li> </ul> <h5 id="多对多">多对多</h5> <p>多对多以及如何建立联合唯一索引</p> <h6 id="方式一foreignkey">方式一:ForeignKey</h6> <p>1.关系表Love中写Class Meta</p> <p>Class Love(models.Model):<br> b = models.ForeignKey('Boy')<br> # b = models.ForeignKey(to='Boy',to_field='id') #关联哪个表哪个字段<br> g = models.ForeignKey('Girl')</p> <p>Class Meta: # 建立唯一索引<br> unique_together = [<br> ('b','g'),<br> ]</p> <h6 id="方式二modelsmanytomanyfield">方式二:models.ManyToManyField</h6> <p>2.通过在Boy或Girl表中写 ManyToManyField(另一张表名)</p> <p>class Boy(models.Model):<br> name = models.CharField(max_length=32)<br> m = models.ManyToManyField('Girl')<br> 这样django会自动生成两表的关系表boy_m,<br> 但是boy_m表中只有三个字段:id、boy_id、girl_id,因为关系表boy_m是自动生成,没有对应类,所以不能直接通过获取关系表对象操作,那么要如何操作呢?</p> <p>对关系表boy_m的操作需要先获取一个Boy对象或者Girl对象</p> <p>- 获取对象<br> obj = models.Boy.objects.filter(name='alex').first()<br> - 增加<br> obj.m.add(2) # 给alex增加girl_id=2的数据<br> - 批量增加<br> obj.m.add(1,2,...)<br> - 列表增加<br> l = [1,2,...]<br> obj.m.add(*l)</p> <p>- 删除<br> obj.m.remove(1) # 给alex删除girl_id=1这条数据<br> - 批量删除,同添加<br> obj.m.remove(1,2,...)<br> - 列表删除,同添加<br> l = [1,2,...]<br> obj.m.remove(*l)</p> <p>- 重制<br> obj.m.set([1,2,...]) # 将原本的关系全部删除,插入心的关系</p> <p>- 查询<br> girl_list = obj.m.all() # 查询到的是 QuerySet列,其中是Girl object<br> - 条件查询<br> girl_list = obj.m.filter(nick='alice')</p> <p>- 清空<br> obj.m.clear() # 清空与alex有关全部数据</p> <p>那么对于Girl对象如何操作关系表呢?通过 小写表名_set实现</p> <p>- 获取对象<br> obj = models.Girl.objects.filter(nick='alice').first()<br> - 查询<br> boy_list = obj.boy_set.all()</p> <h6 id="方式三modelsmanytomanyfield--foreignkey">方式三:models.ManyToManyField + ForeignKey</h6> <p>方式一、方式二可以连用,ManyToManyField中需要though和through_fields,但是方式二中的用法只有查询和清空有效</p> <pre><code class="language-py">#models.py class Boy(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Girl',through="Love",through_fields=('b','g',)) # 查询和清空 class Girl(models.Model): nick = models.CharField(max_length=32) # m = models.ManyToManyField('Boy') class Love(models.Model): b = models.ForeignKey('Boy') g = models.ForeignKey('Girl') class Meta: unique_together = [ ('b','g'), ] </code></pre> <h5 id="单表自关联">单表自关联</h5> <p>数据库中一个表中可以根据id两两关联<br> 例如,有UserInfo表,让其中id进行关联,如何实现自关联?</p> <h6 id="新建关系表--foreignkey">新建关系表 + ForeignKey</h6> <p>新建关系表 + ForeignKey</p> <pre><code class="language-py"># models.py文件 class U2U(models.Model): # 关系表 b = models.ForeignKey('UserInfo', related_name='boys') g = models.ForeignKey('UserInfo', related_name='girls') # b = models.ForeignKey('UserInfo', related_query_name='boys') # g = models.ForeignKey('UserInfo', related_query_name='girls') </code></pre> <p>其中,要用到反向查找别名替换,如何让设置别名?这里涉及知识点</p> <p>related_query_name = 'b'<br> related_name = 'a'<br> 这样从关系表中查询UserInfo表中的信息时,使用别名即可</p> <p>obj.a_set.all() # 通过related_query_name起名<br> obj.a.all() # 通过related_name起名<br> 以上,通过ForeignKey + 关系表实现了 自关联</p> <p>查询到与id为1的男生有关系的女生nickname</p> <pre><code class="language-py"> obj = models.UserInfo.objects.filter(id=1).first() # 找到id为1的UserInfo对象obj result = obj.boys.all() # 与对象obj有关的所有信息:Querset列表[U2Ud对象] for i in result: print(i.g.nickname) # 从U2U对象正向查找g的nickname </code></pre> <h6 id="manytomanyfield">ManyToManyField</h6> <p>通过ManyToManyField实现</p> <pre><code class="language-py">#models.py class UserInfo(models.Model): ... m = models.ManyToManyField('UserInfo') </code></pre> <p>创建后会生成关系表,其中字段:id、from_userinfo_id、to_userinfo_id,其中,from_userinfo_id存放男生id,to_userinfo_id存放女生id</p> <p>查询与id=1的男生有关系的女生姓名</p> <pre><code class="language-py"> obj = models.UserInfo.objects.filter(id=1).first() result = obj.m.all() for i in result: print(i.nickname) </code></pre> <p>其中,obj.m是将obj作为from_userinfo_id,查询全部的to_userinfo_id</p> <p>查询到与id为4的女生有关系的男生nickname</p> <pre><code class="language-py"> obj = models.UserInfo.objects.filter(id=4).first() result = obj.userinfo_set.all() for i in result: print(i.nickname) </code></pre> <p>其中,userinfo_set是将obj作为to_userinfo_id,查询全部的from_userinfo_id</p> <h6 id="fk自关联-评论信息存储表">FK自关联 (评论信息存储表)</h6> <p>应用于评论信息存储表</p> <table> <thead> <tr> <th>id</th> <th>news_id</th> <th>conent</th> <th>user</th> <th>reply_id</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>1</td> <td>什么鬼</td> <td>alex</td> <td>null</td> </tr> <tr> <td>2</td> <td>1</td> <td>什么什么鬼</td> <td>egon</td> <td>1</td> </tr> <tr> <td>3</td> <td>1</td> <td>sb</td> <td>eric</td> <td>2</td> </tr> </tbody> </table> <pre><code class="language-py">#models.py class Comment(models.Model): news_id = models.IntegerField() conent = models.CharField(max_length=100) user = models.CharField(max_length=32) reply_id = models.ForeignKey('Comment', null=True, blank=True) </code></pre> <h3 id="操作表">操作表</h3> <h4 id="1基本操作">1.基本操作</h4> <p>views.py</p> <pre><code class="language-python">def sql(request): from app01 import models # 增加数据 models.UserGroup.objects.create(title="销售部") models.UserInfo.objects.create(username='root', password='123', age="20", ug_id=1) # 查询 user_list = models.UserInfo.objects.all() # 拿到QuerySet列表,其中存储的对像(一行就是一个对象) #models.UserInfo.objects.all().first() # 取到QuerySet中的第一个对象 group_list = models.UserGroup.objects.filter(id=1, title="销售部") # 条件查询 group_list = models.UserGroup.objects.filter(id__gt=1) # id>1(_lt小于) print(user_list, group_list) for i in user_list: # 显示字段 print(i.username, i.age) # 删除 models.UserInfo.objects.filter(id=2).delete() # 删除 # 更新 models.UserInfo.objects.filter(id=1).update(title="人事部") # 更新 return HttpResponse("...") </code></pre> <h4 id="2进阶操作">2.进阶操作</h4> <h5 id="获取行数-count">获取行数 .count()</h5> <p>models.UserInfo.objects.all().count()</p> <h5 id="比较-__gt">比较 __gt</h5> <p>models.UserInfo.objects.filter(id__gt=1) # id>1<br> models.UserInfo.objects.filter(id__gte=1) # id>=1<br> models.UserInfo.objects.filter(id__lt=2) # id<2<br> models.UserInfo.objects.filter(id__lte=2) # id<=2<br> models.UserInfo.objects.filter(id__gte=1, id__lt=2) # id>=1,id<2</p> <h5 id="in-和-not-in">in 和 not in</h5> <p>models.UserInfo.objects.filter(id__in=[1, 2, 4]) # 获取id在1,2,4中数据<br> models.UserInfo.objects.exclude(id__in=[1, 2, 4]) # 获取除id在1,2,4中的数据</p> <h5 id="isnull-为空">isnull 为空</h5> <p>查询字段为空的数据 ... where ut_id IS NULL</p> <p>models.UserInfo.objects.filter(ut__isnull=True)</p> <h5 id="contains-字段内容包含不包含">contains 字段内容包含/不包含</h5> <p>字段是否包含内容 ... where name like '%a%' /... where not (name like '%a%' )</p> <p>models.UserInfo.objects.filter(name__contains="a")<br> models.UserInfo.objects.filter(name__icontains="a") # 大小写不敏感<br> models.UserInfo.objects.exclude(name__icontains="a")</p> <h5 id="range-范围">range 范围</h5> <p>bettwen ... and ...</p> <p>models.UserInfo.objects.filter(id_range=[1, 3])</p> <h5 id="startswithistartswithendswithiendswith">startswith/istartswith/endswith/iendswith</h5> <p>... where name like 'a%'</p> <p>models.UserInfo.objects.filter(name__startswith='a')<br> models.UserInfo.objects.filter(name__istartswith='a')</p> <h5 id="排序-order_by">排序 order_by()</h5> <p>- 筛选结果按照id ASC排序<br> models.UserInfo.objects.filter(id__gt=1).order_by("id")<br> - DESC排序<br> models.UserInfo.objects.filter(id__gt=1).order_by("-id")<br> PS:order_by("id","name") 先按照id再按照name ASC排序</p> <h5 id="分组-annotate">分组 annotate()</h5> <p>- import聚合函数<br> from django.db.models import Count, Sum, Max, Min, Avg<br> - annotate()必须配合values(),values()中是要分组的列<br> models.UserInfo.objects.values("ut_id").annotate(x=Count('id'))<br> 对应sql:<br> "SELECT ut_id, COUNT(id) AS x FROM UserInfo GROUP BY ut_id"<br> PS:分组后结果再filter(),类似于group by后的having操作</p> <p>models.UserInfo.objects.values("ut_id").annotate(x=Count('id')).filter(ut_id__gt=1)</p> <h5 id="select_related">select_related()</h5> <p>查询时,主动做连表操作,类似于inner join操作</p> <p>- select_related(要连接表的外键)<br> q = models.UserInfo.objects.all().select_related('ut')<br> - 执行原理<br> " select * from UserInfo inner join UserType on UserInfo.ut = UserType.id "</p> <p>- 取值<br> for row in q:<br> print(row.name,row.ut.title)</p> <h5 id="prefetch_related">prefetch_related()</h5> <p>查询时,不做连表,作多次单表查询,实现连表操作目的</p> <p>- prefetch_related(外键)<br> q = models.UserInfo.objects.all().perfetch_related('ut')<br> - 执行原理<br> " select * from UserInfo "<br> 获取UserInfo全部ut组成列表[1,2,...]<br> " select * from UserType where id in [1,2,...] "</p> <p>- 取值<br> for row in q:<br> print(row.id,row.ut.title)</p> <p>PS:单表查询比连表操作查询性能高</p> <h5 id="sql语句-query">sql语句 .query</h5> <p>通过.query 可以查看sql语句</p> <p>result = models.UserInfo.objects.all()<br> print(result.query)</p> <h5 id="all">all()</h5> <p>获取所有的数据对象</p> <h5 id="filter">filter()</h5> <p>条件查询<br> 条件可以是:参数,字典,Q</p> <h5 id="exclude">exclude()</h5> <p>条件查询<br> 条件可以是:参数,字典,Q</p> <h5 id="annotate">annotate()</h5> <p>实现聚合group by 查询</p> <h5 id="distinct">distinct()</h5> <p>去重复<br> models.UserInfo.objects.values('nid').distinct()</p> <h5 id="order_by">order_by()</h5> <p>排序</p> <h5 id="extra">extra()</h5> <p>构造额外查询条件或者映射</p> <h5 id="reverse">reverse()</h5> <p>倒序<br> models.UserInfo.objects.all().order_by('-id').reverse()<br> PS:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序</p> <h5 id="defer">defer()</h5> <p>映射中排除某些列<br> models.UserInfo.objects.defer('name','id')<br> 或者<br> models.UserInfo.objects.filter(...).defer('name','id')</p> <h5 id="only">only()</h5> <p>仅取某个表中的数据<br> models.UserInfo.objects.only('name','id')<br> 或<br> models.UserInfo.objects.filter(...).only('name','id')<br> PS:使用only效率比all高,但是使用only也可以点出未包括在only中的列,但是效率会比all第,所以,only哪些列就使用哪些列</p> <h5 id="using">using()</h5> <p>制定使用的数据库,参数为别名,setting.py中设置的</p> <h5 id="raw">raw()</h5> <p>执行原生sql<br> models.UserInfo.objects.raw('select * from userinfo')<br> 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名<br> models.UserInfo.objects.raw('select pid as id from 其他表')<br> 为原生SQL设置参数<br> models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])<br> 将获取的到列名转换为指定列名<br> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}<br> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)<br> 指定数据库<br> models.UserInfo.objects.raw('select * from userinfo', using="default")</p> <h5 id="values">values()</h5> <p>获取每行数据为字典格式</p> <h5 id="values_list">values_list()</h5> <p>获取每行数据为元组</p> <h5 id="dates">dates()</h5> <p>def dates(self, field_name, kind, order='ASC'):<br> # 根据时间进行某一部分进行去重查找并截取指定内容<br> # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)<br> # order只能是:"ASC" "DESC"<br> # 并获取转换后的时间<br> - year : 年-01-01<br> - month: 年-月-01<br> - day : 年-月-日</p> <p>models.DatePlus.objects.dates('ctime','day','DESC')</p> <h5 id="datetimes">datetimes()</h5> <p>def datetimes(self, field_name, kind, order='ASC', tzinfo=None):<br> # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间<br> # kind只能是 "year", "month", "day", "hour", "minute", "second"<br> # order只能是:"ASC" "DESC"<br> # tzinfo时区对象<br> models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)<br> models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))</p> <p>"""<br> pip3 install pytz<br> import pytz<br> pytz.all_timezones<br> pytz.timezone(‘Asia/Shanghai’)<br> """</p> <h5 id="none">none()</h5> <p>def none(self):<br> # 空QuerySet对象</p> <h5 id="aggregate">aggregate()</h5> <p>def aggregate(self, *args, **kwargs):<br> # 聚合函数,获取字典类型聚合结果<br> from django.db.models import Count, Avg, Max, Min, Sum<br> result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))<br> ===> {'k': 3, 'n': 4}</p> <h5 id="count">count()</h5> <p>def count(self):<br> # 获取个数</p> <h5 id="get">get()</h5> <p>def get(self, *args, **kwargs):<br> # 获取单个对象</p> <h5 id="create">create()</h5> <p>def create(self, **kwargs):<br> # 创建对象</p> <h5 id="bulk_create">bulk_create()</h5> <p>def bulk_create(self, objs, batch_size=None):<br> # 批量插入<br> # batch_size表示一次插入的个数<br> objs = [<br> models.DDD(name='r11'),<br> models.DDD(name='r22')<br> ]<br> models.DDD.objects.bulk_create(objs, 10)</p> <h5 id="get_or_create">get_or_create()</h5> <p>def get_or_create(self, defaults=None, **kwargs):<br> # 如果存在,则获取,否则,创建<br> # defaults 指定创建时,其他字段的值<br> obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})</p> <h5 id="update_or_create">update_or_create()</h5> <p>def update_or_create(self, defaults=None, **kwargs):<br> # 如果存在,则更新,否则,创建<br> # defaults 指定创建时或更新时的其他字段<br> obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})</p> <h5 id="first">first()</h5> <p>def first(self):<br> # 获取第一个</p> <h5 id="last">last()</h5> <p>def last(self):<br> # 获取最后一个</p> <h5 id="in_bulk">in_bulk</h5> <p>def in_bulk(self, id_list=None):<br> # 根据主键ID进行查找<br> id_list = [11,21,31]<br> models.DDD.objects.in_bulk(id_list)</p> <h5 id="delete">delete()</h5> <p>def delete(self):<br> # 删除</p> <h5 id="update">update()</h5> <p>def update(self, **kwargs):<br> # 更新</p> <h5 id="exists">exists()</h5> <p>def exists(self):<br> # 是否有结果</p> <h4 id="3其他操作">3.其他操作</h4> <h5 id="f">F</h5> <p>获取数据表中列字段进行操作</p> <p>- import F<br> from django.db.models import F<br> - F("age")+1 age列自加1,更新时取到原来的值<br> v = models.UserInfo.objects.update(age=F('age') + 1)</p> <p>PS:v取到被修改的行数</p> <h5 id="q-字典传参数">Q 字典传参数</h5> <p>用于构造复杂的查询条件,两种用法,对象和方法</p> <p>- import Q<br> from django.db.models import Q</p> <p>对象 方式多用于一般条件查询,类似filter,所以不常用</p> <p>- Q对象,表示一个条件<br> models.UserInfo.objects.filter(Q(id=1))<br> - 多条件查询<br> models.UserInfo.objects.filter(Q(id=1) | Q(id=2))</p> <p>方法 方式多用于组合条件查询,动态查询</p> <p>同条件进行or,不同条件进行and,循环拼接查询条件</p> <pre><code class="language-py"> q1 = Q() q1.connector = "OR" q1.children.append(("id", 1)) q1.children.append(("id", 2)) q1.children.append(("id", 3)) q2 = Q() q2.connector = "OR" q2.children.append(("name", "alex")) q2.children.append(("name", "tom")) q2.children.append(("name", "peter")) con = Q() con.add(q1, "AND") con.add(q2, "AND") result = models.UserInfo.objects.filter(con) print(result) for i in result: print(i.name, i.ut.title) </code></pre> <p>类似查询:</p> <p>select * from UserInfo where (id=1 or id=2 or id =3)AND (name="alex" or name ="tom" or name = "peter")</p> <h5 id="extra-额外查询条件">extra 额外查询条件</h5> <p>在model基础上进行额外的查询条件以及相关表,排序</p> <p>extra的参数有select,select_params,where,params,tables,order_by</p> <p>- select可以作为临时表的查询结果放在select映射部分,其中x为别名,select_params配合select使用<br> models.UserInfo.objects.extra(select={"x": "select count(id) from app01_usertype where id >%s"},select_params=[1, ])<br> - where可以作为where条件部分,params配合where使用<br> models.UserInfo.objects.extra(where=['id>%s'], params=[1, ])<br> - tables可以作为原表连接的新表,组成笛卡尔积<br> models.UserInfo.objects.extra(tables=['app01_usertype'])<br> - order_by可以对原表排序<br> models.UserInfo.objects.extra(order_by=['-id'])</p> <h5 id="原生sql语句">原生sql语句</h5> <p>django原生sql已帮我们连接数据库,获取游标</p> <p>- 导入connection利用现成链接<br> from django.db import connection, connections<br> - 通过连接获取游标<br> cursor = connection.cursor() # 默认连接setting.py中的default DATABASES<br> - 可以选择其它数据库<br> # cursor = connections['default'].cursor() # default是选择setting.py中的DATABASES,可以根据需要变换数据库<br> - sql语句<br> cursor.execute("select * from app01_userinfo where id = %s", [1, ])<br> - fetchone或者fetchall<br> v = cursor.fetchone()</p> <h4 id="4连表操作">4.连表操作</h4> <h5 id="一对多">一对多</h5> <p>对象如何实现连表查询?<br> 答:通过外键实现</p> <p>通过操作表数据的介绍可知,一般查询方法有all(),filter(),如此这般查询到的结果是QuerySet列表,列表中存放的是对象(一行数据就是一个对象)</p> <p>通过values()查询到的结果是QuerySet列表,列表中存放的是字典</p> <p>通过values_list()查询到的结果是QuerySet列表,列表中存放的是元组</p> <h6 id="对象">对象</h6> <p>- 正向操作<br> 本表中有外键的,直接通过 外键列名.字段即可</p> <p>result = models.UserInfo.objects.all() # 获取UserInfo表中全部QuerySet列表<br> for i in result:<br> print(i.id, i.name, i.age, i.ut_id, i.ut.title) # 外键ut.title</p> <p>- 反向操作<br> 本表主键是其它表的外键,直接通过 对象.小写被外键的表名_set.all() 即可</p> <p>obj = models.UserType.objects.all().first() # 获取UserType表中的第一行QuerySet对象<br> list = obj.userinfo_set.all() # 小写被外键的表名_set.all()<br> print('用户类型', obj.id, obj.title)<br> for i in list:<br> print(i.name, i.age)</p> <h6 id="字典">字典</h6> <p>字典可以是直接通过values()得到,也可以是all().values()或者filter().values()得到</p> <p>- 正向操作<br> - 获取字典</p> <p>result = models.UserInfo.objects.all().values('id', 'name', 'ut__title')<br> 或者<br> result = models.UserInfo.objects.values('id', 'name', 'ut__title')</p> <p>- 通过键取值</p> <p>print(result)<br> for obj in result:<br> print(obj['id'], obj['name'], obj['ut__title'])</p> <p>其中,ut__title通过外键加双下划线列名,可以实现字典跨表查询</p> <p>- 反向操作<br> 想要通过字典反向跨表操作,通过 小写表名__列名 即可(只写小写表名为id)</p> <h1 id="字典反向操作">字典反向操作</h1> <p>result = models.UserType.objects.values("id", "title", "userinfo", "userinfo__age", "userinfo__name")<br> print(result)<br> for obj in result:<br> print(obj["id"], obj["title"], obj["userinfo"], obj["userinfo__age"], obj["userinfo__name"])</p> <h6 id="元组">元组</h6> <p>元组可以是直接通过values_list()得到,也可以是all().values_list()或者filter().values_list()得到</p> <p>- 正向操作<br> - 获取元组</p> <p>obj = models.UserInfo.objects.all().values_list('id','name','ut_title')<br> 或者<br> obj = models.UserInfo.objects.values_list('id','name','ut_title')</p> <p>- 通过索引取值</p> <p>obj[0],obj[1],obj[3]<br> 其中,ut__title通过外键加双下划线列名,可以实现元组的跨表查询</p> <p>- 反向操作<br> 想要通过元组反向跨表操作,通过 小写表名__列名 即可(只写小写表名为id)</p> <h1 id="元组反向操作">元组反向操作</h1> <p>result = models.UserType.objects.values_list("id", "title", "userinfo", "userinfo__age", "userinfo__name")<br> print(result)<br> for obj in result:<br> print(obj[0], obj[1], obj[2], obj[3], obj[4])</p> <p>PS:</p> <ul> <li>filter().values()或者filter().values_list()可以增加筛选条件,这样将筛选结果再跨表操作</li> <li>正向反向都是left join效果</li> </ul> <h5 id="多对多-1">多对多</h5> <p>已知:表Boy、表Girl、关系表Love<br> 要求:查询到与alex有关系的girl</p> <h6 id="一般查询">一般查询</h6> <p>有四种查询方式</p> <pre><code class="language-py"># 方式一:从Boy表获取alex对象,反向操作Love表得到全部与alex有关系list,循环list正向取得girl名字,查询2n+2次 obj = models.Boy.objects.filter(name="alex").first() # 查询1次 love_list = obj.love_set.all() # 查询1次 for i in love_list: # 查询 2n次 print(i.g.nick) # 方式二:Love表字典正向操作取得有alex的list,循环list正向取得girl名字,查询2n+1次 love_list = models.Love.objects.filter(b__name="alex") # 查询 1次 for obj in love_list: # 查询 2n次 print(obj.g.nick) # 方式三:Love表left join Girl表正向操作取的有alex的字典list,循环list取得girl名字,查询n+1次 love_list = models.Love.objects.filter(b__name='alex').values("g__nick") # 查询1次 for item in love_list: # 查询n次 print(item["g__nick"]) # 方式四:Love表inner join Girl表正向操作取的有alex的字典list,循环list取得girl名字,查询n+1次 obj = models.Love.objects.filter(b__name='alex').select_related("g") # 查询 1次 for i in obj: # 查询n次 print(i.g.nick) </code></pre> <p>PS:推荐后两种,查询效率稍高</p> <h2 id="form组件">Form组件</h2> <p>Django中Form组件主要功能:</p> <ul> <li>生成HTML标签</li> <li>验证用户数据(显示错误信息)</li> <li>HTML Form提交保留上次提交数据</li> <li>初始化页面显示内容</li> </ul> <h3 id="form组件简单示例">Form组件简单示例</h3> <p>1.创建Form类(确定字段约束)</p> <pre><code class="language-py">from django.forms import Form,fields class LoginForm(Form): username = fields.CharField( # html标签的name属性 就是 Form类字段名 max_length=18, min_length=6, required=True, error_messages={ 'required': '用户名不能为空', 'min_length': '太短了', 'max_length': '太长了', } ) </code></pre> <p>其中,<br> 字段不可为空: required=True<br> 字段长度: min_length~max_length<br> 错误信息:error_messages</p> <p>2.view函数(使用提交的数据)</p> <pre><code class="language-py"># 获取POST对象 obj = LoginForm(request.POST) # 是否校验成功 True或False v = obj.is_valid() # 所有错误信息 , 对象 obj.errors # 获取字典格式数据,通过**dic可以直接操作数据表的增改查询 obj.cleaned_data </code></pre> <p>3.html(前端生成标签)</p> <pre><code class="language-html">{{ obj.username }} {{ obj.errors.username.0 }} </code></pre> <p>PS:</p> <p>1.cleaned_data字典<br> 对数据表中的某行数据的增、改、查操作可以使用字典作为传入参数,精简代码,所以对数据表的操作可以直接将obj.cleaned_data字典数据当作参数传入</p> <p>dic = {'name':'alex','age':12,...}</p> <p>- 增加<br> models.Table.objects.create(<strong>dic)<br> - 更新<br> models.Table.objects.filter(</strong>dic)<br> - 查询 查询条件间是and关系<br> models.Table.objects.filter(**dic)</p> <p>2.is_valid()原理</p> <p>1.实例化 LoginForm</p> <p>self.fields{<br> 'user':正则表达式,<br> 'pwd':正则表达式,<br> ...<br> }</p> <p>2.循环 self.fields</p> <p>flag = True<br> for k, v self.fields.items(): # k:user,v:正则表达式<br> if request.POST.get(k)不符合v的正则表达式:<br> flag = False<br> return flag</p> <h3 id="form类">Form类</h3> <h4 id="django内置字段">django内置字段</h4> <p>Field<br> required=True, 是否允许为空<br> widget=None, HTML插件<br> label=None, 用于生成Label标签或显示内容<br> initial=None, 初始值<br> help_text='', 帮助信息(在标签旁边显示)<br> error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}<br> show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)<br> validators=[], 自定义验证规则<br> localize=False, 是否支持本地化<br> disabled=False, 是否可以编辑<br> label_suffix=None Label内容后缀</p> <p>CharField(Field)<br> max_length=None, 最大长度<br> min_length=None, 最小长度<br> strip=True 是否移除用户输入空白</p> <p>IntegerField(Field)<br> max_value=None, 最大值<br> min_value=None, 最小值</p> <p>FloatField(IntegerField)<br> ...</p> <p>RegexField(CharField)<br> regex, 自定制正则表达式<br> max_length=None, 最大长度<br> min_length=None, 最小长度<br> error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'}</p> <p>DecimalField(IntegerField)<br> max_value=None, 最大值<br> min_value=None, 最小值<br> max_digits=None, 总长度<br> decimal_places=None, 小数位长度</p> <p>BaseTemporalField(Field)<br> input_formats=None 时间格式化</p> <p>DateField(BaseTemporalField) 格式:2015-09-01<br> TimeField(BaseTemporalField) 格式:11:12<br> DateTimeField(BaseTemporalField)格式:2015-09-01 11:12</p> <p>DurationField(Field) 时间间隔:%d %H:%M:%S.%f<br> ...</p> <p>EmailField(CharField)<br> ...</p> <p>FileField(Field)<br> allow_empty_file=False 是否允许空文件</p> <p>ImageField(FileField)<br> ...<br> 注:需要PIL模块,pip3 install Pillow<br> 以上两个字典使用时,需要注意两点:<br> - form表单中 enctype="multipart/form-data"<br> - view函数中 obj = MyForm(request.POST, request.FILES)</p> <p>URLField(Field)<br> ...</p> <p>BooleanField(Field)<br> ...</p> <p>NullBooleanField(BooleanField)<br> ...</p> <p>ChoiceField(Field)<br> ...<br> choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)<br> required=True, 是否必填<br> widget=None, 插件,默认select插件<br> label=None, Label内容<br> initial=None, 初始值<br> help_text='', 帮助提示</p> <p>ModelChoiceField(ChoiceField)<br> ... django.forms.models.ModelChoiceField<br> queryset, # 查询数据库中的数据<br> empty_label="---------", # 默认空显示内容<br> to_field_name=None, # HTML中value的值对应的字段<br> limit_choices_to=None # ModelForm中对queryset二次筛选</p> <p>ModelMultipleChoiceField(ModelChoiceField)<br> ... django.forms.models.ModelMultipleChoiceField</p> <p>TypedChoiceField(ChoiceField)<br> coerce = lambda val: val 对选中的值进行一次转换<br> empty_value= '' 空值的默认值</p> <p>MultipleChoiceField(ChoiceField)<br> ...</p> <p>TypedMultipleChoiceField(MultipleChoiceField)<br> coerce = lambda val: val 对选中的每一个值进行一次转换<br> empty_value= '' 空值的默认值</p> <p>ComboField(Field)<br> fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式<br> fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])</p> <p>MultiValueField(Field)<br> PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用</p> <p>SplitDateTimeField(MultiValueField)<br> input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']<br> input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']</p> <p>FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中<br> path, 文件夹路径<br> match=None, 正则匹配<br> recursive=False, 递归下面的文件夹<br> allow_files=True, 允许文件<br> allow_folders=False, 允许文件夹<br> required=True,<br> widget=None,<br> label=None,<br> initial=None,<br> help_text=''</p> <p>GenericIPAddressField<br> protocol='both', both,ipv4,ipv6支持的IP格式<br> unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用</p> <p>SlugField(CharField) 数字,字母,下划线,减号(连字符)<br> ...</p> <p>UUIDField(CharField) uuid类型<br> ...</p> <h4 id="补充">补充</h4> <p>检测内置字段本身错误信息,例如,EmailField检测的就是字段不符合邮箱地址的格式</p> <p>fields.CharField(error_messages={'inalid','格式错误'})</p> <p>针对IntegerField字段,字段范围</p> <p>number = fields.IntegerField(max_value=100,min_value=15)</p> <p>生成标签有关字段</p> <p>t1 = fields.CharField(<br> label='用户名', # 标签的label命名<br> lable_suffix=':' # label内容后缀<br> help_text=‘请入电话号码’, # 标签帮助信息<br> initital='username', # input输入框显示默认值<br> disabled=True, # 输入框是否为可以编辑</p> <p>import django.forms import widgets<br> widget=widgets.Select, # 选择字段标签为select,默认是input<br> )</p> <p>html 调用</p> <p>{{ obj.t1.label }}{{ obj.t1.lable_suffix }}</p> <p>其实,html 调用 只需要写如下就可以了</p> <p>{{ obj.as_p }}</p> <p>PS:</p> <p>有些浏览器会自动帮我们增加校验,却掉浏览器的校验给标签加 novalidate 属性</p> <p><form action='...' method='...' movalidate >...</form></p> <h3 id="form生成html标签验证不刷新初始值全套">Form生成HTML标签、验证、不刷新、初始值全套</h3> <p>为了实现form提交不会刷新页面,我们之前使用ajax方法,同样的我们可以通过使用Django的form组件实现</p> <h4 id="ajax提交示例">ajax提交示例</h4> <p>通过ajax不刷新页面验证form,并且保留上次输入内容</p> <p>views.py</p> <pre><code class="language-py">def ajax_login(request): import json ret = {'status': True, 'message': None} obj = loginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: ret['status'] = False ret['message'] = obj.errors return HttpResponse(json.dumps(ret)) </code></pre> <p>login.html</p> <pre><code class="language-html"><script src="/static/jquery-3.2.1.js"></script> <script> function ajaxForm() { $('.c1').remove(); $.ajax({ url: '{% url "ajax_login" %}', type: 'POST', data: $('#f1').serialize(), dataType: 'JSON', success: function (arg) { if (arg.status) { location.href = '{% url index %}' } else { $.each(arg.message, function (index, value) { var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find("input[name='" + index + "']").after(tag) }) } } }) } </script> </code></pre> <p>login.html</p> <pre><code class="language-html"><form method="post" action="{% url 'login' %}" id="f1"> ... <a onclick="ajaxForm();" class="btn btn-default">ajax提交</a> </form> </code></pre> <p>PS:</p> <p>1.serialize()提交表单全部<br> ajax提交form表单时,可以将form表单内全部数据提交,使用serialize()方法</p> <pre><code class="language-html">data: $('#f1').serialize(), # 其中<form id='f1'> </code></pre> <p>提交原理</p> <p>user=alex&pwd=123456&scrftoken=...</p> <h4 id="form提交示例">form提交示例</h4> <p>不刷新页面验证form,并且保留上次输入内容</p> <p>1.创建form类</p> <pre><code class="language-py">from django.shortcuts import render, HttpResponse, redirect from django.forms import Form, fields, widgets from app01 import models class loginfForm(Form): username = fields.CharField( label='用户名', widget=widgets.Input(attrs={'class': 'form-control'}) ) password = fields.CharField( label='密码', widget=widgets.Input(attrs={'class': 'form-control', 'type': 'password'}) ) </code></pre> <p>2.view函数处理</p> <pre><code class="language-py">def loginf(request): if request.method == 'GET': obj = loginfForm() return render(request, 'loginf.html', {'obj': obj}) else: obj = loginfForm(request.POST) if obj.is_valid(): row = models.User.objects.filter(username=obj.cleaned_data['username'], password=obj.cleaned_data['password']).first() if row: return redirect('/index.html') return render(request, 'loginf.html', {'obj': obj}) </code></pre> <p>3.loginf.html</p> <pre><code class="language-html"><form method="post" action="{% url 'loginf' %}" novalidate> {% csrf_token %} {{ obj.username.label }}{{ obj.username }} {{ obj.errors.username.0 }} {{ obj.password.label }} {{ obj.password }} {{ obj.errors.password.0 }} <input type="submit" value="登录"> </form> </code></pre> <p>PS:</p> <p>单选:生成select字段,参数需是元组</p> <p>widget=widgets.Select(choices=[(1,'北京'),(2,'上海'),...])<br> widget=widgets.Select(choices=models.City.objects.values_list('id','title'))</p> <p>生成字段加css样式</p> <p>widget=widgets.TextInput(attrs={'class':'form-control'})</p> <h4 id="初始值data和initial">初始值(data和initial)</h4> <p>生成html标签,并含有错误信息</p> <p>obj = funcForm(request.POST)<br> 省略了data,本质就是<br> obj = funcForm(data = request.POST)</p> <p>只生成html标签,并且可以附带默认值</p> <p>obj = funcForm({'username':'alex','cls_id':[1,2,3]})<br> 省略了initial,本质是<br> obj = funcForm(initial = {'username':'alex','cls_id':[1,2,3]})</p> <h4 id="form验证">Form验证</h4> <p>1.首先,字段验证<br> 2.然后,单独字段方法 clean__参数<br> 3.最后,clean 方法</p> <h5 id="1正则校验">1正则校验</h5> <p>a.字段本身正则</p> <p>自定义正则表达式的字段有两种方式</p> <p>- 1.自定义验证规则,作为内置字段正则的补充<br> fields.CharField( validators=[] )</p> <p>- 2.自定义正则字段<br> fields.RegexField('010\d+') # 010开头的数字</p> <p>b.额外的正则</p> <p>from django.forms import Form,widgets,fields<br> from django.core.validators import RegexValidator</p> <p>class MyForm(Form):<br> user = fields.CharField(<br> validators=[RegexValidator(r'<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],<br> )</p> <h5 id="2验证字段是否已存在在数据库中">2验证字段是否已存在在数据库中</h5> <p>关键字:clean_字段 和 clean</p> <p>class funcForm(Form):<br> user = fields.CharField()<br> pwd = fields.CharField()<br> email = fields.CharField()</p> <h1 id="单个字段校验">单个字段校验</h1> <p>def clean_user(self):<br> v = self.cleand_data['user']<br> if models.Student.objects.filter(name=v).count():<br> # 主动异常<br> raise ValidationError('用户名已存在')<br> return self.cleaned_data['user'] # 必须返回,否则obj.cleaned_data['user']就被清空了</p> <p>def clean_pwd(self)😃<br> return self.cleaned_data['pwd'] # 必须返回</p> <h1 id="联合校验">联合校验</h1> <p>def clean(self):<br> user = self.cleand_data['user']<br> email = self.cleand_data['email']<br> if models.Student.objects.filter(name=user,email=email).count():<br> raise ValidationError('用户名和邮箱联合已经存在')<br> return self.cleaned_data # 必须返回,否则obj.cleaned_data就被清空了</p> <h4 id="制作插件">制作插件</h4> <h5 id="单选下拉菜单">单选下拉菜单</h5> <p>之前使用的IntegerField</p> <p>cls_id = fields.IntegerField(<br> # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])<br> widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})<br> )</p> <p>使用ChoiceField</p> <p>cls_id = fields.ChoiceField(<br> choices = models.Classes.objects.values_list('id','title'),<br> widget = widgets.Select()<br> )</p> <h5 id="可多选下拉菜单">可多选下拉菜单</h5> <p>首先尝试使用字段CharField</p> <p>cls_id = fields.CharField(<br> widget = widgets.SelectMultiple(choices = models.Classes.objects.values_list('id','title'))<br> )<br> 这样得到的cls_id结果是<br> { 'cls_id': "['1','2']" }<br> 其中,列表是SelectMultiple结果,但是列表又变成了字符串,那是因为CharField的作用,所以不能使用CharField</p> <p>所以真正的单选是这样的,使用ChoiceField,并且要把其中的choices参数提取出来</p> <p>cls_id = fields.MultipleChoiceField(<br> choices = models.Classes.objects.values_list('id','title'),<br> widget = widgets.SelectMultiple<br> )<br> 这样得到的cls_id结果是</p> <p>下拉列表中的数据在程序启动时加入内存,无法动态显示数据库中的变化,为了修复这个Bug,刷新无法动态显示数据库内容:</p> <p>方式一:</p> <p>class TeacherForm(Form):<br> tname = fields.CharField(min_length=2)<br> xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all())<br> # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all())</p> <p>方式二:</p> <p>class TeacherForm(Form):<br> tname = fields.CharField(min_length=2)<br> xx = fields.MultipleChoiceField(<br> # choices = models.Classes.objects.values_list('id','title') # 这句可注释<br> widget=widgets.SelectMultiple<br> )<br> def <strong>init</strong>(self,<em>args,**kwargs):<br> super(TeacherForm,self).<strong>init</strong>(</em>args,**kwargs)<br> self.fields['xx'].choices = models.Classes.objects.values_list('id','title')</p> <p>编辑界面如何将信息加载到页面</p> <p>def edit_teacher(request, nid):<br> if request.method == 'GET':<br> row = models.Teacher.object.filter(id=nid).first()<br> class_ids = row.c2t.values_list(id)<br> id_list = list(zip(<em>class_ids))[0] if list(zip(</em>class_ids)) else []<br> obj = TeacherForm(initial={'tname':row.tname,'xx':id_list})<br> return render(request,'edit_teacher.html',{'obj':obj})</p> <h5 id="checkbox">checkbox</h5> <p>一个</p> <p>cb = fields.CharField(<br> widget = widgets.CheckboxInput<br> )</p> <p>多个</p> <p>cbs = fields.MultipleChoiceField(<br> choices = [(1,'篮球'),(2,'足球'),(3,'排球')],<br> widget = widgets.CheckboxSelectMultiple<br> )</p> <h5 id="radio">radio</h5> <p>r = fields.ChoiceField(<br> choices = [(1,'篮球'),(2,'足球'),(3,'排球')],<br> widget = widgets.RadioSelect<br> )</p> <h5 id="文件上传">文件上传</h5> <p>f1.html<br> 需要在form标签增加 enctype="multipart/form-data"</p> <pre><code class="language-html"> <form method="post" action="/f1/" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="fafafa"> <input type="submit" value="提交"> </form> </code></pre> <p>普通上传</p> <p>def f1(request):<br> if request.method == "GET":<br> return render(request, 'f1.html')<br> else:<br> import os<br> file_obj = request.FILES.get('fafafa')<br> f = open(os.path.join('static', file_obj.name), 'wb')<br> for chunk in file_obj.chunks():<br> f.write(chunk)<br> f.close()<br> return render(request, 'f1.html')</p> <p>基于form实现文件上传</p> <p>class f2Form(Form):<br> fafafa = fields.FileField()</p> <p>def f2(request):<br> if request.method == "GET":<br> obj = f2Form()<br> return render(request, 'f2.html', {'obj': obj})<br> else:<br> import os<br> obj = f2Form(files=request.FILES)</p> <p>if obj.is_valid():<br> print(obj.cleaned_data.get('fafafa').name)<br> print(obj.cleaned_data.get('fafafa').size)<br> f = open(os.path.join('static', obj.cleaned_data.get('fafafa').name), 'wb')<br> for chunk in obj.cleaned_data.get('fafafa').chunks():<br> f.write(chunk)<br> f.close()</p> <p>return render(request, 'f2.html', {'obj': obj})</p> <h2 id="xss攻击">XSS攻击</h2> <p>恶意web用户将代码植入到提供给其它用户使用的页面中</p> <p>通过页面评论等功能,插入js代码,获取cookie等信息,叫做跨站脚本攻击(Cross Site Scripting)</p> <p><script>alert(123)</script><br> django默认阻止传入页面带有标签的字符串,想要浏览器不阻止传入字符串的解析需要标注safe关键字</p> <p>- 前端<br> <div>{{ msg | safe }}</div><br> - 后台<br> from django.utils.safestring import mark_safe<br> msg = "<a href='http://www.sdf.com'>sdf</a>"<br> safe_msg = mark_safe(msg)<br> return render(request,'conmment.html',{'msg':safe_msg})<br> 所以,前端不要轻易加safe</p> <p>但是,非要加safe,又想要避免被xss攻击,可以在后台接收字段时,判断是否有标签关键字</p> <p>v = request.POST.get('content')<br> if 'script' in v:<br> return render(request,"conmment.html",{'error':'输入内容存在安全隐患'})</p> <p>PS:</p> <ul> <li>慎用 safe和mark_safe</li> <li>必须得用的话,一定要过滤关键字</li> </ul> <h2 id="跨站请求伪造">跨站请求伪造</h2> <h3 id="csrf攻击">CSRF攻击</h3> <p>CSRF(Cross-site request forgery)跨站请求伪造,通过伪装来自受信任用户的请求来利用受信任的网站。</p> <p>一个网站用户Bob可能正在浏览聊天论坛,而同时另一个用户Alice也在此论坛中,并且后者刚刚发布了一个具有Bob银行链接的图片消息。设想一下,Alice编写了一个在Bob的银行站点上进行取款的form提交的链接,并将此链接作为图片src。如果Bob的银行在cookie中保存他的授权信息,并且此cookie没有过期,那么当Bob的浏览器尝试装载图片时将提交这个取款form和他的cookie,这样在没经Bob同意的情况下便授权了这次事务。</p> <p>主要利用受害者刚好也在操作银行页面,cookie没有过期,攻击者利用图片链接中的隐藏的form提交POST请求给银行,银行验证cookie没问题,会执行转账操作。</p> <p>这样通过其它网站直接向银行发送请求的操作应该被禁止,所以在用户登录银行系统时,银行会给用户一个随机字符串,在接下来的POST请求中,必须要有这个随机字符串,用户才可以正常操作,否则,银行认为操作操作违法。</p> <h3 id="启用csrf验证">启用CSRF验证</h3> <p>Diango中带有自动生成字符串的功能来避免被CSRF攻击</p> <p>- 启用CSRF验证<br> settings.py中MIDDLEWAR的'django.middleware.csrf.CsrfViewMiddleware'表示启用验证<br> - form表单中增加<br> {% csrf_token %}<br> - 页面加载后会有隐藏的input标签<br> <input type="hidden" name="csrfmiddlewaretoken" value="dguniTLGtMB3xCoq3YFuyTzBtgOaCjdYygEf5QvzTwGqQuAyetpnCdvh6o1bIqNm"><br> - 浏览器中的cookie将生成<br> <img src="media/14980919010345/14986489795952.jpg" alt="" loading="lazy"></p> <h3 id="禁用csrf验证">禁用CSRF验证</h3> <p>- 全站禁用<br> 将settings.py中'django.middleware.csrf.CsrfViewMiddleware',注释</p> <p>- 局部禁用 FBV 给函数加装饰器<br> from django.views.decorators.csrf import csrf_exempt<br> @csrf_exempt<br> def func(request):<br> ...</p> <p>- 局部使用<br> # 'django.middleware.csrf.CsrfViewMiddleware', # 注释</p> <p>from django.views.decorators.csrf import csrf_protect<br> @csrf_protect<br> def func(request):<br> ...</p> <p>- 局部使用 CBV csrf_protect不可给类中的方法加装饰器<br> from django.views import View<br> from django.views.decorators.csrf import csrf_protect<br> from django.utils.decorators import method_decorator</p> <h1 id="method_decoratorcsrf_protectname--dispatch---方法二给全部方法被装饰">@method_decorator(csrf_protect,name = 'dispatch') # 方法二:给全部方法被装饰</h1> <h1 id="method_decoratorcsrf_protectname--post---方法三制定给某个方法加装饰器">@method_decorator(csrf_protect,name = 'post') # 方法三:制定给某个方法加装饰器</h1> <p>@method_decorator(csrf_protect) # 方法一:给类加,全部方法被装饰<br> class Login(View):<br> def get(self, request):<br> return render(request, "login.html")</p> <p>def post(self, request):<br> print(request.POST.get("user"))<br> return HttpResponse("login.post")</p> <h4 id="pscbv-如何加装饰器">PS:CBV 如何加装饰器</h4> <p>Django中CBV不可直接加装饰器,需要通过method_decorator(装饰器)</p> <p>from django.views import View<br> from django.utils.decorators import method_decorator</p> <h1 id="method_decoratorauthname--dispatch---方法二给全部方法被装饰">@method_decorator(auth,name = 'dispatch') # 方法二:给全部方法被装饰</h1> <h1 id="method_decoratorauthname--fun2---方法三制定给某个方法加装饰器">@method_decorator(auth,name = 'fun2') # 方法三:制定给某个方法加装饰器</h1> <p>@method_decorator(auth) # 方法一:给类加,全部方法被装饰<br> class Login(View):<br> def func1(self, request):<br> ...</p> <h1 id="method_decoratorauth---方法四给方法加仅该方法被装饰">@method_decorator(auth) # 方法四:给方法加,仅该方法被装饰</h1> <p>def fun2(self, request):<br> ...</p> <h3 id="ajax提交数据时候携带csrf">Ajax提交数据时候,携带CSRF</h3> <h4 id="放在data中携带">放在data中携带</h4> <p>如果网站中带有csrftoken,发送ajax应该将csrftoken带进去</p> <pre><code class="language-js"><script src="/static/jquery-3.2.1.js"></script> <script> function submitForm() { {#从页面中获取csrf值#} var csrf = $('input[name="csrfmiddlewaretoken"]').val(); $.ajax({ url:"/login.html", type:"POST", data:{'csrfmiddlewaretoken':csrf}, success:function(arg){ console.log(arg) } }) } </script> </code></pre> <h4 id="放在请求头">放在请求头</h4> <p>从cookie中获取csrf值</p> <p>获取cookie的插件:jquery.cookie.js<br> 获取token:$.cookie('csrftoken')<br> 加入请求头:headers:{'X-CSRFToken':token}</p> <pre><code class="language-js"><script src="/static/jquery-3.2.1.js"></script> <script src="/static/jquery.cookie.js"></script> <script> function submitForm() { var token = $.cookie('csrftoken'); $.ajax({ url:"/login.html", type:"POST", data:{}, {#加入请求头#} headers:{'X-CSRFToken':token}, success:function(arg){ console.log(arg) } }) } </script> </code></pre> <h2 id="cookie">Cookie</h2> <p>1.保存在浏览器端的“键值对”<br> 2.服务端可以向用户浏览器端写cookie<br> 3.客户端每次请求,会携带coolie</p> <p>- 发送cookie<br> obj = redirect("/index/")<br> obj.set_cookie("ticket","1qazxsw23edc",max_age = 10)<br> return obj</p> <p>- set_cookie方法:<br> max_age = 10 //超时时间<br> path = '/' //指定url才可以后去cookie<br> domain = None //域名等级划分,多点登录,统一认证登录<br> httponly = True //只有http请求中才可用,js代码无法获取cookie<br> secure = True //只有https请求才可用,用户修改cookie没用</p> <p>- 接收cookie<br> tk = request.COOKIES.get("ticket")</p> <p>- 加签名<br> set_signed_cookie("ticket","1qazxsw23edc",salt='abc')<br> salt = '' //加盐</p> <p>- 接收cookie+解密<br> tk = request.get_signed_cookie("ticket",salt='abc')</p> <h2 id="session">Session</h2> <h3 id="原理">原理</h3> <p>cookie是保存在客户端浏览器上的键值对<br> Session是保存在服务端的数据(本质也是键值对)</p> <p>http请求特点是短链接,无状态<br> session与cookie具有同样的功能:用于服务端验证客户端信息,启到保持会话作用,<br> 并且session应用需要依赖cookie<br> session与cookie具有不同的是:session不会将敏感信息直接给客户端</p> <h3 id="django实现">django实现</h3> <p>发送session步骤:<br> 1.生成 随机字符串<br> 2.通过cookie将 随机字符串 发送给客户端<br> 3.服务端将 随机字符串 对应 客户端信息 键值对{ 随机字符串:{‘username’:‘alex’,...} }保存到某个地方</p> <p>- 语句<br> def func(request):<br> request.session['username']='alex'<br> # request.session['user_info']={'username':'alex',...} #可以写成字典,作为session随机字符串的value</p> <p>return ...<br> 获取session步骤:<br> 1.获取客户端cookie中的 随机字符串<br> 2.去session中查询有没有对应 随机字符串<br> 3.去session对应的key的value中查看是否有username</p> <p>- 语句<br> def func2(request):<br> v = request.session.get('username')<br> # v = request.session.get('user_info').get('username')<br> if v:<br> 登录成功<br> else:<br> 失败</p> <p>前端:</p> <p>- 前端直接通过request.session获取字典<br> {{ request.session.user_info.username }}<br> session其他操作:</p> <pre><code class="language-python"> 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的随机字符串 s_k = request.session.session_key # 获取随机字符串本身 # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否存在 request.session.exists(s_k) # 删除 当前用户的所有Session数据 request.session.delete(s_k) # 可以用作登出loginout # session超时 request.session.clear() # 可以用作登出loginout # 修改 session超时时间 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。 </code></pre> <p>配置文件设置session settings.py</p> <p>SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)<br> SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)<br> SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)<br> SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)<br> SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)<br> SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)<br> SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)<br> SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)只要用户重新操作页面,session超时时间就会重置,可以设置为True</p> <p>session保存设置</p> <p>session默认保存在数据库,django可以让session放在内存、缓存、数据库(默认)、文件、cookie等等<br> 想要session存放于不同位置,只需要修改配置文件settings.py</p> <p>数据库:</p> <p>SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)</p> <p>缓存:(缓存相当于另外一台服务器的内存)</p> <p>SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎<br> SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置</p> <p>文件:</p> <p>SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎<br> SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T</p> <p>缓存+数据库(优先去缓存拿,缓存没有去数据库拿)</p> <p>SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎</p> <p>加密cookie(相当于又放到coolie,没什么用)</p> <p>SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎</p> <h2 id="数据分页">数据分页</h2> <p>数据查询到后显示到页面,如何实现分页,Django自带分页功能,但是有些缺陷,所以我们自定义一个分页组件</p> <h3 id="分页核心功能">分页核心功能</h3> <p>ORM通过类似python中的切片功能实现分批获取数据</p> <p>models.UserInfo.objects.all()[0:10] # 从0开始取到9,取头不取尾<br> models.UserInfo.objects.all()[10:20] # 从10开始取到19</p> <h3 id="django自带分页">Django自带分页</h3> <p>Django自带分页适用于只有上一页,下一页的情况</p> <p>views.py</p> <pre><code class="language-py">def index(request): ''' 分页 :param request: :return: ''' current_page = request.GET.get('page') user_list = models.UserInfo.objects.all() paginator = Paginator(user_list, 10) # 用户列表,每页显示10条数据 # ---paginator方法--- # page: page对象 # per_page: 每页显示条目数量 # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) try: posts = paginator.page(current_page) # 新建page对象 # ---posts方法--- # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表 # number 当前页 # paginator paginator对象 except PageNotAnInteger as e: # 页码不是整型 posts = paginator.page(1) except EmptyPage as e: # 页码为空 posts = paginator.page(1) return render(request, 'index.html', {'posts': posts}) </code></pre> <p>index.html</p> <pre><code class="language-py"><ul> {% for i in posts.object_list %} <li>{{ i.name }}</li> {% endfor %} </ul> <div> {% if posts.has_previous %} <a href="/index.html?page={{ posts.previous_page_number }}">上一页</a> {% endif %} </div> <div> {% if posts.has_next %} <a href="/index.html?page={{ posts.next_page_number }}">下一页</a> {% endif %} </div> </code></pre> <h3 id="自定义一个分页组件">自定义一个分页组件</h3> <p>views.py</p> <pre><code class="language-py">from utils.pager import PageInfo def custom(request): total_row = models.UserInfo.objects.all().count() # 全部数据行数 page_info = PageInfo(request.GET.get('page'), total_row, 10, '/custom.html', 11) user_list = models.UserInfo.objects.all()[page_info.start():page_info.end()] return render(request, 'custom.html', {"userlist": user_list, "page_info": page_info}) </code></pre> <p>utils.pager.py</p> <pre><code class="language-py">class PageInfo(object): def __init__(self, current_page, total_row, per_page, base_url, show_page=11): ''' :param current_page: 当前页 :param total_row: 总数据行数 :param per_page: 每页显示几条数据 :param base_url: url地址 :param show_page: 显示几页 ''' try: self.current_page = int(current_page) except Exception as e: self.current_page = 1 self.per_page = per_page # 求页码数 x, y = divmod(total_row, per_page) if y: # 如果余数不为0,页码+1 x = x + 1 self.total_page = x self.show_page = show_page self.base_url = base_url def start(self): return (self.current_page - 1) * self.per_page def end(self): return self.current_page * self.per_page def pager(self): page_list = [] half_page = int((self.show_page - 1) / 2) # 1/2显示几页 # 总页数小于显示几页 <11 if self.total_page < self.show_page: begin = 1 stop = self.total_page + 1 # 总页数大于显示几页 >11 else: # 当前页小于一半 if self.current_page <= half_page: begin = 1 stop = self.show_page + 1 # 当前页大于一半 else: # 当前页+一半大于总页码 if self.current_page + half_page > self.total_page: begin = self.total_page - self.show_page + 1 stop = self.total_page + 1 # 当前页+一半小于总页码 else: begin = self.current_page - half_page stop = self.current_page + half_page + 1 if self.current_page <= 1: prev = '<li><a href="#">上一页</a></li>' else: prev = '<li><a href="%s?page=%s">上一页</a></li>' % (self.base_url, self.current_page - 1,) page_list.append(prev) for i in range(begin, stop): if i == self.current_page: temp = '<li class="active"><a href="%s?page=%s">%s</a></li>' % (self.base_url, i, i) else: temp = '<li><a href="%s?page=%s">%s</a></li>' % (self.base_url, i, i) page_list.append(temp) if self.current_page >= self.total_page: nex = '<li><a href="#">下</a></li>' else: nex = '<li><a href="%s?page=%s">下一页</a></li>' % (self.base_url, self.current_page + 1,) page_list.append(nex) return ''.join(page_list) </code></pre> <p>custom.html</p> <pre><code class="language-html"><ul> {% for i in userlist %} <li>{{ i.name }}</li> {% endfor %} </ul> <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_info.pager|safe }} {# html调用函数不需要加括号pager(),浏览器信任插入字符串safe#} </ul> </nav> </code></pre> <h2 id="缓存">缓存</h2> <h2 id="序列化">序列化</h2> <h2 id="信号">信号</h2> <hr class="footnotes-sep"> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p>0-9 <a href="#fnref1" class="footnote-backref">↩︎</a></p> </li> </ol> </section>

posted @ 2017-07-03 23:02  六神酱  阅读(2572)  评论(2)    收藏  举报