Django之其他功能

时间:Oct. 16, 2017 分类:

目录:

模板语言

内置模版语言

模板中也有自己的语言,该语言可以实现数据展示

变量

{{ item }}

如果方式特殊字符被转义

{{ item|safe }}

循环

{% for item in item_list %}  
<a>{{ item }}</a>  
{% endfor %}

- forloop.counter 循环计数器 - forloop.first - forloop.last

判断

{% if ordered_warranty %}  
{% else %} 
{% endif %}

母板

{% block title %}
{% endblock %}

子板

{% extends "base.html" %}
{% block title %}
{% endblock %}

帮助方法

{{ item.event_start|date:"Y-m-d H:i:s"}}
{{ bio|truncatewords:"30" }}
{{ my_list|first|upper }}
{{ name|lower }}

自定义模板

  1. 创建templatetags模块
  2. 在模块中创建自定义模块的py文件
  3. 将自定义模板在html中load
  4. 使用自定义模板

自定义模板的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
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)

默认防止跨站xss攻击,mark_safe则认为是安全的

html中load

{% load xx %}

使用模板

{% my_simple_time 1 2 3%}
{% my_input 'id_username' 'hide'%}

更多的可以参考官方文档

cookies

django默认支持cookies功能

获取cookies

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

设置cookies

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获取(不是绝对,底层抓包可以获取到也可以被覆盖)

JQuery操作cookies

<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

session

django默认支持session,并且提供了5种支持session的方式

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

缓存配置方式

数据库Session

对于默认情况下,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,默认修改之后才保存(默认)

缓存session

除了数据库都需要配置CACHE,根据CACHE配置进行缓存


    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

    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


    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

加密cookie的session


    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'

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的随机字符串 在数据库中是否
        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失效策略。

demo

views

def check(request):
    print '1111111111'
    return HttpResponse(request.session['k1'])

def session(request):
    request.session['k1'] = 123
    return HttpResponse('session')

urls

urlpatterns = [
    url(r'session/$', views.session),
    url(r'check/$', views.check),
]

session的url

check的url

数据库session查询

mysql> select * from django_session;
+----------------------------------+----------------------------------------------------------------------+----------------------------+
| session_key                      | session_data                                                         | expire_date                |
+----------------------------------+----------------------------------------------------------------------+----------------------------+
| igb92r473tmbkjxzarb6kgb2w8aiwfp4 | NDJjOGNlNjg3MzZhZTY3OWZhN2I4MDJmMzVlNzIyMTRkY2Y5MTliMzp7ImsxIjoxMjN9 | 2017-10-29 12:35:32.999000 |
+----------------------------------+----------------------------------------------------------------------+----------------------------+
1 row in set (0.00 sec)

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

中间件

正常访问我们可能需要记录一下这些访问,如果使用装饰器,就会每一个views都需要添加这个装饰器,而中间件是全局的中间件.

Django中间件类似于tornado的钩子,中间件是在请求处理之前进行操作,在url访问之前先经过中间件,然后是url到views业务逻辑处理,然后返回再次穿过中间件返回给用户

在settings中的默认配置

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

其为默认的中间件,请求进来的时候顺序为从上到下,请求返回的时候顺序为从下到上

例如csrf,如果请求在经过csrf的时候没有验证通过,直接就进行返回,不会交给url处理

csrf

csrf是中间件的一种

views.py

def csrf(request):
    return render(request,'csrf.html')

csrf.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/csrf/" method="post">
        <input name="username">
        <input type="submit" value="submit">
    </form>
</body>
</html>

对于GET请求是没有任何的问题

而POST就会进行验证

django控制台打印的日志

[11/Oct/2017 01:10:01] "GET /app/csrf/ HTTP/1.1" 200 256
Forbidden (CSRF cookie not set.): /app/csrf/
[11/Oct/2017 01:10:17] "POST /app/csrf/ HTTP/1.1" 403 2857

报错信息

Forbidden (403)
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.
If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for 'same-origin' requests.
Help
Reason given for failure:
    CSRF cookie not set.

In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function passes a request to the template's render method.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.
You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.

其中主要的介绍就是

  1. 浏览器需要支持cookies
  2. 在views使用render
  3. 将{% csrf_token %}写在form标签中

