Django框架基础知识汇总(无项目版)

一、WEB框架本质


所有的Web应用,本质上其实是一个socket服务端,用户浏览器是一个socket客户端.

#!/usr/bin/env python
#coding:utf-8
# 服务端SOCKET 
import socket  
def handle_request(client):
    buf = client.recv(1024)
    client.send("HTTP/1.1 200 OK\r\n\r\n")
    client.send("Hello, Seven")  
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost',8000))
    sock.listen(5)  
    while True:
        connection, address = sock.accept()
        handle_request(connection)
        connection.close()
  if __name__ == '__main__':
    main()

python web程序分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,在请求到来时对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。

WSGI(Web Server Gateway Interface)是一种规范。它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。

from wsgiref.simple_server import make_server

def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [bytes('

Hello, web!

'
, encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print("Serving HTTP on port 8000...") httpd.serve_forever()

二、自定义Web框架


  • 框架:

python标准库提供的独立WSGI服务器称为wsgiref。

#通过python标准库提供的wsgiref模块开发一个自己的Web框架

#!/usr/bin/env python
#coding:utf-8
from wsgiref.simple_server import make_server 
def index():
    return 'index' 
def login():
    return 'login' 
def routers():     
    urlpatterns = (
        ('/index/',index),
        ('/login/',login),
    )     
    return urlpatterns 
def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    urlpatterns = routers()
    func = None
    for item in urlpatterns:
        if item[0] == url:
            func = item[1]
            break
    if func:
        return func()
    else:
        return '404 not found'     
if __name__ == '__main__':
    httpd = make_server('', 8000, RunServer)
    print "Serving HTTP on port 8000..."
    httpd.serve_forever()

模板引擎生成静态内容:

在上叙中,所有login、index均返回给用户浏览器简单字符串,在现实的Web请求中会返回一个复杂的符合HTML规则的字符串,需将要返回给用户的HTML写在指定文件中,然后再返回,如:


<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>title>
head>
<body>
    <h1>Indexh1>
body>
html>



<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>title>
head>
<body>
    <form>
        <input type="text" />
        <input type="text" />
        <input type="submit" />
    form>
body>
html>
……views模块内容:
def index():
    # return 'index'
    f = open('index.html')
    data = f.read()
    return data 
def login():
    # return 'login'
    f = open('login.html')
    data = f.read()
    return data 
……

开源工具Jinja2实现动态内容:

上述代码返回给用户HTML的内容来现实复杂的页面,但如何给用户返回动态内容?

  • 自定义一套特殊的语法,进行替换
  • 使用开源工具jinja2,遵循其指定语法
# index.html文件内容:

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>title>
head>
<body>
    <h1>{
     {name}}h1>
    <ul>
        {% for item in user_list %}
        <li>{
     {item}}li>
        {% endfor %}
    ul>
body>
html>

#遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容:

#!/usr/bin/env python
# -*- coding:utf-8 -*- 
from wsgiref.simple_server import make_server
from jinja2 import Template 

def index():
    # return 'index' 
    # template = Template('Hello {
     { name }}!')
    # result = template.render(name='John Doe') 
    f = open('index.html')
    result = f.read()
    template = Template(result)
    data = template.render(name='John Doe', user_list=['alex', 'eric'])
    return data.encode('utf-8') 
def login():
    # return 'login'
    f = open('login.html')
    data = f.read()
    return data 
def routers(): 
    urlpatterns = (
        ('/index/', index),
        ('/login/', login),
    ) 
    return urlpatterns 
def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    urlpatterns = routers()
    func = None
    for item in urlpatterns:
        if item[0] == url:
            func = item[1]
            break
    if func:
        return func()
    else:
        return '404 not found' 
if __name__ == '__main__':
    httpd = make_server('', 8000, run_server)
    print "Serving HTTP on port 8000..."
    httpd.serve_forever()

三、Django基本配置


创建django程序:

  • 终端命令:django-admin startproject sitename
  • IDE创建Django程序时,本质上都是自动执行上述命令
    其他常用命令:
    python manage.py runserver 0.0.0.0
    python manage.py startapp appname # 创建app程序模块
    python manage.py syncdb
    python manage.py makemigrations
    python manage.py migrate
    python manage.py createsuperuser

程序目录:

mysite
mysit
init.py
settings.py
urls.py
wsgi.py
templates
manage.py

setting.py 配置文件:

Mysql数据库:
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.mysql’,
‘NAME’:’dbname’,
‘USER’: ‘root’,
‘PASSWORD’: ‘xxx’,
‘HOST’: ”,
‘PORT’: ”,
}
}

