Django知识点总结

Django知识点总结

web架构简介

主流python web框架

Django、Torando、Flask、Bottle

MVC和MTV

model(模型、数据库)、views(html模版)、controllers(业务逻辑处理) --> MVC

model(模型、数据库)、templates(html模版)、view(业务逻辑处理) --> MTV

程序的三层架构

数据处理层

- User表的增,删,改,查

业务处理层

- 登录
- 首页
…

UI层(交互界面)

- web端
- 客户端

Django项目目录结构

/Projname
	__init__.py
	settings.py # 用户级的配置文件
	urls.py		# 路由配置,为用户输入的url匹配对应的视图函数
/app01			# app文件
    /migration/	# 数据迁移记录
    __init__.py
    admin.py	# Django自带的后台管理功能
    apps.py		# app注册配置文件
    models.py   # 模型
    test.py		# 单元测试	
    view.py		# 业务处理,循环函数  # 可将view变换成目录,并分块
/templates		# 模板目录(html)
/static			# 静态文件目录(自定)
manage.py		# 程序启动文件

# 以下可选
/repository		# 可将主程序的Models提取到此处
/api			# 专门的api处理
/backend		# 用于用户的后台管理

Django配置入门

开始

安装
sudo pip3 install django
# 或 
pip3 install django==2.0.1   # 指定版本 
创建Django项目(Project)
django-admin startproject projectname
进入程序目录
cd projectname
启动服务器,等待用户发送请求
python manage.py runserver 127.0.0.1:8080

模版及静态文件路径配置

——在settings.py文件下操作

模版路径配置
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]   # 此处为设置模版搜索路径
        # 'templates'名称要与模版文件所在的目录一致
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
静态文件路径配置
STATIC_URL = '/static/'     			# 路径前缀,程序将在根目录的此路径下搜索静态文件
STATICFILES_DIRS =(  					# 新增此变量配置
	os.path.join(BASE_DIR, 'static'),   # 一定要加逗号,'static'与 STATIC_URL保持一致
)

模板文件即可使用该目录下的静态文件

<link rel="stylesheet" href="/static/css-filename.css">

创建app

终端创建

cd到项目目录下

python3 manage.py startapp app01     # 可有多个,用于主站、后台管理、运维平台等等
通过pycharm创建

在创建Project的时候输入app名

高级配置文件

——适用于非浏览器的的客户端

参考django内部的配置文件,制作支持用户配置及默认配置文件
django.conf import global_settings  # 默认配置文件
django.conf import settings			# 用户配置文件

在客户端的start.py文件下

import os
os.environ['USER_SETTINGS'] = "config.settings"  # 执行时自动将用户配置文件设为环境变量

# config.py文件下
import os
from . import global_settings

class Settings(object):
    def __init__(self):
        # 找到默认配置,先导入,如有重复以用户为准
        for name in dir(global_settings):
            if name.isupper():
                value = getattr(global_settings, name)
                setattr(self, name, value)
                # 找到用户自定义配置
                settings_module = os.environ['USER_SETTINGS']
                if not settings_module:
                    return
                m = importlib.import_module(settings_module)
                for name in dir(m):
                    if name.isupper():
                        value = getattr(m, name)
                        setattr(self, name, value)

小技巧:

#测试模式配置
DEBUG=True

# 函数中
DEBUG = settings.DEBUG
if DEBUG:
	# 启动测试模式专用程序	
# except显示错误堆栈信息
try:
	...
except Exception:
	print(traceback.format_exc())

程序启动方法的设置(不用manage.py启动)

在启动文件中输入以下代码,达到类似manage.py启动的效果

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名.settings")  # 设置环境变量
import django
django.setup()   # 手动注册django所有的app

新建Django文件总结:

  1. 创建project
  2. 创建APP
  3. 创建数据库
  4. 静态文件路径配置
  5. urls.py新建url模式指向视图函数。
  6. view.py创建创建视图函数返回模板。
  7. (如有)创建form表单约束规则。
  8. 新建前端模版。
  9. 重复5、6、7、8步

Views——业务逻辑处理

FBV和CBV

指的是使用函数来作为业务逻辑处理的方式,CBV是指url对应类;FBV指url对应函数,FBV更为灵活。

FBV

urlpatterns = [
	url(r'^index/', index),  # FBV:url对应函数
]

CBV

urls.py

urlpatterns = [
	url(r'^login/', view.Login.as_view()),  # CBV:url对应类,自动处理GET和POST等请求
]

view.py

from django.shortcuts import View

# 通过反射作用实现
class Login(View):     # 继承View类
	def get(self, request):      				# 定义get请求时的函数
        return render(request, 'login.html')
    def post(self, request):    
        return render(request, 'login.html')

方法命名应遵循REST API规则

URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。

GET查找、POST创建、PUT更新、DELETE删除

其余PATCH、HEAD、OPTIONS、TRACE

Restful API

面向资源编程:把网络上的任何东西当作资源

1.method:方法
# GET
# POST
# PUT
# DELETE

2.状态码
# 200
# 404
# 500
# 403

3.url必须是名词

参考资料: http://www.ruanyifeng.com/blog/2014/05/restful_api.html

小贴士

重写源码中的dispatch功能
class Login(View):
	def get(self, request):      				# 定义get请求时的函数
        return render(request, 'login.html')
    def post(self, request):    
        return render(request, 'login.html')
	def dispatch(self, request, *args, **kwargs):
        obj = super(Login, self).dispatch(request, *args, **kwargs) # 调用View中的dispatch 方法再进行改写
        '''
        可在此加入改写代码
        '''
        return obj

cookie控制

什么是cookie?

  1. 保存在浏览器上的键值对
  2. 服务端可以向用户浏览器端写cookie
  3. 客户端每次发请求时会携带cookie去

应用于投票、登录等

使用方法

def login(request)
    obj = HttpResponse('OK')

    # 设置cookie超时 
    # 方式1 设置时间(推荐)
    obj.set_cookie(
        'key',				# 第一个参数为key
        value='value',   	# 第二个参数为value
        max_age=100   		# 超时时间为100秒
    )  
	
    # 方式2 设置日期   
    import datetime
    from datetime import timedelta
    ct = datetime.datetime.utcnow()
    v = timedelta(seconds=10)   # 计算10秒以后的日期
    value = ct + v  		
    obj.set_cookie(
        'key',
        value='value',
        expires=value		# 设置最长超时日期
    )   

    return obj

	# 其他设置:url、域名等
    obj.set_cookie(
        'key',
        value='value',
        path='/url/'		# 设置可用的url
        domain=None,		# 设置可用域名,默认为当前域名
        secure=False,		# 证书:为https提供的功能,使用https需要改成True
        httponly=False		# 只能通过http请求中进行传输,js代码无法获取
    )

HttpResponse、render、redirect均可使用

获得cookie
request.COOKIES
request.get_signed_cookie('k1', salt='jjjj')

cookie签名

tk = obj.set_signed_cookie('ticket','123123sda',salt='jjjj')
创建自己的签名规则
  1. 在创建新模块c1,自己写一个TimestampSigner
from django.core.signing import TimestampSigner
class Mysigner(TimestampSigner):
    def sign(self, value):
        # 此处可自行加密
    	return value + '12345678'
	def unsign(self, value, max_age=None):
        print(value)
        return value[0:-8]
  1. settings.py文件中将配置改成
SIGNING_BACKEND = "c1.MySigner"

小贴士

用户登录状态的cookie应用——使用装饰器对函数进行修改

Session控制

session与cookie

cookie是保存在客户端浏览器上的键值对

Session即保存在服务器端的数据(本质是键值对)

应用:依赖cookie

作用:保持会话(web网站)

session一般使用过程

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        u = request.POST.get('user')
        p = request.POST.get('pwd')
		obj = models.UserAdmin.objects.filter(username=u, password=p).first()
        if obj:
            # 1.生成随机字符串
            # 2.通过cookie发送给客户端
            # 3.服务端保存(django_session表里面)
            # {
            #    随机字符串1:{'username': 'alex', 'email': '11',...}
            # }
			request.session['username'] = obj.username
            return redirect('/index/')
        else:
            return render(request, 'login.html', {'msg': "用户名或密码错误"})
        
        
def index(request):
    # 1.获取客户端cookie中的随机字符串
    # 2.去session中查找有没有随机字符串
    # 3.去session对应的key的value中查看是否有username
    v = request.session.get('username')
    if v:
        return HttpResponse("登录成功")
    else:
        return redirect('/login/')

session相关操作