然后将{% csrf_token %}写在form标签中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/csrf/" method="post">
        {% csrf_token %}
        <input name="username">
        <input type="submit" value="submit">
    </form>
</body>
</html>

这样就能正常的进行POST请求了

其实csrf的原理就是进行GET请求的时候返回一个cookies,而进行POST请求的时候,就携带着这个cookies进行POST请求

而cookies存在在两个位置,一个写在form表单,一个写在cookies中

在form表单中的csrf

在cookies中的csrf

'django.middleware.csrf.CsrfViewMiddleware',csrf中间是全站生效的,如果注释掉就是全站都不使用,局部使用需要通过装饰器

局部csrf

  • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  • @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

不过用render_to_response也是可以的

from django.template.context import RequestContext
# Create your views here.


def test(request):

    if request.method == 'POST':
        print request.POST
        return HttpResponse('ok')
    return  render_to_response('app01/test.html',context_instance=RequestContext(request))

对于ajax请求

发送ajax请求的全局配置,放到请求头中

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% csrf_token %}

    <input type="button" onclick="Do();"  value="Do it"/>

    <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
    <script src="/static/plugin/jquery/jquery.cookie.js"></script>
    <script type="text/javascript">
        var csrftoken = $.cookie('csrftoken');

        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });
        function Do(){

            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:'POST',
                success:function(data){
                    console.log(data);
                }
            });

        }
    </script>
</body>
</html>

中间件原理

我们可以导入一下

from django.middleware.security import SecurityMiddleware

这是一个,有三个方法,__init__process_requestprocess_response,其中一个是请求的时候经过,而另一个是返回的时候经过的。我们也可以按照这个格式来进行定义中间件。

在1.9及之前的版本,都是直接继承object类即可,原理是直接创建Middleware对象,然后执行每一个方法,1.10版本需要继承MiddlewareMixin类,而是直接类的call方法

我们还使用/app/csrf/的url

test.py

from django.utils.deprecation import MiddlewareMixin


class test1Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request111'

     def process_response(self, request, response):
         print 'response111'
         return response


class test2Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request222'

     def process_response(self, request, response):
         print 'response222'
         return response


class test3Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request333'

     def process_response(self, request, response):
         print 'response333'
         return response

settings.py文件中添加这些中间件

MIDDLEWARE = [
    'middleware_test.test.test1Middleware',
    'middleware_test.test.test2Middleware',
    'middleware_test.test.test3Middleware',
    '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',
]

views中

def csrf(request):
    print 'views'
    return render(request,'csrf.html')

目录结构如下图

request111
request222
request333
views
response333
response222
response111
[12/Oct/2017 01:08:02] "GET /app/csrf/ HTTP/1.1" 200 388

注意process_request方法中可以没有返回request,因为判断不用会直接返回对应的错误内容,但是process_response方法中一定要返回response,这是因为前一个中间件需要返回response进而实现返回数据,每一个中间件都进行return,进而返回给用户

不写的话会有以下情况

我在process_response写的是pass

然后测试一下如果在process_request进行返回后会怎样

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class test1Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request111'

     def process_response(self, request, response):
         print 'response111'
         return response


class test2Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request222'
         if True:
             return HttpResponse('request222 no pass')

     def process_response(self, request, response):
         print 'response222'
         return response


class test3Middleware(MiddlewareMixin):

     def process_request(self, request):
         print 'request333'

     def process_response(self, request, response):
         print 'response333'
         return response

控制台打印结果

request111
request222
response222
response111
[12/Oct/2017 00:54:13] "GET /app/csrf/ HTTP/1.1" 200 18

前端返回

request.META['REMOTE_ADDR']进行拦截

在1.9版本request return后,仍然会执行所有的responces,但是在1.10版本不是这个情况了,有待考证

流程是:

  1. 创建process_request等中间件执行顺序列表
  2. 读取配置文件settings.MIDDLEWARE_CLASSES
  3. 进行for class_path in settings.MIDDLEWARE_CLASSES
  4. 类 = __import__(class_path),然后创建类的对象
  5. 通过hasattr(对象, 'process_request')来判断是否有对应方法,对于请求过来的就正序添加到列表中,而对于返回的就倒序添加到列表中