备注:
由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
如下设置放置的与project同名的配置的 init.py文件中
**import pymysql
pymysql.install_as_MySQLdb()** 

四、路由系统


单一路由对应:
url(r’^index$’, views.index)

.

基于正则的路由:
url(r’^index/(\d*)’, views.index), # 匹配0到无穷个数字
url(r’^manage/(?P\w*)/(?P\d*)’, views.manage), # 匹配到两个组,别名分别为name,id

.

添加额外的参数:
url(r’^manage/(?P\w*)’, views.manage,{‘id’:333}), #> 视图函数中的参数可接收id

.

为路由映射设置名称:

url(r’^home’, views.home, name=’h1’),
url(r’^index/(\d*)’, views.index, name=’h2’),

设置名称之后,可以在不同的地方调用,如:
模板中使用生成URL {% url ‘h2’ 2012 %}

函数中使用生成URL reverse(‘h2’, args=(2012,)) 路径:django.urls.reverse
Model中使用获取URL 自定义get_absolute_url() 方法:

class NewType(models.Model):
    caption = models.CharField(max_length=16)
    def get_absolute_url(self):
        """
        为每个对象生成一个URL
        应用:在对象列表中生成查看详细的URL,使用此方法即可!!!
        :return:
        """
        # return '/%s/%s' % (self._meta.db_table, self.id)
        # 或
        from django.urls import reverse
        return reverse('NewType.Detail', kwargs={
    'nid': self.id})

根据app对路由规则进行分类:
url(r’^web/’,include(‘web.urls’)),

命名空间:
# a. project.urls.py:
from django.conf.urls import url,include 
urlpatterns = [
    url(r'^a/', include('app01.urls', namespace='author-polls')),
    url(r'^b/', include('app01.urls', namespace='publisher-polls')),
]

# b. app01.urls.py:
from django.conf.urls import url
from app01 import views 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P\d+)/$', views.detail, name='detail')
]

# c. app01.views.py:
def detail(request, pk):
    print(request.resolver_match)
    return HttpResponse(pk)

# 以上定义带命名空间的url之后,使用name反向生成URL时候,应该如下:
v = reverse('app01:detail', kwargs={
    'pk':11})
{% url 'app01:detail' pk=12 pp=99 %}

django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给一个views中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。

五、模板


模版的执行:

模版的创建过程,是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户.

def current_datetime(request):
    now = datetime.datetime.now()
    html = "It is now %s." % now
    return HttpResponse(html)

内置模版语言:

{
     { item }}   # 获得字典数据
{% for item in item_list %}  <a>{
     { item }}a>  {% endfor %}  # for循环出数据
  forloop.counter
  forloop.first
  forloop.last 
{% if ordered_warranty %}  {% else %} {% endif %}   # if判断数据
母板:{% block title %}{% endblock %}   # title为母板名称
子板:{% extends "base.html" %}
   {% block title %}{% endblock %}  # 子板继承母板title
帮助方法:
{
     { item.event_start|date:"Y-m-d H:i:s"}}    # 日期函数应用
{
     { bio|truncatewords:"30" }}
{
     { my_list|first|upper }}   # simple_tag实现模板中加载函数执行
{
     { name|lower }}    # 模版中应用小写的函数

自定义simple_tag模版语言:

定义:

a、在app中创建templatetags模块文件夹
b、创建任意 .py 文件,如:xx.py
# xx.py 文件内容如下:

#!/usr/bin/env python
#coding:utf-8
from django import template
from django.utils.safestring import mark_safe   
register = template.Library()   
@register.simple_tag
def my_simple_time(v1,v2,v3):
    return  v1 + v2 + v3   
@register.simple_tag   # 将函数注册到simple_tag中
def my_input(id,arg):   # 定义处理的函数
    result = "" %(id,arg,)
    return mark_safe(result)   # 创建html能识别的格式标签,而非字符

使用方式:

使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名
{% load xx %}  # 加载自定义的simple_tag 所在的文件名
{% my_simple_time 1 2 3%} # 使用自定义的simple_tag函数名,并传入参数
{% my_input 'id_username' 'hide'%}

注意:

在settings中配置当前app,不然django无法找到自定义的simple_tag:
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
     'app01',   # 注册
)

七、中间件


中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。
中间件中可以定义5个方法,分别是:

process_request(self,request)   # 重要
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)   # 重要

以上方法的返回值可以是None和HttpResonse对象。
如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户,不再走其他中间件的request方法

引用块内容:

创建中间件类:

class RequestExeute(object):      
    def process_request(self,request):    # 先执行所有中间件的process_request函数
        pass
    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):  #所有中间件的所有process_request执行完后再执行process_response函数
        return response

注册中间件:

 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',
    'wupeiqi.middleware.auth.RequestExeute',
)

八、admin后台


使用步骤:

django amdin是django提供的一个后台管理页面,该管理页面提供完善的html和css,
使得在通过Model创建完数据库表后,就可对数据直接进行增删改查。

  • 创建后台管理员:python manage.py createsuperuser
  • 配置url :url(r’^admin/’, include(admin.site.urls)) # 默认会自动生成
  • 注册和配置django admin后台管理页面:
    .
# 设置数据表名称:
class UserType(models.Model):
    name = models.CharField(max_length=50)  
    class Meta:   # 元信息类
        verbose_name = '用户类型'
        verbose_name_plural = '用户类型'
# 在admin.py模块中执行如下配置:
from django.contrib import admin  
from app01 import  models  
admin.site.register(models.UserType)   # 将model模块中的UserType类注册到admin中
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
# 打开表之后,可设定默认显示,需要在model.py模块中作如下配置:
class UserType(models.Model):
    name = models.CharField(max_length=50)  
    def __unicode__(self):
        return self.name

class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')  # 默认显示
# 为数据表添加搜索功能:
from django.contrib import admin  
from app01 import  models

class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')
    search_fields = ('username', 'email')   # 搜索功能
# 添加快速过滤:
from django.contrib import admin  
from app01 import  models  
class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')
    search_fields = ('username', 'email')
    list_filter = ('username', 'email')   # 快速过滤

八、Model类


使用前准备工作:

  • 创建数据库,设计表结构和字段
  • 使用 MySQLdb 来连接数据库,并编写数据访问层代码
  • 业务逻辑层去调用数据访问层执行数据库操作
import MySQLdb 
def GetList(sql):
    db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost')   # 连接数据库
    cursor = db.cursor()   # 获得游标
    cursor.execute(sql)   # 执行sql语句
    data = cursor.fetchall()   # 获得数据表所有数据
    db.close()   # 关闭连接
    return data  

def GetSingle(sql):
    db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost')
    cursor = db.cursor()
    cursor.execute(sql)
    data = cursor.fetchone()
    db.close()
    return data

django为使用一种新的方式,即:对象关系映射(Object Relational Mapping,简称ORM)。
django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。

创建数据表:

- 基本结构:

from django.db import models   
class userinfo(models.Model):
    name = models.CharField(max_length=30)   # 定义字符格式字段
    email = models.EmailField()   # 邮箱格式字段,会自动进行格式校验
    memo = models.TextField()   # 文本格式字段

- 字段:


AutoField(Field)   # int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)   # bigint自增列,必须填入参数 primary_key=True

# 注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):    
    username = models.CharField(max_length=32) # 自动创建一个列名为id的且为自增的整数列在其中
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
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                数据库中字段是否可以为空
db_column           数据库中字段的列名
db_tablespace
default             数据库中字段的默认值
primary_key         数据库中字段是否为主键
db_index            数据库中字段是否可以建立索引
unique              数据库中字段是否可以建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

verbose_name        Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                   如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                   字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                   如:{
    'null': "不能为空.", 'invalid': '格式错误'}

validators          自定义错误验证(列表类型),从而定制想要的验证规则
                   from django.core.validators import RegexValidator
                   from django.core.validators import EmailValidator,URLValidator,DecimalValidator,                   
                   MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                   如:
                       test = models.CharField(
                           max_length=32,
                           error_messages={
                               'c1': '优先错信息1',
                               'c2': '优先错信息2',
                               'c3': '优先错信息3',
                           },
                           validators=[
                               RegexValidator(regex='root_\d+', message='错误了', code='c1'),
                               RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
                               EmailValidator(message='又错误了', code='c3'), ]
                       )

- 元信息:

class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)
        class Meta:
            # 数据库中生成的表名称: app名称 + 下划线 + 类名  (默认生成)
            db_table = "table_name"
            # 联合索引
            index_together = [
                ("pub_date", "deadline"),
            ]
            # 联合唯一索引
            unique_together = (("driver", "restaurant"),)
            # admin中显示的表名称
            verbose_name
            # verbose_name加s
            verbose_name_plural

- 补充知识:

1.触发Model中的验证和错误提示有两种方式:
a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息。
b. 调用Model对象的 clean_fields 方法,如:
            # models.py
            class UserInfo(models.Model):
                nid = models.AutoField(primary_key=True)
                username = models.CharField(max_length=32)
                email = models.EmailField(error_messages={
    'invalid': '格式错了.'})

            # views.py
            def index(request):
                obj = models.UserInfo(username='11234', email='uu')
                try:
                    print(obj.clean_fields())
                except Exception as e:
                    print(e)
                return HttpResponse('ok')

           # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

    2.Admin中修改错误提示:
        # admin.py
        from django.contrib import admin
        from model_club import models
        from django import forms

        class UserInfoForm(forms.ModelForm):
            username = forms.CharField(error_messages={
    'required': '用户名不能为空.'})
            email = forms.EmailField(error_messages={
    'invalid': '邮箱格式错误.'})
            age = forms.IntegerField(initial=1, error_messages={
    'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

        class Meta:
            model = models.UserInfo
            # fields = ('username',)
            fields = "__all__" 

        class UserInfoAdmin(admin.ModelAdmin):
            form = UserInfoForm
       admin.site.register(models.UserInfo, UserInfoAdmin)   # 注册到admin组件中

创建连表:

  • 一对多:models.ForeignKey(其他表)
  • 多对多:models.ManyToManyField(其他表)
  • 一对一:models.OneToOneField(其他表)

应用场景:

  • 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
    例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
  • 多对多:在某表中创建一行数据是,有一个可以多选的下拉框
    例如:创建用户信息,需要为用户指定多个不同的爱好,生成多条1个用户与不同爱好关联的数据,且一个爱好对应多个用户的数据。
  • 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了,相当于一对多+唯一性约束)
    例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列新的数据

字段以及参数:

  • ForeignKey
ForeignKey(ForeignObject) # ForeignObject(RelatedField)
        to,                         # 要进行关联的表名
        to_field=None,              # 要关联的表中的字段名称
        on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
                                        - models.CASCADE,删除关联数据,与之关联也删除
                                        - models.DO_NOTHING,删除关联数据,引发错误IntegrityError
                                        - models.PROTECT,删除关联数据,引发错误ProtectedError
                                        - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
                                        - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
                                        - models.SET,删除关联数据,
                                                      a. 与之关联的值设置为指定值,设置:models.SET(值)
                                                      b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

 def func():
     return 10
 class MyModel(models.Model):
     user = models.ForeignKey(
         to="User",
         to_field="id"
         on_delete=models.SET(func),)

        related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:

# 如:
     - limit_choices_to={
    'nid__gt': 5}
     - limit_choices_to=lambda : {
    'nid__gt': 5}

     from django.db.models import Q
     - limit_choices_to=Q(nid__gt=10)
     - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
     - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

        db_constraint=True          # 是否在数据库中创建外键约束
        parent_link=False           # 在Admin中是否显示关联数据
  • OneToOneField:
OneToOneField(ForeignKey)
to,                         # 要进行关联的表名
to_field=None               # 要关联的表中的字段名称
on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为

###### 对于一对一 ######
 # 1. 一对一其实就是 一对多 + 唯一索引
 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
 # 如下会在A表中额外增加一个c_part_id列且唯一:
class C(models.Model):
    nid = models.AutoField(primary_key=True)
    part = models.CharField(max_length=12)

class A(C):
    id = models.AutoField(primary_key=True)
    code = models.CharField(max_length=1)
  • ManyToManyField:
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)

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,              # 默认创建第三张表时,数据库中表的名称

单表操作:

  • 基本操作:
     增    
     models.Tb1.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs

     obj = models.Tb1(c1='xx', c2='oo')
     obj.save()

     查
     models.Tb1.objects.get(id=123)            # 获取单条数据,不存在则报错(不建议)
     models.Tb1.objects.filter().first   # 推荐该方式获取单条数据
     models.Tb1.objects.all()                  #获取全部
     models.Tb1.objects.filter(name='seven')   #获取指定条件的数据

     删
     models.Tb1.objects.filter(name='seven').delete()  删除指定条件的数据

     改
     models.Tb1.objects.filter(name='seven').update(gender='0')   将指定条件的数据更新,均支持 **kwargs