def index(request):
    # 获取、设置、删除Session中数据
    request.session['k1']
    request.session.get('k1',None)       # 没有k1,则创建默认给None
    request.session['k1'] = 123
    request.session.setdefault('k1',123) # 存在则不设置
    del request.session['k1']			 # 删除值,不删除对

    # 所有 键、值、键值对
    request.session.keys()
    request.session.values()
    request.session.items()
    request.session.iterkeys()        # 以迭代器的方式,一个一个取
    request.session.itervalues()
    request.session.iteritems()


    # 用户session的随机字符串
    request.session.session_key

    # 将所有Session失效日期小于当前日期的数据删除
    request.session.clear_expired()

    # 检查 用户session的随机字符串 在数据库中是否
    request.session.exists("session_key")

    # 删除当前用户的所有Session数据
    request.session.delete("session_key")

    request.session.set_expiry(value)
    #* 如果value是个整数,session会在些秒数后失效。
    #* 如果value是个datatime或timedelta,session就会在这个时间后失效。
    #* 如果value是0,用户关闭浏览器session就会失效。
    #* 如果value是None,session会依赖全局session失效策略。

修改session默认配置

Django原本已有一套默认配置,如需自定义,须在settings.py下增加以下变量

数据保存配置
# 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)


# 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 使用文件保存
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()     


# 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' 
# 将session数据放到另外一台机子的服务器内存上。
SESSION_CASHE_ALIAS = 'default'    # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
# 需配合Django缓存配置


# 缓存+数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
# 数据库用于做持久化,缓存用于提高效率

# 存于cookie中
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
其他相关配置
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,默认修改之后才保存(默认)

路由系统

路由关系配置

——编辑url并定义处理函数

主目录urls.py文件下

urlpatterns = [
    # url(r'^admin/', admin.site.urls),  # 在此列表新增url,把admin替换成url后缀
    url(r'^login/', view.login),   		 # 例:替换成r'^login/'和login函数
	
    # 终止符
    url(r'^index/', view.index),    # r'^index/'为正则表达式,以^开头,$或/表示结束
    url(r'^index/(\d+)/', view.index),  # 使用正则表达式来匹配url,匹配出的组作为参数传到views
    # FBV接收 --> def index(request,a1)
    
    # 位置参数式
    url(
        r'^index/(\w+)/(\w+)', # 两个及以上正则表达式
        # FBV接收 --> def index(request,a1,a2)  按顺序接收
        view.index
    ),  
    
    # 指定参数式
    url(
        r'^index/(?P<a1>\w+)/(?P<a2>\w+)', # 两个及以上正则表达式
        # FBV接收 --> def index(request,a1,a2)  指定参数名称为a1,a2,并接收
        view.index
    ),
    
    # 混合式
    没有混合式,必须统一,要么位置参数。
]

正则补充:

\w 匹配字母或数字或下划线或汉字 等价于 '\[^A-Za-z0-9_]\'

伪静态

静态的好处是速度快(搜索引擎更偏爱)

动态因为要到数据库查找数据所以慢

伪静态是为了做SEO,让搜索引擎觉得你是个静态网站

url(r'^index/(\w+).html$',index),      # 加上.html结尾,伪装为静态网站  

路由分发

使用include函数分发
  • 当用户浏览器输入127.0.0.1/app01/index.html
  • 主目录下匹配前面的部分
url(r'^app01/',include('app01.urls')),  # 将url分发到app01执行
  • 转交app01匹配剩余的部分
url(r'^index.html',view.index),  # 在app01下匹配index函数
错误url处理
url(r'^',view.index),  # 将错误的url给到首页 

循环函数别名反向生成url

路由配置
# 位置参数版
url(r'^index.html/(\d+)/',view.index, name='n1'),  # 为url命名

# 命名参数版
url(r'^index.html/(?P<a1>\d+)/',view.index, name='n2'),  # 为url命名
函数获取url
import reverse

# 位置参数版
v = reverse('n1', args=(1,))  			# args为返回url:'/index/a' 中的a

# 命名参数版
v = reverse('n2', kwargs={'a1: 111'})  	# a1为命名参数指定名称的参数
配合特殊标记的用法
<form method='POST' action='{% url "n1" %}' />    // 模版特殊标记通过名称反生成url 
	
数据替换结果为
<form method='POST' action='/index/1/' />    // 模版特殊标记通过名称反生成url 

可在标记中加入参数
<ul>
  {% for i in user_list %}
      <li> {{ i }} | <a herf="/edit/{{ i }}/">编辑</a></li>  
  	  // 下面语句与上面效果一样
      <li> {{ i }} | <a herf="{% url 'n1' i %}">编辑</a></li>
  {% endfor %}
</ul>

权限管理中,保存别名,然后反生成菜单的url作为action的值

注:别的框架不支持别名

UI层——模版语言

路由关系配置

路由与视图函数的连接

views.py部分

import HttpResponse,render

def login(request):  				    # 定义处理url的函数
	return HttpResponse('<input type="text" />')   # 利用HttpResponse返回字符串

def index(request):
    return render(request,"index.html")  #直接读取html文件,读取内容并返回字符串
	# 注:请提前做好模版文件路径配置

render(request, 模版路径, dict)

HttpResponse(request, str(or dict))

redirect("网址(or 新url)")

urls.py部分

urlpatterns = [
    # url(r'^admin/', admin.site.urls),  # 在此列表新增url,把admin替换成url后缀
    url(r'^login/', views.login),    # 例:替换成r'^login/'和login函数
    url(r'^index/', views.index),
]
request方法简介

用get()、getlist()(用于多选下拉框)方法可以拿到用户请求的相关信息

GET —— 只有request.GET有值

POST——两者都有值

method——请求的方法

FILES——请求中附带的文件

更多方法:https://www.cnblogs.com/scolia/archive/2016/07/01/5633351.html

视图函数与模板文件的连接

模版html文件下

<form method="post" action="/login/"> 	{# method作为提交的方法,value作为提交的值 #} 
    <input type="text" name="username">  {# name作为提交的键,value作为提交的值 #} 
    <input type="password" name="password">
    <input type="submit" value="登录">
  	{{ msg }}     {# 特殊字符 放提示语的地方 #}
</form>

views.py

def login(request):
    if request.method == 'get':
        return render(request, 'login.html') # 判断客户端使用的方法及对其进行处理
    else:
        # request.GET或request.POST 储存用户发过来的值
        u = request.POST.get('user')     # 用户POST提交的username
        u = request.POST.get('pwd')       # 用户POST提交的password
        if u == 'root' and p == '123':
            return redirect('http://www.xxx.com')
        else:
            # 用户名密码错误时,动态显示 提示信息
            return render(request, 'login.html', {'msg':'用户名或密码错误'})

模版渲染

特殊标记返回值不一定是字符串,可以为列表或字典

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': 'alex23714@163.com'},
            {'id':3, 'name':'alex3', 'email': 'alex33714@163.com'},
        ]
    } 
)

普通取值

<p>{{ name }}</p>

<p>{{ users.0 }}</p>    # 获取列表中第一个元素
<p>{{ users.1 }}</p>

<p>{{ user_dict.k1 }}</p>  # 获取字典中key为k1的value值
<p>{{ user_dict.k2 }}</p>

循环取值

<ul>
  {% for item in users %}   # 循环开始
  	<li>{{ item }}</li>
  {% end for %}				# 循环结束
</ul>

<table>
  {% for row in users %}   # 循环开始
  	<tr>
      <td>{{ row.id }}</td>
      <td>{{ row.name }}</td>
      <td>{{ row.email }}</td>
      <td>
      	<a>编辑</a><a herf="/del"={{ row.id }}></a>
      </td>
  	</tr>
  {% end for %}
</table>