源码在wsgi.py文件的from django.core.wsgi import get_wsgi_application的WSGIHandler()的class WSGIHandler(base.BaseHandler)的load_middleware方法

                if hasattr(mw_instance, 'process_request'):
                    self._request_middleware.append(mw_instance.process_request)
                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.append(mw_instance.process_view)
                if hasattr(mw_instance, 'process_template_response'):
                    self._template_response_middleware.insert(0, mw_instance.process_template_response)
                if hasattr(mw_instance, 'process_response'):
                    self._response_middleware.insert(0, mw_instance.process_response)
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.insert(0, mw_instance.process_exception)

每个中间件有5个方法,带0的是倒序添加

  • process_request = process_view
  • process_template_response
  • process_response
  • process_exception

分页

分页暂略

缓存

Django期初作为用于动态网站,每次请求的数据均会进行数据库操作,当程序访问量大的时候,耗时会很明显,所以提供了缓存的功能,将views的返回值进行缓存

Django内部提供了6中缓存方式

  1. 开发调试
  2. 内存
  3. 文件
  4. 数据库
  5. memcache缓存,通过python-memcached模块
  6. memcache缓存,通过pylibmc模块

配置

开发调试

# 此为开始调试用,实际内部不做任何操作
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
                'OPTIONS':{
                    'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
                    'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
                },
                'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
                'VERSION': 1,                                                 # 缓存key的版本(默认1)
                'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
            }
        }


    # 自定义key
    def default_key_func(key, key_prefix, version):
        """
        Default function to generate keys.

        Constructs the key used by all other methods. By default it prepends
        the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
        function with custom key making behavior.
        """
        return '%s:%s:%s' % (key_prefix, version, key)

    def get_key_func(key_func):
        """
        Function to decide which key function to use.

        Defaults to ``default_key_func``.
        """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
        return default_key_func

CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': os.path.join(BASE_DIR,'cache'),
            }
        }

内存

# 此缓存将内容保存至内存的变量中
# 配置:
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'unique-snowflake',
        }
    }

# 注:其他配置同开发调试版本

文件

# 此缓存将内容保存至文件
# 配置:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache',
        }
    }
# 注:其他配置同开发调试版本

数据库

# 此缓存将内容保存至数据库

# 配置:
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
            'LOCATION': 'my_cache_table', # 数据库表
        }
    }

# 注:执行创建表命令 python manage.py createcachetable

Memcache缓存(python-memcached模块)

# 此缓存使用python-memcached模块连接memcache

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': 'unix:/tmp/memcached.sock',
    }
}   

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11211',
        ]
    }
}

Memcache缓存(pylibmc模块)

# 此缓存使用pylibmc模块连接memcache

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '/tmp/memcached.sock',
    }
}   

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11211',
        ]
    }
}

应用

单独视图缓存

url

urlpatterns = [
    url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
    url(r'add/$', views.add),
    url(r'form1/$', views.form1),
    url(r'csrf/$', views.csrf),
    url(r'cache/$', views.cache),
]

views

import time

def cache(request):
    t = time.time()
    return render(request,'cache.html',{ 't':t })

第一次访问的时间

第二次访问的时间

这一看到这个时间是时间戳,会进行变化

进行缓存

import time
from django.views.decorators.cache import cache_page
@cache_page(10)
def cache(request):
    t = time.time()
    return render(request,'cache.html',{ 't':t })

@cache_page(10)代表缓存10s,注意需要from django.views.decorators.cache import cache_page

也可以写成这种方式@cache_page(60 * 15)

可以看到在10s内

from django.conf.urls import url, include
from . import views
from django.views.decorators.cache import cache_page

urlpatterns = [
    url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
    url(r'add/$', views.add),
    url(r'form1/$', views.form1),
    url(r'csrf/$', views.csrf),
    url(r'cache/$', cache_page(10)(views.cache)),
]

缓存的是二进制文件,两个分别是头部和内容信息

如果是数据库,需要进行创建cache表

python manage.py createcachetable

缓存表为cache_key,value和expires三个字段

局部视图缓存

以上是对单个视图缓存,也可以对局部视图进行缓存

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% load cache %}
    {{ t }}
    <hr />
    {% cache 10 arg_time %}
    {{ t }}
    {% endcache %}
</body>
</html>

第一次访问

10s内访问

全局缓存