第2种修改数据方式:
     obj = models.Tb1.objects.get(id=1)
     obj.c1 = '111'   # 对c1字段进行赋值班
     obj.save()                                                  修改单条数据
  • 进阶操作 (双下划线连表操作)
# 获取个数:       
         models.Tb1.objects.filter(name='seven').count()

        # 大于,小于:        
         models.Tb1.objects.filter(id__gt=1)               获取id大于1的值
         models.Tb1.objects.filter(id__gte=1)               获取id大于等于1的值
         models.Tb1.objects.filter(id__lt=10)              获取id小于10的值
         models.Tb1.objects.filter(id__lte=10)              获取id小于10的值
         models.Tb1.objects.filter(id__lt=10, id__gt=1)    获取id大于1 且 小于10的值,等价于and

         #in,范围有效值判断         
         models.Tb1.objects.filter(id__in=[11, 22, 33])    获取id等于112233的数据
         models.Tb1.objects.exclude(id__in=[11, 22, 33])   等价于 not in

         # isnull,判断是否为空
         Entry.objects.filter(pub_date__isnull=True)

         # contains 表中字段是否包含某一个值       
         models.Tb1.objects.filter(name__contains="ven")
         models.Tb1.objects.filter(name__icontains="ven")   # icontains大小写不敏感
         models.Tb1.objects.exclude(name__icontains="ven")

         # range 范围判断       
         models.Tb1.objects.filter(id__range=[1, 2])    范围bettwen and
         # 其他类似:        
         startswith,istartswith, endswith, iendswith,

         # order by排序        
         models.Tb1.objects.filter(name='seven').order_by('id')     # asc升序
         models.Tb1.objects.filter(name='seven').order_by('-id')    # desc降序

         # group by分组        
         from django.db.models import Count, Min, Max, Sum
         models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) #以id为分组条件,分组结果字段别名为c,获取每个组中num字段的数量
         #等价于:SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

         # limit限制查询数据, offset偏移 :       
         models.Tb1.objects.all()[10:20]

         # regex正则匹配,iregex 不区分大小写:        
         Entry.objects.get(title__regex=r'^(An?|The) +')  # 至少有一个以An或The开头的内容
         Entry.objects.get(title__iregex=r'^(an?|the) +')

         # date日期        
         Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))  # 查询pub_date字段中日期为2005,1,1的数据
         Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

         # year:        
         Entry.objects.filter(pub_date__year=2005)
         Entry.objects.filter(pub_date__year__gte=2005)

         # month:        
         Entry.objects.filter(pub_date__month=12)
         Entry.objects.filter(pub_date__month__gte=6)

         # day:        
         Entry.objects.filter(pub_date__day=3)
         Entry.objects.filter(pub_date__day__gte=3)

         # week_day:        
         Entry.objects.filter(pub_date__week_day=2)
         Entry.objects.filter(pub_date__week_day__gte=2)

         # hour:        
         Event.objects.filter(timestamp__hour=23)
         Event.objects.filter(time__hour=5)
         Event.objects.filter(timestamp__hour__gte=12)

         # minute:        
         Event.objects.filter(timestamp__minute=29)
         Event.objects.filter(time__minute=46)
         Event.objects.filter(timestamp__minute__gte=29)

         # second(秒):        
         Event.objects.filter(timestamp__second=31)
         Event.objects.filter(time__second=2)
         Event.objects.filter(timestamp__second__gte=31)
  • 其它高级操作:
 # extra (添加额外语句)   
     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'])

#F  (找到数据表字段)  
     from django.db.models import F
     models.Tb1.objects.update(num=F('num')+1) # 在所有的num字段基础上加1


# Q (条件组合)    
     方式一:
     Q(nid__gt=10)
     Q(nid=8) | Q(nid__gt=10)
     Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

     方式二:
     con = Q()

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

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

     con.add(q1, 'AND')
     con.add(q2, 'AND')    
     models.Tb1.objects.filter(con)

# 等价于((id=1)or(id=10)or(id=9))and((c1=1)or(c1=10)or(c1=9))


# 通过sqlAlchemy执行原生SQLfrom django.db import connection, connections
     cursor = connection.cursor()   # 创建游标