{# 字典循环 #}
{% for k, v in userinfo.items %}
	<h6>{{ k }}-{{ v }}</h6>
{% endfor %}

母版继承

母版:存放所有页面公用部分

子版:继承母版公用部分及定制私有部分

{% extends 'layout.html' %}     {# 表示继承母版 #}

{% block html %}                  {# 母版中应有此block html标记 #}
	<div>...</div>              {# 在母版中有此block标记的地方插入以下代码 #}
{% endblock %}


{# block的其他用途 #}
{% block css %}               
	<style></style>				{# 导入自己专用的css #}
{% endblock %}

{% block js %}				  
	<script></script>			{# 导入自己专用的js #}
{% endblock %}
用include导入小组件

创建小组件如pub.html

{# 注意:删除其他标签,只剩下组件部分 #}
<div>
  <h3>特别漂亮的小组件</h3>
  <div class="title">标题:{{ name }}</div>
  <div class="content">内容:{{ name }}</div>
</div>

程序会先导入所有组件、母版后再开始渲染,因此组件内的特殊标记也会被渲染成功

在需要用到小组件的地方导入

{% include "pub.html"%}

自定义simple_tag

simple_tag是指下方的upper这种有函数作用的标签

{{ name|upper }}

创建simple_tag有以下步骤:

a、在app中创建templatetags模块

b、创建任意 .py 文件,如:xx.py

from django import template
from django.utils.safestring import mark_safe
   
register = template.Library()

@register.filter
def my_upper(value, args)         # fillter 最多只能传2个参数,可传入列表后逐个提取 
	return value.upper() + args

@register.simple_tag
def my_simple_time(v1,v2,v3):
    return  v1 + v2 + v3

@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)

c、在使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名

{% load xx %}

d、使用simple_tag

{% name|my_upper:"1,2,3" %}     {# @register.filter可作为条件语句进行判断使用。注意冒号后不可有空格 #}
{% if name|my_bool %}			
	<h3>真</h3>
{% else %}
	<h3>假</h3>
{% endif %}

{% my_simple_time 1 2 3 %}		{# @register.simple_tag不可用于条件语句 #}
{% my_input 'id_username' 'hide' %}

e、在settings中配置当前app,不然django无法找到自定义的simple_tag

Ajax应用

Ajax是前端与后台数据交互的技术,即偷偷地向后台发请求,可用于模态对话框(自制的弹框)。

另外一种类似的方式是'新url',就是跳转到新的url,返回新的页面 ,用于处理较大量的数据。

使用form表单提交,页面会刷新,Ajax提交不刷新

使用jquery中的ajax方法
$.ajax({
  url: '要提交的地址',
  type: 'post', // GET或POST,提交方式
  data: {'k1': 'v1', 'k2': 'v2'},  // 提交的数据的值,支持列表,不支持字典,只能通过序列化
  traditional: true,    // 如果提交的数据的值有列表则需要添加此属性
  dataType: 'JSON'  // 返回的数据反序列化
  success: function (data) {
  				// 当前服务端处理完毕后,自动执行的回调函数
  				// data为返回的数据
  				location.herf = 'www.baidu.com'
			}
})
原生Ajax——XMLHttpRequest
function add2(){
  var xhr = new XMLHttpRequest();
  var onreadystatechange = function(){
    if (xhr.readyState == 4)			// 判断准备状态是否在已经获得相应
      alert(xhr.responseText);       	// 响应的结果数据
  };
  
  // GET请求
  xhr.open('GET', '/add2/?i1=12&i2=19') // 注明请求方法及需要打开的url 
  xhr.send();						  	// 发送请求
  
  // POST请求
  xhr.open('POST', '/add2/');		    // 使用POST方法,将数据值藏在请求体内
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');          # 设置请求头告知处理请求体
  xhr.send('i1=12&i2=19');
}
视图函数部分
def add2():
	if request.method == "GET":
        i1 = int(request.GET.get('i1'))
        i2 = int(request.GET.get('i2'))
        return HttpResponse(i1 + i2)
XMLHttpRequest的主要方法
a. void open(String method,String url,Boolen async)
   用于创建请求
    
   参数:
       method: 请求方式(字符串类型),如:POST、GET、DELETE...
       url:    要请求的地址(字符串类型)
       async:  是否异步(布尔类型)
 
b. void send(String body)
    用于发送请求
 
    参数:
        body: 要发送的数据(字符串类型)
 
c. void setRequestHeader(String header,String value)
    用于设置请求头
 
    参数:
        header: 请求头的key(字符串类型)
        vlaue:  请求头的value(字符串类型)
 
d. String getAllResponseHeaders()
    获取所有响应头
 
    返回值:
        响应头数据(字符串类型)
 
e. String getResponseHeader(String header)
    获取响应头中指定header的值
 
    参数:
        header: 响应头的key(字符串类型)
 
    返回值:
        响应头中指定的header对应的值
 
f. void abort()
 
    终止请求
XMLHttpRequest的主要属性
a. Number readyState
   状态值(整数)
 
   详细:
      0-未初始化,尚未调用open()方法;
      1-启动,调用了open()方法,未调用send()方法;
      2-发送,已经调用了send()方法,未接收到响应;
      3-接收,已经接收到部分响应数据;
      4-完成,已经接收到全部响应数据;
 
b. Function onreadystatechange
   当readyState的值改变时自动触发执行其对应的函数(回调函数)
 
c. String responseText
   服务器返回的数据(字符串类型)
 
d. XmlDocument responseXML
   服务器返回的数据(Xml对象)
 
e. Number states
   状态码(整数),如:200、404...
 
f. String statesText
   状态文本(字符串),如:OK、NotFound...
jQuery Ajax——内部基于"原生Ajax",不生产Ajax,它只是Ajax的搬运工:
$.ajax({
  ...
})
伪Ajax,非XMLHttpRequest,另外一种技术iframe
iframe具有不刷新整个页面发送http请求的特性
<form method="POST" action="/fake_ajax/" target="ifr">
  <iframe name="ifr" id='ifr' style='display: none'></iframe>
  <input type="text" name="user" />
  <a onclick="submitForm();">提交</a>    # 绑定提交表格的函数
</form>

<script>
  function submitForm(){
    document.getElementById('ifr').onload = loadIframe;               # 提交表格时执行loadIframe函数
    document.getElementById('f1').submit();
  }
  function loadIframe(){
  var content = document.getElementById('f1').contentWindow.document.body.innerText;
    alert(content);
  }
</script>
视图函数部分
def fake_ajax(request):
    if request.method == 'GET':
        return render(request, 'fake_ajax.html')
    else:
        print(request.POST)
        return HttpResponse("返回值")

基于Ajax上传文件

<h1>原生Ajax上传文件</h1>
<input type='file' id='i1'/>
<a onclick="upload1()" id='i1'>上传</a>
<div id='container1'></div>                 {# 预览功能 #}

<h1>jQuery.Ajax上传文件</h1>
<input type='file' id='i2'/>
<a onclick="upload2()" id='i2'>上传</a>
<div id='container2'></div>                 

<h1>伪Ajax上传文件</h1>
<form method="POST" action="/upload/" target="ifr" enctype="mutipart/form-data">
  <iframe name="ifr" id='ifr' style='display: none'></iframe>
  <input type="file" name="fafafa" />
  <a onclick="upload3();">上传</a>
</form>


<script src="/static/jquery-1.12.4.js"></script>
<script>  
  function upload1(){			// 原生Ajax上传文件
    var formData = new FormData();      # 重要,添加文件的重要载体
    formData.append('fafafa', document.getElementById('i1'.files[0]));
    
	var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
      if(xhr.readyState == 4){
        var file_path = xhr.responseText;
        var tag = document.createElement('img');
        tag.src = '/' + file_path;
        document.getElementById('container1').appendChild(tag); // 将上传的图片即时展现
      }               
    }
    xhr.open('POST', '/upload/');
    xhr.send(formdata);                # 非字符串,无需再发送请求头

  function upload2(){			// jQuery.Ajax上传文件
    var formData = new FormData();
    formData.append('fafafa', $('i2')[0].files[0]));  // jQuery 转换
    $.ajax({
      url: '/upload/',
      tyoe: 'POST',
      data: formData,
      contentType:false,		// 告知jQuery不用处理数据(设置请求头)
      processData:false,		// 告知jQuery不用处理数据(设置请求头)
      success: function(arg){
        var tag = document.createElement('img');
        tag.src = '/' + arg;
        $('container2').append(tag); // 将上传的图片即时展现
      }
    })
  }
    
    
  function upload3(){			// 伪Ajax上传文件
    document.getElementById('ifr').onload = loadIframe;  # 提交表格时执行loadIframe函数
    document.getElementById('f1').submit();
  }
  function loadIframe(){
  	var content = document.getElementById('f1').contentWindow.document.body.innerText;
    var tag = document.createElement('img');
    tag.src = '/' + content;
    document.getElementById('container3').appendChild(tag); // 将上传的图片即时展现
  }
</script>
小tips——jQuery对象和DOM对象的互换
$('#i2')  -->  $('#i2')[0]            // jQuery转DOM
document.getElementById('i1')  -->  $(document.getElementById('i1'))    // DOM转jQuery
视图函数部分
def upload(request):
    if request.method == 'GET':
        return render(request, 'upload.html')
    else:
        print(request.POST, request.FILES)
        file_obj = request.FILES.get("fafafa")
        file_path = os.path.join("static", file_obj.name)
        with open(file_path, 'wb') as f:
            for chunk in file_obj.chunks():
                f.write(chunk)
        return HttpResponse(file_path)

其他

跨域Ajax:JASONP技术是一种解决跨域问题的方式

问题描述:浏览器的同源策略不支持跨域发送Ajax请求(Ajax发送跨域请求时,再回来时浏览器拒绝接受)

突破口1:script标签没有被禁止跨域。

局限性:只能用GET请求,服务端和前端必须约定好

<a onclick="getUsers();">发送</a>
<ul id='usernames'></ul>

<script>
  function getUsers(){
    var tag = document.createElement('script');
    // tag.src = "http://www.jxntv.cn/data/jmd-jxtv2/html?callback=list&_1454376870403"
    tag.src = "http://www.s4.com:8001/users/?callback=bbb"
    document.head.appendChild(tag);
  }
  function bbb(arg){
    for (var i=0, i<arg.length, i++){
      var tag_li = document.createElement('li');
      tag_li.innerText = arg[i];
      document.getElementByID("usernames").appendChild(tag_li)
    }
  }
</script>

// jQuery版
<script>
  function getUsers(){
    $.ajax(
      url: 'http://www.s4.com:8001/user/',
      // url将拼接为'http://www.s4.com:8001/user/?callback=bbb'
      type: 'GET',
      dataType: 'JSONP',
      jsonp: 'callback',
      jsonpCallback: 'bbb'
    )
  }
  function bbb(arg){
    for (var i=0, i<arg.length, i++){
      var tag_li = document.createElement('li');
      tag_li.innerText = arg[i];
      document.getElementByID("usernames").appendChild(tag_li)
    }
  }
</script>

视图函数部分

def users(request):
    v = request.GET.get('callback')
    user_list = [
        'alex', 'eric', 'egon'
    ]
    user_list_str = json.dumps(user_list)
    temp = "%s(%s)" % (v, user_list_str,)
    return HttpResponse(temp)
小tips1——修改Hosts可以将本地IP指向不同的域名
127.0.0.1 www.s4.com
小tips2——允许域名设置

settings.py加入以下配置即可使用新加入的域名

ALLOWED_HOSTS = ['http://www.s4.com',]

突破口2:CORS跨站资源共享。修改视图函数配置,修改响应头。

局限性:但需要服务器方开放共享资源。

简单请求
def new_users(request):
    user_list = [
        'alex', 'eric', 'egon'
    ]
    user_list_str = json.dumps(user_list)
	obj = HttpResponse(user_list_str)
    obj['Access-Control-Allow-Origin'] = 'http://www.s5.com:8000'  # 加入允许访问的域名
    obj['Access-Control-Allow-Origin'] = '*'   # 即所有人均可访问
    return obj
复杂请求

简单请求(仅发送一次请求):HEADGETPOST

复杂请求(发送两次请求,一次为预检请求):OPTIONS

def new_users(request):
    if request.method == "OPTIONS":
        print('预检...')
        obj = HttpResponse()
        obj['Access-Control-Allow-Origin'] = "*"
        obj['Access-Control-Request-Methods'] = 'DELETE'
        return obj
    user_list = [
        'alex', 'eric', 'egon'
    ]
    user_list_str = json.dumps(user_list)
	obj = HttpResponse(user_list_str)
    obj['Access-Control-Allow-Origin'] = '*'   # 即所有人均可访问
    return obj

其他突破口:通过服务端发送请求再返回给浏览器

总结:
  1. 优先使用jQuery,不允许的情况下使用XMLHttpRequest或伪Ajax
  2. 保证兼容性使用伪Ajax
  3. 上传文件使用伪Ajax

参考资料:

http://www.cnblogs.com/wupeiqi/articles/5703697.html

ajax用POST方法传字典时收不到数据
在ajax中加入参数: contentType:"application/x-www-form-urlencoded",

Models部分——ORM操作

参考资料

http://www.cnblogs.com/wupeiqi/articles/5246483.html

http://www.cnblogs.com/wupeiqi/articles/6216618.html

在Django中使用mysql数据库

Django默认使用sqlite,因此要修改成mysql

  1. 到项目下settings.py修改
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  	# 修改为mysql
        'NAME': 'DjangoTEST',           		# 修改数据库名
        'USER': 'root',					
        'PASSWORD':'123',
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
}
  1. __init__.py文件下加入
import pymysql
pymysql.install_as_MySQLdb()
  1. settings.py下注册app
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',                # here
]
  1. 导入Django自带表:
python3 manage.py migrate			# 将上述APP的数据导入到数据库
连接其他数据库的方法

settings.py下default为默认connection,可加入多个数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  	# 修改为mysql
        'NAME': 'djangodb',           		# 修改数据库名
        'USER': 'root',
        'PASSWORD':'123',
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
    'db2': {
        'ENGINE': 'django.db.backends.sqlite3',  # 增加一个sqlite
        'NAME': 'djangodb2',           		# 修改数据库名
    }
}
获得cursor
from django.db import connection, connections

cursor = connection.cursor() # connection=default数据
cursor = connections['db2'].cursor()
cursor.execute("select * ……")
cursor与pymysql用法一致

用pymysql写属于自己的sqlhelper模块(models)

web请求生命周期

  1. 用户输入url
  2. 客户端发送请求头(post方法有请求体)
  3. 服务端接收并提取请求内容
  4. 交由路由关系匹配
  5. 函数进行模版数据渲染
  6. 返回到用户(响应头+响应体)

自定义的sqlhelper

import pymysql

class SqlHelper:
    def __init__(self):
        self.connect()
        
    def connect(self, sql, args)
    	self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='mysite',charset='utf-8')
    	self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
    
    def modify(self, sql, args):
        self.cursor.execute(sql, args)
        self.conn.commit()

    def multiple_modify(self, sql)
    	self.cursor.executemany(sql, args)
        self.conn.commit()
    
    def getlist(self, sql, args):
        self.cursor.execute(sql, args)
        result = self.cursor.fetchall()
        self.conn.commit()
        return result

    def getone(self, sql, args):
        self.cursor.execute(sql, args)
        result = self.cursor.fetchone()
       	self.conn.commit()
        return result

    def create(self, sql, args):
        self.cursor.execute(sql, args)
        self.conn.commit()
        return self.cursor.lastrowid
    
    def close(self):
        self.cursor.close()
        self.conn.close()

ORM相关操作

操作表

创建表

model.py文件下创建表

class UserInfo(models.Model):
    nid = models.BigAutoField(primary_key=True)  # 自增字段 可以不写,Django会自动生成id = models.AutoField()
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

创建数据表命令:

python3 manage.py makemigrations		# 根据model里面的规则创建表
python3 manage.py migrate
修改表

model.py中修改

class UserInfo(models.Model):
    id = models.BigAutoField(primary_key=True)  # 自增ID
    # id = models.AutoField()
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField()    # 增加此字段

再次执行命令即可(改字段名同理):

python3 manage.py makemigrations		# 根据model里面的规则创建表
python3 manage.py migrate

每一次修改会在migrations中记录每次修改进行比对,所以别删里面的文件

出现以下问题是因为默认新增字段不能为空

You are trying to add a non-nullable field 'age' to userinfo without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py

Select an option: 1

Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>>

作以下修改即可

age = models.IntegerField(null=True)    # 修改为可以为空
# 或
age = models.IntegerField(default=1)    # 修改默认值为1

如发现表没有创建成功请再次执行(必须两条均执行)

python3 manage.py makemigrations
python3 manage.py migrate
外键
class UserGroup(models.Model):
    title = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField()
    ug = models.ForeignKey('UserGroup', null=True)  
    # 新增外键,没有数据时要设为可以为空
    # 生成的字段名默认生成为ug_id 
    ur = models.ForeignKey('self', null=True, blank=True) #  ForeignKey的自关联: 引用自己的ID
字段类型设定
AutoField(Field)
- int自增列,必须填入参数 primary_key=True

BigAutoField(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 ~ 32767

            PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正小整数 0 ~ 32767
            IntegerField(Field)
            - 整数列(有符号的) -2147483648 ~ 2147483647

            PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正整数 0 ~ 2147483647

            BigIntegerField(IntegerField):
                - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

                自定义无符号整数字段

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)',

BooleanField(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中提供验证 URL

SlugField(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.FileSystemStorage

ImageField(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-DD

TimeField(DateTimeCheckMixin, Field)
- 时间格式      HH:MM[:ss[.uuuuuu]]

DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
- 浮点型

DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度

BinaryField(Field)
- 二进制类型
字段参数设定
# 单个字段
null=True
default='111'
db_index=True
unique=True

# 多个组合
class Meta:
    unique_together(
        (u,g),
    )
    index_together(
        (u,g),        
    )

# ADMIN参数
blank=True   # 可以为空
verbose_name='用户名'  # 修改字段名
editable=False    # 不可编辑,将被隐藏
help_text='这是提示信息'   # 输入提示信息
choice=[(0, 阿斯顿),(1, 地方)],   # 通常跟个default注明默认值
error_messages  # 自定义错误信息,如 {'null': 不能为空, 'invaild':'格式错误',……} 字典键:null, blank, invaild, invaild_choice, unique, unique for date
validators         # 使用正则表达式自定义错误验证(列表类型),从而定制想要的验证规则
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'), 
	]
)
枚举字段
color_list = (
	(1, "黑色"),
  	(2, "白色"),
  	(3, "蓝色"),
)

color = models.InterField(choice=color_list)  # 使用choice参数

如果变量固定不变,使用此方法。选项动态时使用ForeignKey

删除表

删除类后执行命令直接删除

操作数据行
def index(request):
    from app01 import models
	# 增加
    models.UserGroup.objects.create(title='销售部')  # 输入一个数据
    models.UserInfo.objects.create(user='root', password='123', age='18', ug_id=1)
	
    # 查找
    group_list = models.UserGroup.objects.all() # 获得对象(row)列表
    group_list = models.UserGroup.objects.first() # 获得第一个
    
    group_list = models.UserGroup.objects.filter(id=1, title='销售部')  # AND关系
    # 神奇的双下划线
    
    group_list = models.UserGroup.objects.filter(id__gt=1)  
    # id_gt  大于
    # id_lt	 小于

    # 删除
    group_list = models.UserGroup.objects.
    filter(id__gt=1).delete()  #

    # 更新
    group_list = models.UserGroup.objects.filter(id__gt=1).update(title='公关部')     
    # 字段为空    
    group_list = models.UserGroup.objects.filter(age__isnull=True).update(title='公关部') 
    
    return  render(request, 'newindex.html', {"group_list": group_list})
基本操作一览
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询 —— 过滤
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询 —— 排除
    # 条件可以是:参数,字典,Q

def 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) > 1

def 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')
替代方法:
models.UserInfo.objects.values('username','id') # 区别为字典or对象
        
 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):
   # 创建对象
	# 建议使用**dic的方式去传参数

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)
	# 可使用for循环批量生成对象,再通过bulk_create一次过导入数据库。
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):
   # 是否有结果

其他操作

连表

正向操作

通过外键关联找到类型id对应的名称

objs = models.UserInfo.objects.all()
for obj in objs:
    print(
        obj.name,
        obj.age,
        obj.ut_id,
        obj.ut.title,           # 单表连接:直接使用外键连接的表中的字段
        obj.ut.fo.caption,      # 多表连接:使用本表外键中的源表里面的外键中的字段
    )

反向操作
通过类型表找到属于这个类型的所有用户
obj = models.UserType.objects.all().first()
for row in obj.userinfo_set.all():   # obj.userinfo_set为属于obj这个类型的在userinfo表里的所有行对象的集合
    print(row.name, row.age)

只取部分字段

objs = models.UserInfo.objects.all()    # 返回结果是多个对象[obj,obj,...]
objs = models.UserInfo.objects.all().first()    # 返回结果是单个对象 obj

objs = models.UserInfo.objects.all().values('id','name')   # 返回结果是字典 [{id:v1,name:v1},{id:v2,name:v2},...]
objs = models.UserInfo.objects.all().values_list('id','name')   # 返回结果是元组 [(v1,v1),(v2,v2),...]

# 跨表操作
objs = models.UserInfo.objects.all().values('id','name', 'ut__title')  # 双下划线取跨表关联值。ut为外键,title为源表中的值
直接返回JasonResponse
# 注意必须是字典,否则会报错
from django.http import JsonResponse

# 如果想传列表,需要加参数safe=False
return JsonResponse([1,2,3], safe=False)
queryset序列化方法

方法1: django自带序列化queryset, 到达前端直接DataType

from django.core import serializers
v = models.Server.objects.all()
data = serializers.serialize("json", v)

方法2: 使用values()

v = models.Server.objects.values("id", "hostname", 'create_at')
时间的序列化方法——扩展JSON

​由于json无法序列化时间,所以需要对其扩展

import json 
from datetime import date 
from datetime import datetime 

class JsonCustomEncoder(json.JSONEncoder):   # 继承JSON原生编码类
    def default(self, field): 
        if isinstance(field, datetime): 
            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)

data = json.dumps(list(v), cls=JsonCustomEncoder) # 每序列化一个字段之前都要先调用此类

排序

在表中加入__str__()方法,方便用print()查看排序结果

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField()
    ug = models.ForeignKey('UserGroup', null=True)  
	def __str__(self):
        return "%s-%s"(self.id, self.name)   # 快速查看结果
使用order_by方法
user_list = models.UserInfo.objects.all().order_by('id')	 
user_list = models.UserInfo.objects.all().order_by('-id')    # 倒序
user_list = models.UserInfo.objects.all().order_by('-id', 'name') #加入第二个排序条件
print(user_list)

分组

from django.db.models import Count,Sum,Max,Min  # 可引入多个聚合函数

v = models.UserInfo.objects.values('ug_id').annotate(xxx=Count('id'))
print(v.query)   # 可查看生成的SQL语句
结果
SELECT 'app01_usergroup'.'ug_id', COUNT('app01_userinfo'.'id') AS 'xxx' FROM 'app01_userinfo'.'ug_id';

筛选

from django.db.models import Count,Sum,Max,Min

v = models.UserInfo.objects.filter(id__gt=2).values('ug_id').annotate(xxx=Count('id')).filter(xxx__gt=2)
print(v.query)   # 可查看生成的SQL语句
结果
SELECT 'app01_usergroup'.'ug_id', COUNT('app01_userinfo'.'id') AS 'xxx' FROM 'app01_userinfo' WHERE 'app01_userinfo'.'id' > 2 GROUP BY 'app01_userinfo'.'ug_id' HAVING COUNT('app01_userinfo'.'id') > 2;    // filter 在前面是 WHERE 在后面是 HAVING
其他使用方法
filter(id__gt)
filter(id__lt)
filter(id__lte)
filter(id__gte)
filter(id__in[1,2,3])
filter(id__range[1, 2])  # between
filter(name__startswith='xxx')
filter(name__contains='xxx')
exclude(id=1)  # 排除,即id!=1

多对多操作

内置方法ManyToManyField()
class Boy():
    name = CharField(32)
	m = models.ManyToManyField("Girl")      # 自动创建第三张表
    
class Girl():
    nick = CharField(32)

boyobj = models.Boy.objects.filter(name='boyname')
boyobj.m.all()				# 正向查询获得Girl对象
boyobj.m.add(1,2)         	# 增加  
boyobj.m.remove(*[1,2])   	# 删除
boyobj.m.set([1])    		# 重置:清空然后加入此条关系
boyobj.m.clear()			# 清空

girlobj = models.Boy.objects.filter(name='girlname')
girlobj.boy_set()			# 反向查询获得Boy对象
自定义方法
class Love(models.Model):
    b = models.ForeignKey('Boy')
    g = models.ForeignKey('Girl')
    class Meta:
        unique_together = [      # 联合唯一
            (b, g),
        ]
混合方法
class Boy():
    name = CharField(32)
	m = models.ManyToManyField("Girl", through='Love', through_fields=('b','g'))      # 使用自定义的表作为关系表
    
class Girl():
    nick = CharField(32)

class Love(models.Model):
    b = models.ForeignKey('Boy')
    g = models.ForeignKey('Girl')
    class Meta:
        unique_together = [      # 联合唯一
            (b, g),
        ]

# 此方法仅可使用以下两种方法
boyobj.m.all()
boyobj.m.clear()
ManyToManyField其他参数介绍
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,              # 默认创建第三张表时,数据库中表的名称

)