缓存在中间件,写在中间件的最外部,因为在内部的response可能会做一些操作,而读取缓存的时候应该在最内部

使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    # 其他中间件...
    'django.middleware.cache.FetchFromCacheMiddleware',
]

以下是缓存参数

CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""

两个中间件

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    'middleware_test.test.test1Middleware',
    'middleware_test.test.test2Middleware',
    'middleware_test.test.test3Middleware',
    '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',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

CACHE_MIDDLEWARE_SECONDS = 10

更多参考官方文档

信号

内置信号

Model signals
    pre_init                    # django的modal执行其构造方法前,自动触发(就是在运行的时候执行的方法)
    post_init                   # django的modal执行其构造方法后,自动触发
    pre_save                    # django的modal对象保存前,自动触发
    post_save                   # django的modal对象保存后,自动触发
    pre_delete                  # django的modal对象删除前,自动触发
    post_delete                 # django的modal对象删除后,自动触发
    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
    pre_migrate                 # 执行migrate命令(生成数据库表)前,自动触发
    post_migrate                # 执行migrate命令后,自动触发
Request/response signals
    request_started             # 请求到来前,自动触发
    request_finished            # 请求结束后,自动触发
    got_request_exception       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发

信号需要写到代码会执行的位置,最简单的放到project的__init__.py

sender参数是触发者,可以获取一些字段进行拦截,例如封锁ip等等都可以实现,目前我只知道可以这么做

对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

__init__.py

from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception

from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate

from django.test.signals import setting_changed
from django.test.signals import template_rendered

from django.db.backends.signals import connection_created


def callback1(sender, **kwargs):
    print("pre_save_callback")
    print(sender,kwargs)

def callback2(sender, **kwargs):
    print("post_save_callback")
    print(sender,kwargs)

pre_save.connect(callback1)
post_save.connect(callback2)


def signals1(request):
    UserGroup.objects.create(caption='signals1')
    return HttpResponse('yes')

url

from django.conf.urls import url, include
from . import views
from django.views.decorators.cache import cache_page

urlpatterns = [
    url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
    url(r'add/$', views.add),
    url(r'form1/$', views.form1),
    url(r'csrf/$', views.csrf),
    url(r'cache/$', views.cache),
    url(r'signals1/$', views.signals1),
]

进行请求的时候返回值

request111
request222
request333
response333
response222
response111
[12/Oct/2017 23:58:21] "GET /app/signals1 HTTP/1.1" 301 0
request111
request222
request333
pre_save_callback
(<class 'app.models.UserGroup'>, {'update_fields': None, 'raw': False, 'signal': <django.db.models.signals.ModelSignal object at 0x000000000379F048>, 'using': 'default', 'instance': <UserGroup: signals1>})
post_save_callback
(<class 'app.models.UserGroup'>, {'update_fields': None, 'instance': <UserGroup: signals1>, 'signal': <django.db.models.signals.ModelSignal object at 0x000000000379F0B8>, 'created': True, 'raw': False, 'using': 'default'})
response333
response222
response111
[12/Oct/2017 23:58:21] "GET /app/signals1/ HTTP/1.1" 200 3

可以看到301跳转也是通过中间件的,也才发现,然后可以获取到的

也可以做以下操作

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

自定义信号

定义信号

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)

pizza_done.connect(callback)

触发信号

from 路径 import pizza_done

pizza_done.send(sender='seven',toppings=123, size=456)

由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。

https://docs.djangoproject.com/en/dev/topics/signals/

admin

django amdin是django提供的一个后台管理页面,改管理页面提供完善的html和css,使得你在通过Model创建完数据库表之后,就可以对数据进行增删改查,而使用django admin 则需要以下步骤:

  1. 创建后台管理员
  2. 配置url
  3. 注册和配置django admin后台管理页面

创建后台管理员