cursor = connections['default'].cursor()    # 设置游标
     cursor.execute("SELECT * from auth_user where id = %s", [1])   # 执行sql语句
     row = cursor.fetchone()    # 获取一条数据

连表操作:

  • 表结构
class UserProfile(models.Model):
    user_info = models.OneToOneField('UserInfo')
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)

    def __str__(self): 
        return self.username


class UserInfo(models.Model):
    user_type_choice = (
        (0, u'普通用户'),
        (1, u'高级用户'),
    )
    user_type = models.IntegerField(choices=user_type_choice)
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    address = models.CharField(max_length=128)

    def __str__(self):
        return self.name


class UserGroup(models.Model):

    caption = models.CharField(max_length=64)

    user_info = models.ManyToManyField('UserInfo')

    def __str__(self):
        return self.caption


class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.GenericIPAddressField()
    user_group = models.ForeignKey('UserGroup')

    def __str__(self):
        return self.hostname

九、扩展内容:(上传文件)


  • 自定义上传文件:
def upload_file(request):
    if request.method == "POST":
        obj = request.FILES.get('fafafa')   # 获得文件对象
        f = open(obj.name, 'wb')    # 创建文件句柄,以字节形式写入文件
        for chunk in obj.chunks():  # 获得文件块
            f.write(chunk)  # 将文件块写入文件
        f.close()
    return render(request, 'file.html')
  • Form上传文件实例:
Form定义:

class FileForm(forms.Form):
    ExcelFile = forms.FileField() # 定义表单中的文件上传组件
Model类定义:
from django.db import models

class UploadFile(models.Model):
    userid = models.CharField(max_length = 30)
    file = models.FileField(upload_to = './upload/')
    date = models.DateTimeField(auto_now_add=True) # 自动添加日期
views:
def UploadFile(request):
    uf =  FileForm(request.POST,request.FILES)   # 将请求中的数据发送给定义的Form组件进行校验
    if uf.is_valid():   # 校验通过,Form组件中包含用户输入到字段对应的值
            upload = models.UploadFile()   # 实例化Model类,获得对象
            upload.userid = 1   # 为对象的userid赋值,可以对应成是为数据表中的字段赋值
            upload.file = uf.cleaned_data['ExcelFile']  # 从Form组件中获得ExcelFile中的值传给Model类中对应数据表中的字段
            upload.save()   # 将该条数据保存到数据库

            print (upload.file)

十、Form内置组件


django中的Form一般有2种功能:

  • 输入html(生成表单组件)
  • 验证用户输入

Form:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
from django.core.exceptions import ValidationError

# 定义验证函数:
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')    # 定义正则表达式规则,并进行编译
    if not mobile_re.match(value):  # 判定传入的参数是否匹配正则表达式的规则
        raise ValidationError('手机号码格式错误')   # 将手机输入框组件的错误信息添加到该字段对应的错误信息列表中


class PublishForm(forms.Form):  # 定义Form组件类

    user_type_choice = (
        (0, u'普通用户'),
        (1, u'高级用户'),
    )
    user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
                                                                  attrs={
    'class': "form-control"})) # 定制下拉框表单组件

    title = forms.CharField(max_length=20,
                            min_length=5,
                            error_messages={
    'required': u'标题不能为空',
                                            'min_length': u'标题最少为5个字符',
                                            'max_length': u'标题最多为20个字符'},
                            widget=forms.TextInput(attrs={
    'class': "form-control",  # 对组件添加属性
                                                          'placeholder': u'标题5-20个字符'}))    # 默认提示内容

    memo = forms.CharField(required=False,
                           max_length=256,
                           widget=forms.widgets.Textarea(attrs={
    'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3}))

    phone = forms.CharField(validators=[mobile_validate, ], # 添加验证器,对用户在该表单组件中输入的值进行正则表达式函数的验证
                            error_messages={
    'required': u'手机不能为空'},   # 设置验证不通过时前端显示的错误信息
                            widget=forms.TextInput(attrs={
    'class': "form-control",
                                                          'placeholder': u'手机号码'}))

    email = forms.EmailField(required=False,    # 该字段非必填项
                            error_messages={
    'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=forms.TextInput(attrs={
    'class': "form-control", 'placeholder': u'邮箱'}))

views:

def publish(request):
    ret = {
    'status': False, 'data': '', 'error': '', 'summary': ''}   # 定义返回参数
    if request.method == 'POST':
        request_form = PublishForm(request.POST)  # 将请求数据传入Form组件进行验证
        if request_form.is_valid():
            request_dict = request_form.clean()  # 获得Form组件验证过的数据            
            ret['status'] = True
        else:
            error_msg = request_form.errors.as_json()  # 获得错误信息列表的json字符串
            ret['error'] = json.loads(error_msg) # 将json字符串转换为字典对象
    return HttpResponse(json.dumps(ret))  # 将字典对象转换成json字符串进行传输

扩展ModelForm:

在使用Model和Form时,都需要对字段进行定义并指定类型,
通过ModelForm则可以省去From中字段的定义。
class AdminModelForm(forms.ModelForm):

    class Meta:
        model = models.Admin
        #fields = '__all__'
        fields = ('username', 'email')

        widgets = {
            'email' : forms.PasswordInput(attrs={
    'class':"alex"}),
        }

十一、跨站请求伪造攻击(crsf)


简介:

crsf会生成随机字符串发送给前端,前端请求可携带随机字符串来进行识别。
django为用户实现防止跨站请求伪造的功能,
通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
而对于django中设置防跨站请求伪造功能有分为全局和局部。

 - 全局:
在settion配置文件中添加中间件 django.middleware.csrf.CsrfViewMiddleware
 - 局部:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

应用:

  • 普通Form表单:
veiw中设置返回值:
  return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))  
     或者
     return render(request, 'xxx.html', data)  # 返回的request请求中包含crsf随机字符串发送给前端