内置分页功能(分批获取数据)

创建Paginator对象
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

# 此步模拟数据库中获取的结果,请忽略
L = []
for i in range(999):
    L.append(i)

def index(request):
    current_page = request.GET.get('p')

    paginator = Paginator(L, 10)   # 实例化分页对象
    # Paginator的参数解释:
        # per_page: 每页显示条目数量
        # count:    数据总个数
        # num_pages:总页数
        # page_range:总页数的索引范围,如: (1,10),(1,200)
        # page:     page对象
    try:
        posts = paginator.page(current_page)
        # has_next              是否有下一页
        # next_page_number      下一页页码
        # has_previous          是否有上一页
        # previous_page_number  上一页页码
        # object_list           分页之后的数据列表
        # number                当前页
        # paginator             paginator对象
    except PageNotAnInteger:      # 如非整型数字错误则把当前页设为1
        posts = paginator.page(1)
    except EmptyPage:			# 如页面为空则把当前页设为最后一页
        posts = paginator.page(paginator.num_pages)
    return render(request, 'index.html', {'posts': posts})
HTML部分设置
<ul>
    {% for item in posts %}    {# 打印当前页内容 #}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

<div class="pagination">
      <span class="step-links">  {# 如果有上一页则显示Previous按钮 #} 
        {% if posts.has_previous %}
            <a href="?p={{ posts.previous_page_number }}">Previous</a>
        {% endif %}
          <span class="current">  {# 显示效果为 '当前页码' of '总页码' #} 
            Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
          </span>
          {% if posts.has_next %}  {# 如果有下一页则显示Next按钮 #} 
              <a href="?p={{ posts.next_page_number }}">Next</a>  
          {% endif %}
      </span>

</div>

自定义分页功能

分页时需要做三件事:

  • 创建处理分页数据的类
  • 根据分页数据获取数据
  • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
#!/usr/bin/env python
# _*_coding:utf-8_*_
from django.utils.safestring import mark_safe
 
class PageInfo(object):
    def __init__(self, current, totalItem,peritems=5):
        self.__current=current
        self.__peritems=peritems
        self.__totalItem=totalItem
    def From(self):
        return (self.__current-1)*self.__peritems
    def To(self):
        return self.__current*self.__peritems
    def TotalPage(self):  #总页数
        result=divmod(self.__totalItem,self.__peritems)
        if result[1]==0:
            return result[0]
        else:
            return result[0]+1
 
def Custompager(baseurl,currentPage,totalpage):  #基础页,当前页,总页数
    perPager=11
    #总页数<11
    #0 -- totalpage
    #总页数>11
        #当前页大于5 currentPage-5 -- currentPage+5
            #currentPage+5是否超过总页数,超过总页数,end就是总页数
        #当前页小于5 0 -- 11
    begin=0
    end=0
    if totalpage <= 11:
        begin=0
        end=totalpage
    else:
        if currentPage>5:
            begin=currentPage-5
            end=currentPage+5
            if end > totalpage:
                end=totalpage
        else:
            begin=0
            end=11
    pager_list=[]
    if currentPage<=1:
        first="<a href=''>首页</a>"
    else:
        first="<a href='%s%d'>首页</a>" % (baseurl,1)
    pager_list.append(first)
 
    if currentPage<=1:
        prev="<a href=''>上一页</a>"
    else:
        prev="<a href='%s%d'>上一页</a>" % (baseurl,currentPage-1)
    pager_list.append(prev)
 
    for i in range(begin+1,end+1):
        if i == currentPage:
            temp="<a href='%s%d' class='selected'>%d</a>" % (baseurl,i,i)
        else:
            temp="<a href='%s%d'>%d</a>" % (baseurl,i,i)
        pager_list.append(temp)
    if currentPage>=totalpage:
        next="<a href='#'>下一页</a>"
    else:
        next="<a href='%s%d'>下一页</a>" % (baseurl,currentPage+1)
    pager_list.append(next)
    if currentPage>=totalpage:
        last="<a href=''>末页</a>"
    else:
        last="<a href='%s%d'>末页</a>" % (baseurl,totalpage)
    pager_list.append(last)
    result=''.join(pager_list)
    return mark_safe(result)   #把字符串转成html语言

F、Q、extra

F:用于引用并改造原有的数据

from django.db.models import F
model.UserInfo.object.all().update(age=F("age" + 1))
condition = {
    'id':1,
    'name':'root'
}
model.UserInfo.object.all().filter(**condition)

Q:用于构造复杂查询条件

第一种:方法方式
from django.db.models import Q

model.UserInfo.object.all().filter(Q(id=8) | Q(id=2))  # Q的or用法
# model.UserInfo.object.all().filter(Q(id=8) & Q(id=2))

q1 = Q()
q1.connector = 'OR'
q1.children.append(('c1', 1))
q1.children.append(('c1', 10))
q1.children.append(('c1', 9))

q2 = Q()
q2.connector = 'OR'
q2.children.append(('c1', 2))
q2.children.append(('c1', 5))
q2.children.append(('c1', 6))

q3 = Q()
q3.connector = 'AND'
q3.children.append(('id', 1))
q3.children.append(('id', 2))
q2.add(q3, 'OR')

con = Q()
con.add(q1, 'AND')
con.add(q2, 'AND')
第二种:对象方式
# 使用for将字段转化为Q
# 假如这是一个前端的条件选择器
condition_dict = {
    'k1': [1, 2, 3, 4],
    'k2': [1,],
    'k3': [11,]
}

# 服务端无需修改
con = Q()
for k,v in condition_dict.items():
    q = Q()
    q.connect = 'OR'
    for i in v:
        q.children.append(('id',i))
    con.add(q, 'AND')

model.UserInfo.object.all().filter(con)

extra:额外的

类似临时表的使用方法

select参数

v = model.UserInfo.object.all().extra(select={
    'n': "SELECT COUNT(1) FROM app01_usertype"
})

"""
SELECT 
	id,
	name,
	(SELECT COUNT(1) FROM app01_usertype) as n 
FROM app01_userinfo;
"""


v = model.UserInfo.object.all().extra(select={
    'n': "SELECT COUNT(1) FROM app01_usertype WHERE id > %s OR id=%s" ,
    'm': "SELECT COUNT(1) FROM app01_usertype WHERE id > %s OR id=%s" ,
}, select_params=[1, 2, 3, 4])

where参数

v = model.UserInfo.object.all().extra(
    where=['id=1 or id=2',"name='alex"]   # 列表之间以AND连接,元素可以为SQL原生语句如or,and等..
,params=[])   # 同理可使用占位符

还有其他参数...

tables 类似连表及笛卡尔积的用法
# 笛卡尔积
v = model.UserInfo.object.all().extra(
    tables=['app01_usertype']   # 列表之间以AND连接,元素可以为SQL原生语句如or,and等..
,params=[]) 
所有参数使用方法示例
v = model.UserInfo.object.all().extra(
    select={'newid':'"SELECT COUNT(1) FROM app01_usertype WHERE id > %s',
    select_params=[1,],
    where=['age > %s'],
    params=[18,],
    order_by=['-age'],
    tables=['app01_usertype']

生成的SQL语句为

SELECT
	app01_userinfo.id,
	(SELECT COUNT(1) FROM app01_usertype WHERE id) AS newid
FROM app01_userinfo, app01_usertype
WHERE
	app01_userinfo.age > 18
ORDER BY
	app01_userinfo.age DESC

Field字段类型及其参数

通用参数
required=True,               是否允许为空
widget=None,                 HTML插件
label=None,                  用于生成Label标签或显示内容
initial=None,                初始值
help_text='',                帮助信息(在标签旁边显示)
error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[],               自定义验证规则
localize=False,              是否支持本地化
disabled=False,              是否可以编辑
label_suffix=None            Label内容后缀
专用参数

CharField(Field)

max_length=None,             最大长度
min_length=None,             最小长度
strip=True                   是否移除用户输入空白

IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值

FloatField(IntegerField)
...

DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度

BaseTemporalField(Field)
input_formats=None 时间格式化

DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12

DurationField(Field) 时间间隔:%d %H:%M:%S.%f
...

RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'}

EmailField(CharField)
...

FileField(Field)
allow_empty_file=False 是否允许空文件

ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)

URLField(Field)
...

BooleanField(Field)
...

NullBooleanField(BooleanField)
...

ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示

ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选

ModelMultipleChoiceField(ModelChoiceField) # 多选
... django.forms.models.ModelMultipleChoiceField

TypedChoiceField(ChoiceField)
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '' 空值的默认值

MultipleChoiceField(ChoiceField)
...

TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '' 空值的默认值

ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])

MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用

SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']

FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text=''

GenericIPAddressField
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用

SlugField(CharField) 数字,字母,下划线,减号(连字符)
...

UUIDField(CharField) uuid类型
...

FORM表单验证

定义规则

from django.forms import Form
from django.forms import fields

class LoginForm(Form):
    username = fields.CharField(  # 与表单<input>中的name一致
        max_length=18,				# 最长长度
        min_length=6,				# 最短长度
        required=True,				# 不能为空
        error_messages={			# 错误信息重写
            'required': "用户名不能为空",
            'min_length': "太短了",
            'max_length': "太长了",            
        }
    )
    password = fields.CharField(max_length=18,required=True)

def login(request):
    if request.method == "GET":
        return render(request, 'index.html')
    else:
        obj = LoginForm(request.POST)	# 拿到POST信息
        if obj.is_valid:        			# 是否通过验证
        	obj.cleand_data         		# 拿到用户输入正确的信息(字典)
        else:
        	obj.errors						# 这是一个对象,加了__str__()
            obj.errors['username']			# 获取错误信息列表 
            obj.errors['password']
            return render(request, 'login.html', {'obj': obj} )

在模版中加入

{{ obj.errors.username.0 }}
{{ obj.errors.password.0 }}
规则详细说明
class LoginForm(Form):
    # 继承Field类,有required,help_text,error_messages等常用参数
	t1 = fields.CharField() # 默认required等于Ture    
    t2 = fields.IntegerField(
        error_messages={
            'required': 't2不能为空',
            'invaild': '格式错误,必须为数字',
            'max_value': '必须小于1000',
            'min_value': '必须大于10'
        })
    
    ### 继承CharField类的所有参数,另有min_length,max_length,strip等参数 ###
    t3 = fields.EmailField(
        error_messages={
            'required': 't3不能为空',
            'invaild': '格式错误,必须为邮箱格式'
        })
    
    
    t4 = fields.URLField()
    t5 = fields.SlugField()    # 除特殊字符外的字符
    t6 = fields.GenericIPAddressField()
    t7 = fields.DateField()
    t8 = fields.DateTimeField()
    
    
    t9 = fields.RegexField(   # 自定义正则的规则
        '139\d+',             # 正则表达式
        error_massage={
            'invaild': '输入有效的号码'
        }
    )
    ########################################################   
field参数解释
from django.forms import fields
from django.forms import widgets

class TestForm(Form):
	t1 = fields.CharField(
        #############组合使用自动生成HTML标签###############
        widget=widgets.Textarea		  # 控制生成的input类型:默认是text
    	label='用户名',				# 标签
        initial='user1',			  # 提供默认值
        help_text='请输入你的用户名'    # 帮助信息
        localize=False,		 # 时间本地化
        disable=False,		 # 禁用
        label_suffix=':'	# 标签后的符号
        #############组合使用自动生成HTML标签###############
        
        validators=[],
    )
    
    # widget多种用法
	cls_id = fields.IntegerField(
        # widget.Select()
    	widget=widgets.Select(choices=[(1, '上海'),(2, '北京')])  # 增加固定值
    	widget=widgets.Select(
            choices=models.Classes.objects.values_list('id', 'title'))		# 使用数据库内的值作为选项
        
        # widget.TextInput()
        widget=widgets.TextInput(attrs={'class': 'form-contrl'}) # 为插件增加属性,每个插件都有这个参数
        
    ) 
    
    注意!这里有BUG!:choice的值无法自动更新,因为class只在启动时将变量启动一次,后续示例化不会再重新取值,choice值亦不会变化。加入以下代码解决问题:
    # 方法一
    def __init__(self, *args, **kwargs):
        super(TeacherForm, self).__init__(*args, **kwargs)
        self.fields['cls_id'].choices = models.Classes.objects.values_list('id','title') 
    
    # 方法二 : 耦合性高,建议小的应用程序使用
from django.forms import models as form_model
    cls_id = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all())   
    # 需要在Classes中加入__str__配合使用
    def __str__():
        return self.title
模版部分
<p>
  {{ obj.as_p }}   {# 顺序为: label|label_suffix|input输入框[默认值](disable)|help_text #}
</p>

<ul>
  {{ obj.as_ul }}   {# 生成ul #}
</ul>

<table>
  {{ obj.as_table }}   {# 生成table #}
</table>

后附全部字段参数的解释

用ajax提交表单

好处:不刷新,上次内容自动保留

模版部分
<form id='f1' action='/login' method='POST'>
  {% csrf_token %}
  <input type='text' name="username">
  <input type='password' name="password">
  <input type='submit' values="提交">  
</form>
<a onclick="submitForm()">Ajax提交</a>


<script>
  function summitForm(){
    $('.c1').remove;
    $.ajax({
      url: '/ajax_login',
      type: 'POST',
      data: $('#f1').serialize() // 将数据变成user=alex&pwd=456&csrftoken=defefaasd
      dataType: 'JASON'
      success: function(arg){
        if (arg.status){
			// 空
        }
        else {
           $.each(arg.msg, function(index, value)) {
      			var tag = document.createElement('span');
      			tag.innerHTML = value[0]
      			$('#f1').find('input[name="' + index + '"]').after(tag);
    	   }   
        }
      }
    })
  }
</script>
视图函数部分
import json

def ajax_login(request):
    ret = {'status': True, 'msg': None}
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)
    else:
        ret['status'] = False
        ret['msg'] = obj.errors
    v = json.dumps(obj.username)
    return HttpResponse(v)

保留form表单输入内容

模版部分login.py
<form id='f1' action='/login/' method='POST' novalidate>  {# novalidate禁止浏览器本身验证功能 #}
	{% csrf_token %}
    <p>
       {{ obj.username }}{{ obj.errors.username.0 }}   {# 自动生成input标签class=username #}
    </p>
    <p>
       {{ obj.password }}{{ obj.errors.password.0 }} 
    </p>       
    <input type='submit' values="提交">  
</form>
<a onclick="submitForm()">Ajax提交</a>
视图函数部分
class LoginForm(Form):
    username = field.CharField(min_length=8)
    password = field.CharField()


def login(request):
    if request.method == "GET":
        obj = LoginForm()                   # 第一次GET进来value不带值
        return render(request, "login.html", {'obj': obj})
    else:
        obj = LoginForm(request.POST)		# 第二次POST进来value带着用户输入的值
        if obj.is_vaild():
            print(obj.cleaned_data)
        else:
            return render(request, 'login.html', {'obj': obj})
            
多对多

小tips——快速取出以元组为元素的序列

id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
常用插件
class TestForm(Form):
    t1 = fields.CharField(
    	widget=widgets.Textarea(attrs={})
        # widget=widgets.TextInput(attrs={})        
        # widget=widgets.PasswordInput(attrs={})
    )

    t2 = fields.CharField(
        widget=widgets.CheckboxInput()    		# 单项选择框
    )

    t3 = fields.MultipleChoiceField(
        widget=widgets.CheckboxSelectMultiple   # 多选
    )
    
    t4 = fields.ChoiceField(
    	widdget=widgets.RadioSelect  			# 单选
    )
    
    t5 = fields.FileField(
    	widdget=widgets.FileInput  			# 文件上传
    )
	
def test(request):
    obj = TestForm(initial={'t3': [2, 3]})   # 设置默认值
    return render(request, 'test.html', {'obj': obj})

验证规则扩展

关注返回值,写错就会有问题,建议看源码

from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class TestForm(Form):
    user = fields.CharField(
    	validators=[RegexVaildator(r'^[0-9]+¥', '请输入数字')]       # 此处可再额外增加正则表达式
    )			  
    
    # 第一个钩子
    def clean_user(self):                 # 单独对字段进行扩展,函数名格式为"clean_变量名"
        v = self.cleaned_data['user']	  # 必须已经取得值
        if models.Student.objects.filter(name=v).count():   # 可以到数据库中查看是否有此值
            raise ValidationError('用户名已经存在')
            # raise ValidationError('用户名已经存在', code='invaild') # code对应error_msg中的键
        return v                          # 为cleaned_data中的
    
    # 第二个钩子
    def clean(self):					  # 对整体的值做验证
        user = self.cleaned_data.get('user')
        email = self.cleaned_data.get('email')
        if models.Student.objects.filter(user=user, email=email).count():
            self.add_error('user', ValidationError('用户名已经存在')) # 可将此错误归为user错误
            raise ValidationError('用户名已经存在')  # 或直接抛错误,归到__all__键
        return self.cleaned_data		# 若为None会返回未经处理的原值
    
    # 第三个钩子
    def _post_clean(self):  			 # 一般不会用到(用clean基本足够)
        pass

注:先执行正则,再执行此函数,如不通过正则,则不会执行此函数。
看源码:is_valid --> self.errors --> self.full_clean() --> 最下面三个函数

附件:字段参数解释

Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField) # 多选
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型
    ...

中间件

内置中间件

MIDDLEWARE_CLASSES = (
    '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',
)

自定义中间件

对所有请求或部分请求做批量处理

正常顺序:process_request【1、2、3】--> process_view【1、2、3】--> process_response【1、2、3】

class MyRequestExeute(object):
      
    def process_request(self,request):     	# 处理请求
        pass       							# 不要返回request,否则直接跳过其他中间件,1.10版本以后有所不同
    
    def process_view(self, request, callback, callback_args, callback_kwargs):							# 路由匹配,执行视图函数 
        i =1
        pass
    
    def process_exception(self, request, exception):   # 发生错误时执行
        pass
      
    def process_response(self, request, response):  # 处理响应信息
        return response								# 需要返回response
    
    def process_template_reponse(self, request, response):
        return response
注册中间件
MIDDLEWARE_CLASSES = (
    '模块.middleware.auth.MyRequestExeute',   # 加入自己的模块路径
)
WSGI网络服务协议
默认:wsgiref + django
生产环境:uwsgi + django

XSS攻击和CSRF防范

XSS攻击预防

XSS攻击是指那些利用输入框写入代码来控制用户客户端的行为

注:Django本身内置防XSS攻击机制,除非注明数据为safe

后台处理
msg = []

def comment(request):      # 用户使用评论输入功能
	if request.method == 'GET':
        return render(request, 'comment.html')
    else:
        v = request.POST.get('content')
        msg.append(v)
        return render(request, 'comment.html')
    
def index(request):			# 将评论在index页面展示
    return render(request, 'index.html', {'msg': msg})
评论提交页面
<form method='POST' action='/comment/'>
  <input type='text' name='content' />
  <input type='submit' name='提交' />  
</form>
评论展示页面

解除XXS防御的标记,前端代码加safe

<h1>评论</h1>
{% for item in msg %}
	<div>{{ item }}</div>   {#
	{# <div>{{ item | safe }}</div>  如果要非用safe必须将特殊字符排除#}  
{% endfor %}
标记数据为安全的函数mark_safe()
newtemp = mark_safe(temp)   # 做此标记后,数据会被前端认为是安全数据

CSRF(跨站请求伪造)处理

在模版form表单中添加token

防止黑客利用修改URL上的参数进行盗取用户信息或控制浏览器的行为,主要采用token验证身份的方法。

{% csrf_token %}
Ajax下获取token以通过验证
第一种方法:在form表单中获取
<form method="POST" action="/csrf1.html">
    {% csrf_token %}
    <input id="user" type="text" name='user' />
    <input type="submit" value="提交" />
    <a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
    function submitForm(){
        var csrf = $("input[name='csrfmiddlewaretoken']").val() // 获取csrf_token里面的值
        var user = $("#user").val()  // 获取用户名
        $.ajax({
            url: '/csrf.html',
            type: 'POST',
            data: {"user": user, "csrfmiddlewaretoken": csrf},
            success:function(arg){
                console.log(arg);
            }
        })
    }
</script>
第二种方法:获取cookie中的信息
cookie = document.cookie   // 原生的js获取cookie的方法

使用jquery.cookie插件
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"> </script>
<script>
    function submitForm(){
        var token = $.cookie('csrftoken'); // 获取cookie里面的token
        var user = $("#user").val();  // 获取用户名
        $.ajax({
            url: '/csrf.html',
            type: 'POST',
            headers: {"X-CSRFToken": token}  // 添加请求头传token
            data: {"user": user},
            success:function(arg){
                console.log(arg);
            }
        })
    }
</script>
禁用Django自带csrf防御功能
  1. 全站禁用
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',
]
  1. 局部禁用
from django.views.decorators.csrf import csrf_exempt, csrf_protect

@csrf_exempt                                # 此函数不可用
def csrf1(request):
    if request.method == 'GET':
        return render(request, 'csrf1.html')
    else:
        return HttpResponse('OK')
    
@csrf_protect								# 此函数可用
def csrf2(request):
    if request.method == 'GET':
        return render(request, 'csrf1.html')
    else:
        return HttpResponse('OK')
  1. CBV情况下
from django.views import View
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt)     				# CBV需使用此装饰器
@method_decorator(csrf_exempt, name='get')   	# 可用name参数注明给哪个方法加,给'dispatch'加即全部加
class Foo(View):
    def dispatch(self, request, *args, **kwargs):
        pass
    
    def get(self, request):
        pass
    
    @method_decorator(csrf_exempt)				# 可在指定方法加装饰器
    def post(self, request):
        pass

ModelForm

将Model和Form结合,以达到快速创建表单和数据验证并提交的效果。

参考资料:http://www.cnblogs.com/wupeiqi/articles/6229414.html

ModelForm的基本用法

from django.forms.models import ModelForm

class TestModelForm(ModelForm):		# 想自定义仍可自定义
    class Meta:
        model = models.UserInfo			# 无需重新定义表单属性,直接根据models生成
        field = '__all__'
        error_message = {
            'user': {'required': '用户名不能为空'},
            'email': {'required': '邮箱不能为空', 'invalid': '邮箱格式错误'}
        }
        
        
def test(request):
    if request.method == 'GET':
        form = TestModelForm()
        context = {
            'form': form
        }
        return render(request, 'test.html', context)
    else:
        form = TestModelForm(request.POST)
        if form.is_vaild():
            form.save()
            return redirect('http//:www.google.com')
        context = {
            'form': form
        }
        return render(request, 'test.html', context)

    
def edit(request, nid):
    obj = models.UserInfo.object.filter(id=nid).first()
    if request.method == "GET":
        form = TestModelForm(instance=obj)
        context = {
            'form': form
        }
        return render(request, 'edit.html', context)
    else:
        form = TestModelForm(instance=obj, data=request.POST, file=request.FILES)
        if form.is_vaild():
            form.save()				# 保存数据到数据库
            return redirect('http://www.google.com')
        context = {
            'form': form
        }
        return render(request, 'test.html', context)

ModelForm组件的其他参数

ModelForm
    a.  class Meta:
            model,                           # 对应Model的
            fields=None,                     # 字段
            exclude=None,                    # 排除字段
            labels=None,                     # 提示信息
            help_texts=None,                 # 帮助提示信息
            widgets=None,                    # 自定义插件
            error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
            field_classes=None               # 自定义字段类 (也可以自定义字段)
            localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
            如:
                数据库中
                    2016-12-27 04:10:57
                setting中的配置
                    TIME_ZONE = 'Asia/Shanghai'
                    USE_TZ = True
                则显示:
                    2016-12-27 12:10:57
    b. 验证执行过程
        is_valid -> full_clean -> 钩子 -> 整体错误
 
    c. 字典字段验证
        def clean_字段名(self):
            # 可以抛出异常
            # from django.core.exceptions import ValidationError
            return "新值"
    d. 用于验证
        model_form_obj = XXOOModelForm()
        model_form_obj.is_valid()
        model_form_obj.errors.as_json()
        model_form_obj.clean()
        model_form_obj.cleaned_data
    e. 用于创建
        model_form_obj = XXOOModelForm(request.POST)
        #### 页面显示,并提交 #####
        # 默认保存多对多
            obj = form.save(commit=True)
        # 不做任何操作,内部定义 save_m2m(用于保存多对多)
            obj = form.save(commit=False)
            obj.save()      # 保存单表信息
            obj.save_m2m()  # 保存关联多对多信息
 
    f. 用于更新和初始化
        obj = model.tb.objects.get(id=1)
        model_form_obj = XXOOModelForm(request.POST,instance=obj)
        ...
 
        PS: 单纯初始化
            model_form_obj = XXOOModelForm(initial={...})

django自带验证

前提:使用django自带的user表

from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required

@login_required()
def acc_login(request):
    error = ''
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(username=username, password=password)
     	if user:
            login(request, user)
            return redirect('/')
        else:
            error = 'wrong password or username'
            return render(request, 'login/', 'error': error)

@login_required(login_url='/login/')
def acc_logout(request):
    logout(request)
    return redirect(request.GET.get('next', '/'))

@login_required(login_url='/login/')  # 可以在此处单独注明,也可以在settings里面加入LOGIN_URL,即可全局控制
def host_list(request):
{{ request.user }}   // 前端模板调用user
{{ request.path }}	// 获取当前url
{{ request.user.account.host_group.all }}  // 使用request中的user进行查找,像使用object一样

function() {
	$("#miannav-menu a[herf='{{ request.path }}']").parent().addClass('active-link') // 模板中的按键的上一级与url对应的目录变成激活状态
}

//
$.get("{% url 'get_host_list' %}",{'gid': gid}, function(callback){
	console.log(callback); 
})

使用jquery 的get、POST方法传ajax到服务器

function getHostlist(self,bind){
  $.get("{% url 'get_host_list' %}",{'gid': gid, 'csrfmiddlewaretoken': '{{ csrf_token }}'}, function(callback){
      console.log(callback); 
  })
}

// 注意self是click事件绑定时将this传入

function getToken(self, bind_host_id){
  $.post("{% url 'get_token' %}",{'bind_host_id': bind_host_id,'csrfmiddlewaretoken': '{{ csrf_token }}'}, function(callback){
      console.log(callback); 
  })
}

信号、缓存、序列化

http://www.cnblogs.com/wupeiqi/articles/5246483.html

Admin

http://www.cnblogs.com/wupeiqi/articles/7444717.html

posted @ 2018-01-22 10:50  大蛇扬python  阅读(572)  评论(0编辑  收藏  举报