C:\pycode\mysite>python manage.py createsuperuser
Username (leave blank to use 'onlymr.why'): why
Email address: why@whysdomain.com
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Password:                    # 竟然输个123456都不行
Password (again):
pre_save_callback
(<class 'django.contrib.auth.models.User'>, {'update_fields': None, 'raw': False, 'signal': <django.db.models.signals.ModelSignal object at 0x00000000037B1FD0>, 'using': 'default', 'instance': <User: why>})
post_save_callback
(<class 'django.contrib.auth.models.User'>, {'update_fields': None, 'instance': <User: why>, 'signal': <django.db.models.signals.ModelSignal object at 0x00000000037C0080>, 'created': True, 'raw': False, 'using': 'def
ault'})
Superuser created successfully.

配置url

在project的url中配置url(r'^admin/', include(admin.site.urls))

from django.contrib import admin

from django.conf.urls import url, include

from . import views

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
    url(r'^app',include('app.urls')),
    url(r'^index/$', views.index, name='home'),
    url(r'^csrf/$', views.csrf),
]

注意需要引入from django.contrib import admin

访问admin可以看到登录界面

登录进去

注册和配置django admin后台管理页面

创建admin.py,目录结构如下所示

from django.contrib import admin

from app import  models

admin.site.register(models.Host)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.UserProfile)

再次访问admin

可以看一下host里边

都是我们以前创建的数据记录,这里显示的就是models里定义的主机名。

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

    def __unicode__(self):
        return self.hostname

可以点进去看一下

class Host(models.Model): hostname = models.CharField(max_length=64, verbose_name='主机名') ip = models.GenericIPAddressField(verbose_name='ip地址') user_group = models.ForeignKey('UserGroup')

class Meta:
    verbose_name = '主机列表'

def __unicode__(self):
    return self.hostname

消除s

class Host(models.Model):
    hostname = models.CharField(max_length=64, verbose_name='主机名')
    ip = models.GenericIPAddressField(verbose_name='ip地址')
    user_group = models.ForeignKey('UserGroup')

    class Meta:
        verbose_name = '主机列表'
        verbose_name_plural = verbose_name

    def __unicode__(self):
        return self.hostname

不过主机列表只显示主机名一列

class HostAdmin(admin.ModelAdmin):
    list_display = ( 'hostname', 'ip', )


admin.site.register(models.Host, HostAdmin)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.UserProfile)

搜索功能

class HostAdmin(admin.ModelAdmin):
    list_display = ( 'hostname', 'ip', )
    search_fields = (  'hostname', 'ip', )

admin.site.register(models.Host, HostAdmin)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.UserProfile)

快速过滤

class HostAdmin(admin.ModelAdmin):
    list_display = ( 'hostname', 'ip', )
    search_fields = (  'hostname', 'ip', )
    list_filter = (  'hostname', 'ip', )

admin.site.register(models.Host, HostAdmin)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.UserProfile)

admin密码找回

C:\pycode\mysite>python manage.py shell
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:53:40) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='why')
>>> user.set_password('wanghongyu')
>>> user.save()
>>> quit()

file上传

通过open文件写入上传

views

def post_file(request):
    if request.method == 'POST':
        obj = request.FILES.get('f')
        print type(obj)
        print obj.name
        print obj.chunks()
    return render(request,'file.html')

url

urlpatterns = [
    url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
    url(r'add/$', views.add),
    url(r'form1/$', views.form1),
    url(r'csrf/$', views.csrf),
    url(r'cache/$', views.cache),
    url(r'signals1/$', views.signals1),
    url(r'file/$', views.post_file),
]

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/file/" method="post" enctype="multipart/form-data">
        <input type="file" name="f">
        <input type="submit" value="提交">
    </form>
</body>
</html>

提交Django架构.txt控制台的打印结果

<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
Django架构.txt                                           
<generator object chunks at 0x000000000431FC18>

<generator object chunks at 0x000000000431FC18>是一个生成器

进行文件保存

def post_file(request):
    if request.method == 'POST':
        obj = request.FILES.get('f')
        print type(obj)
        print obj.name
        print obj.chunks()
        f = open(obj.name, 'wb')
        for chunk in obj.chunks():
            # chunk为bytes类型
            f.write(chunk)
        f.close()
    return render(request,'file.html')

form上传

class FileForm(forms.Form):
    ExcelFile = forms.FileField()



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)


def UploadFile(request):
    uf = AssetForm.FileForm(request.POST,request.FILES)
    if uf.is_valid():
            upload = models.UploadFile()
            upload.userid = 1
            upload.file = uf.cleaned_data['ExcelFile']
            upload.save()

            print upload.file

查看python版本

python -m django --version