html中设置Token:
  {% csrf_token %}  # 从返回中的request中取出crsf_token随机字符串,再次发送请求时,将该值传递给中间件验证
  • Ajax实现跨站请求验证:
# html部分:
<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 src="/static/jquery.cookie.js">script>
# js文件部分:
<script>
    function submitForm(){
      
        var token = $.cookie('csrftoken'); # 获得cookies中的csrf随机字符串
        var user = $('#user').val();
        $.ajax({
            url: '/csrf1.html',
            type: 'POST',
            headers:{
     'X-CSRFToken': token}, # 将数据添加到请求头中,让Django去取,硬性规定
            data: { "user":user},
            success:function(arg){
      
                console.log(arg);
            }
        })
    }
script>
# views部分

def csrf1(request):
    if request.method == 'GET':
        return render(request,'csrf1.html')  # request中有cookies,其中包含crsf随机字符串发送给前端
    else:
        return HttpResponse('ok')

十二、Cookie


后台获取请求中Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)   # 获得加盐后的cookies
参数:
     default: 默认值
        salt: 加密盐
     max_age: 后台控制过期时间

后台设置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...) 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
    参数:
        key,             键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

前端获取Cookies:

由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'>script>
$.cookie("list_pager_num", 30,{ path: '/' });

十三、Session


简介:

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

数据库Session:

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

配置 settings.py 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认) 
  • 使用示例:
def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']

        # 获得所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems() 

        # 获得用户session的随机字符串,并非值
        request.session.session_key

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

        # 检查 用户session的随机字符串 在数据库中是否存在(传入cookies中的key,它对应的value是随机字符串)
        request.session.exists("session_key")

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

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

缓存session:

 # 配置 settings.py 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 

    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存 

文件Session:

# 配置 settings.py

    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T


    SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存

缓存+数据库Session:

数据库用于做持久化,缓存用于提高效率: 
a. 配置 settings.py 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'      
      # 引擎
……
 b. 使用 
    同上

加密cookies:

a. 配置 settings.py     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎 
    ……
b. 使用 
    同上

扩展:Session用户验证(将验证封装成装饰器)

def login(func):
    def wrap(request, *args, **kwargs):
        # 如果未登陆,跳转到指定页面
        if request.path == '/test/':
            return redirect('http://www.baidu.com')
        return func(request, *args, **kwargs)
    return wrap

十四、分页


Django内置分页:

# views:

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)   # 传入数据及每页需要显示的条数
    # 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:
        posts = paginator.page(1)  # 返回第1页
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)  # 返回最后一页
    return render(request, 'index.html', {
    'posts': posts})  # 
# html:


<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>title>
head>
<body>
<ul>
    {% for item in posts %}
        <li>{
     { item }}li>  # 当前页对应的所有数据
    {% endfor %}
ul>

<div class="pagination">
      <span class="step-links">
        {% if posts.has_previous %}   # 是否有上一页
            <a href="?p={
     { posts.previous_page_number }}">Previousa>   #  posts.previous_page_number表示 上一页页码
        {% endif %}

          <span class="current">
            Page {
     { posts.number }} of {
     { posts.paginator.num_pages }}   # 当前页
          span>

          {% if posts.has_next %}   # 是否有下一页
              <a href="?p={
     { posts.next_page_number }}">Nexta> # posts.previous_page_number表示 下一页页码
          {% endif %}
      span>
div>
body>
html>

自定义分页:

分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入,计算出应该在数据库表中的起始位置。

数据显示部分:
1、设定每页显示数据条数
2、用户输入页码(第一页、第二页...3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置
4、在数据表中根据起始位置取值,页面上输出数据
页数显示部分:(如:[上一页][1][2][3][4][5][下一页])
1、设定每页显示数据条数
2、用户输入页码(第一页、第二页...3、设定显示多少页号
4、获取当前数据总条数
5、根据设定显示多少页号和数据总条数计算出总页数
6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置
7、在数据表中根据起始位置取值,页面上输出数据
8、输出分页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):    # current:当前页; totalItem:数据库查出的数据总条数; peritems:每页显示的条数,默认值为5条
        self.__current=current
        self.__peritems=peritems
        self.__totalItem=totalItem
""" 根据当前页码数计算出数据表的查询范围 """
# 范围查询范例:models.UserInfo.objects.all()[page_info.start():page_info.end()]
    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  # 数据库总数/页面总数后还有多余的数据,所以总条数还需要加1页来显示剩余的数据


""" 根据基础页链接,当前页,总页数计算出页码显示部分的内容 """
def Custompager(baseurl,currentPage,totalpage):  #基础页,当前页,总页数
perPager=11 # 默认页码数显示范围为11
# if 总页数<11
#0 -- totalpage # 获得从起始页到总页数的范围,end页码数就是根据数据库总页数
# if 总页数>11
# if 当前页>5 获得 currentPage-5 -- currentPage+5的页码数
#currentPage+5是否超过总页数,超过总页数,end就是总页数
# if 当前页< 5  获得 0 -- 11 范围的页码数
# 根据begin,end的范围显示所有的中间页码数
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="首页"
else:
first="首页" % (baseurl,1)
pager_list.append(first)

if currentPage<=1:
prev="上一页"
else:
prev="上一页" % (baseurl,currentPage-1)
pager_list.append(prev)
# 循环中间页,与数据表中的数据显示关联起来
for i in range(begin+1,end+1):
if i == currentPage:    # 当前页选中状态,添加class样式
temp="%d" % (baseurl,i,i)
else:
temp="%d" % (baseurl,i,i)
pager_list.append(temp)
#
if currentPage>=totalpage:
next="下一页"
else:
next="下一页" % (baseurl,currentPage+1)
pager_list.append(next)
# 
if currentPage>=totalpage:
last="末页"
else:
last="末页" % (baseurl,totalpage)
pager_list.append(last)
result=''.join(pager_list)  # 将列表数据通过拼接转换成字符串
return mark_safe(result)   #把字符串转成html语言
总结,分页时需要做三件事:
创建处理分页数据的类
根据分页数据获取数据
输出分页HTML,即:[上一页][1][2][3][4][5][下一页]

十五、序列化


Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。

serializers模块:对Queryset对象序列化

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

json.dumps:

import json

    #ret = models.BookType.objects.all().values('caption')
    ret = models.BookType.objects.all().values_list('caption')

    ret=list(ret)   # 将元组列表对象转换为列表对象

    result = json.dumps(ret) # 将列表对象序列化为json字符串

json.dumps时无法处理datetime日期,可通过自定义处理器来做扩展

import json  
from datetime import date  
from datetime import datetime  

class JsonCustomEncoder(json.JSONEncoder): 

    def default(self, field):      
        if isinstance(field, datetime):  # 传入数据类型判断,时间
            return o.strftime('%Y-%m-%d %H:%M:%S')  # 格式化成字符串
        elif isinstance(field, date):  # 日期
            return o.strftime('%Y-%m-%d')  
        else:  
            return json.JSONEncoder.default(self, field)    # 调用父类中的default方法

  # ds = json.dumps(d, cls=JsonCustomEncoder)  # 扩展类的应用范例,d为日期数据

你可能感兴趣的:(Django框架)