Django知识点

内容整理自自强学堂

数据迁移

导出的方法

比如我们有一个项目叫 mysite, 里面有一个 app 叫 blog ,我们想导出 blog 的所有数据

python manage.py dumpdata blog > blog_dump.json

数据导入,不需要指定 appname

python manage.py loaddata blog_dump.json

多数据库连用

每个app都可以单独设置一个数据库

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'db1': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'dbname1',
        'USER': 'your_db_user_name',
        'PASSWORD': 'yourpassword',
        "HOST": "localhost",
    },
    'db2': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'dbname2',
        'USER': 'your_db_user_name',
        'PASSWORD': 'yourpassword',
        "HOST": "localhost",
    },
}

# use multi-database in django
# add by WeizhongTu
DATABASE_ROUTERS = ['project_name.database_router.DatabaseAppsRouter']
DATABASE_APPS_MAPPING = {
    # example:
    #'app_name':'database_name',
    'app1': 'db1',
    'app2': 'db2',
}

使用指定的数据库来执行操作

在查询的语句后面用 using(dbname) 来指定要操作的数据库即可

# 查询
YourModel.objects.using('db1').all() 
或者 YourModel.objects.using('db2').all()

# 保存 或 删除
user_obj.save(using='new_users')
user_obj.delete(using='legacy_users')

多个数据库联用时数据导入导出

如果不是defalut(默认数据库)要在命令后边加 –database=数据库对应的settings.py中的名称 如: –database=db1 或 –database=db2

数据库同步(创建表)
python manage.py syncdb #同步默认的数据库,和原来的没有区别

#同步数据库 db1 (注意:不是数据库名是db1,是settings.py中的那个db1,不过你可以使这两个名称相同,容易使用)
python manage.py syncdb --database=db1
数据导出
python manage.py dumpdata app1 --database=db1 > app1_fixture.json
python manage.py dumpdata app2 --database=db2 > app2_fixture.json
python manage.py dumpdata auth > auth_fixture.json
数据库导入
python manage.py loaddata app1_fixture.json --database=db1
python manage.py loaddata app2_fixture.json --database=db2

Django 缓存系统 (调优)

缓存系统工作原理:

对于给定的网址,尝试从缓存中找到网址,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,一系列操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容

{
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}

也就是默认利用本地的内存来当缓存,速度很快。当然可能出来内存不够用的情况,其它的一些内建可用的 Backends 有

'django.core.cache.backends.db.DatabaseCache'
'django.core.cache.backends.dummy.DummyCache'
'django.core.cache.backends.filebased.FileBasedCache'
'django.core.cache.backends.locmem.LocMemCache'
'django.core.cache.backends.memcached.MemcachedCache'
'django.core.cache.backends.memcached.PyLibMCCache'

利用文件系统来缓存:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
        'TIMEOUT': 600,
        'OPTIONS': {
            'MAX_ENTRIES': 1000
        }
    }
}

利用数据库来缓存,利用命令创建相应的表:python manage.py createcachetable cache_table_name

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'cache_table_name',
        'TIMEOUT': 600,
        'OPTIONS': {
            'MAX_ENTRIES': 2000
        }
    }
}

一般流程

from django.shortcuts import render
def index(request):
    # 读取数据库等 并渲染到网页
    # 数据库获取的结果保存到 queryset 中
    return render(request, 'index.html', {'queryset':queryset})

使用chache之后

from django.shortcuts import render
from django.views.decorators.cache import cache_page

@cache_page(60 * 15) # 秒数,这里指缓存 15 分钟,不直接写900是为了提高可读性
def index(request):
    # 读取数据库等 并渲染到网页
    return render(request, 'index.html', {'queryset':queryset})

Memcached 是目前 Django 可用的最快的缓存,另外,Django 还可以共享缓存。

Django 生成静态页面 (调优)

如果网站的流量过大,每次访问时都动态生成,执行SQL语句,消耗大量服务器资源,这时候可以考虑生成静态页面。把静态页面存储到本地,下次就直接访问静态页面就好了。

from django.shortcuts import render
from django.template.loader import render_to_string
import os


def my_view(request):
    context = {'some_key': 'some_value'}

    static_html = '/path/to/static.html'
     #匹配静态文件
    if not os.path.exists(static_html):
        content = render_to_string('template.html', context)
        with open(static_html, 'w') as static_file:
            static_file.write(content)

    return render(request, static_html)

当用户访问时,如果判断没有静态页面就自动生成静态页面,然后返回静态文件,当文件存在的时候就不再次生成。

但是一般情况下都不需要生成静态页面,因为Django 有缓存功能,使用 Django Cache(缓存)就相当于把生成生成静态页面,而且还有自动更新的功能,比如30分钟刷新一下页面内容。

Django安全

Django表单用在模板中的时候我们会加一句 {% csrf_token %}

Django国际化

开启国际化的支持,需要在settings.py文件中设置

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.locale.LocaleMiddleware',
)


LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

LANGUAGES = (
    ('en', ('English')),
    ('zh-cn', ('中文简体')),
    ('zh-tw', ('中文繁體')),
)

#翻译文件所在目录,需要手工创建
LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "django.core.context_processors.i18n",
)

生成需要翻译的文件:

django-admin.py makemessages -l zh-cn
django-admin.py makemessages -l zh-tw

手工翻译 locale 中的文本后,我们需要编译一下,这样翻译才会生效

django-admin.py compilemessages

Django session

Django完全支持也匿名会话,简单说就是使用跨网页之间可以进行通讯,比如显示用户名,用户是否已经发表评论。session框架让你存储和获取访问者的数据信息,这些信息保存在服务器上(默认是数据库中),以 cookies 的方式发送和获取一个包含 session ID的值,并不是用cookies传递数据本身。

启用session

编辑settings.py中的一些配置

MIDDLEWARE_CLASSES 确保其中包含以下内容

'django.contrib.sessions.middleware.SessionMiddleware',

INSTALLED_APPS 是包含

'django.contrib.sessions',

这些是默认启用的。如果你不用的话,也可以关掉这个以节省一点服务器的开销。

提示:您也可以配置使用比如 cache 来存储 session

在视图中使用 session

request.session 可以在视图中任何地方使用,它类似于python中的字典
session 默认有效时间为两周,可以在 settings.py 中修改默认值。

# 创建或修改 session:
request.session[key] = value
# 获取 session:
request.session.get(key,default=None)
# 删除 session
del request.session[key] # 不存在时报错

一个不让用户评价两次的例子

from django.http import HttpResponse

def post_comment(request, new_comment):
    if request.session.get('has_commented', False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session['has_commented'] = True
    return HttpResponse('Thanks for your comment!')

一个简化登录的验证

def login(request):
    m = Member.objects.get(username=request.POST['username'])
    if m.password == request.POST['password']:
        request.session['member_id'] = m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")


def logout(request):
    try:
        del request.session['member_id']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

Django传递数据给JS

有时候我们想把一个 list 或者 dict 传递给 javascript,处理后显示到网页上,比如要用 js 进行可视化的数据。

错误的使用方法

  • view.py
from __future__ import unicode_literals
from django.shortcuts import render

def home(request):
    List = ['自强学堂', '渲染Json到模板']
    return render(request, 'home.html', {'List': List})
  • html
<script type="text/javascript">
    var List = {{ List }};
    alert(List);
</script>

如果直接这么做,传递到 js 的时候,网页的内容会被转义,得到的格式会报错。

访问时会得到 Uncaught SyntaxError: Unexpected token ILLEGAL

需要注意两点:

  1. views.py中返回的函数中的值要用 json.dumps()处理

  2. 在网页上要加一个 safe 过滤器。

正确的使用方法

  • view.py
# -*- coding: utf-8 -*-

from __future__ import unicode_literals

import json
from django.shortcuts import render

def home(request):
    List = ['自强学堂', '渲染Json到模板']
    Dict = {'site': '自强学堂', 'author': '涂伟忠'}
    return render(request, 'home.html', {
            'List': json.dumps(List),
            'Dict': json.dumps(Dict)
        })
  • html
//列表
var List = {{ List|safe }};
alert(List);
//字典
var Dict = {{ Dict|safe }};
alert(Dict);

Django遍历数组和字典的几种方法

遍历数组的三种方法

var List = {{ List|safe }};

//下面的代码把List的每一部分放到头部和尾部
$('#list').prepend(List[0]);
$('#list').append(List[1]);

console.log('--- 遍历 List 方法 1 ---')
for(i in List){
    console.log(List[i]);// i为索引
}

console.log('--- 遍历 List 方法 2 ---')
for (var i = List.length - 1; i >= 0; i--) {
    // 鼠标右键,审核元素,选择 console 可以看到输入的值。
    console.log(List[i]);
};

console.log('--- 同时遍历索引和内容,使用 jQuery.each() 方法 ---')
$.each(List, function(index, item){
    console.log(index+'   '+item);
});

遍历字典的方法和字典的取值

    // 字典
    var Dict = {{ Dict|safe }};
    console.log("--- 两种字典的取值方式  ---")
    console.log(Dict['site']);
    console.log(Dict.author);

    console.log("---  遍历字典  ---");
    for(i in Dict) {
        console.log(i + Dict[i]);//注意,此处 i 为键值
    }

Django使用Ajax

一个简单的例子,获取单个数值。

关键是jQuery的get方法

$.get('API',{'参数名1':参数值1,'参数名2':参数值2},function(结果值){
    $('接受返回数据的元素').html(结果值);
    $('接受返回数据的元素').val(结果值);
})
  • view.py
# coding:utf-8
from __future__ import unicode_literals
import json
from django.http import HttpResponse
from django.shortcuts import render

def Add(request):
    a = request.GET['a']
    b = request.GET['b']    
    a = int(a)
    b = int(b)
    return HttpResponse(str(a+b))
  • html
{% extends 'Base.html' %} {% block content %}
<p>输入两个数字</p>
<form action="/add/" method="get">
    a: <input type="text" id="a" name="a"><br>
    b: <input type="text" id="b" name="b"><br>
    <p>result: <span id="result"></span></p>
    <button type="button" id="sum">计算</button>
</form>
<script>
    $(document).ready(function(){
        $('#sum').click(function(){
            var a = $('#a').val();
            var b = $('#b').val();
            //使用很重要的jQuery的get方法
            $.get('/add/',{'a':a,'b':b},function(ret){
                $('#result').html(ret)
            })
        });
    });
</script>
<img src="/static/images/time.jpg" alt="" /  > {% endblock %}

较复杂的例子,传递列表和字典。

传递一个数组或字典到网页,由JS处理,再显示出来。

关键是jQuery的getJson方法

$.getJSON('API',function(结果值){
    //返回值 ret 在这里是一个字典
    $('#dict_result').append(ret.字典中的键名+'<br>');
    //也可以用ret['字典中的键名']
});

需要注意的是API可以这样来写,{% url ‘ajax-list’ %} ,用 urls.py 中的 name 来获取是一个更好的方法!

  • view.py
from django.http import HttpResponse,JsonResponse

def ajax_list(request):
    a = range(100)
    # return HttpResponse(json.dumps(a),content_type="application/json")
    #Django1.6以后的写法
    return JsonResponse(a,safe=False)

def ajax_dict(request):
    name_dict = {'twz':'Love python and Django','zqxt':'I am teaching Django'}
    #return HttpResponse(json.dumps(name_dict),content_type="application/json")
    #Django1.6以后的写法
    return JsonResponse(name_dict,safe=False)
  • html
<button id='list'>Ajax加载字典</button>
<p id="list_result"></p>

<button id='dict'>Ajax加载列表</button>
<p id="dict_result"></p>

<script>
    $(document).ready(function(){
        // 列表 list
        $('#list').click(function(){
            // $.getJSON('/ajax_list/',function(ret){
            $.getJSON('{% url 'ajax-list' %}',function(ret){
                //返回值 ret 在这里是一个列表
                for (var i = ret.length - 1; i >= 0; i--) {
                  // 把 ret 的每一项显示在网页上
                  $('#list_result').append(' ' + ret[i])
                };
            });
        });
        //字典dict
        $('#dict').click(function(){
            $.getJSON('/ajax_dict/',function(ret){
                //返回值 ret 在这里是一个字典
                $('#dict_result').append(ret.zqxt+'<br>');
                //也可以用ret['twz']
            });
        });     
    });
</script>

复杂字典的使用方法

  • view.py
def ajax_dict(request):
    name_dict = {'twz':'Love python and Django','zqxt':'I am teaching Django'}
    person_info_dict = [
    {"name":"xiaoming", "age":20},
    {"name":"tuweizhong", "age":24},
    {"name":"xiaoli", "age":33}
    ]

    #return HttpResponse(json.dumps(name_dict),content_type="application/json")
    #Django1.6以后的写法
    return JsonResponse(person_info_dict,safe=False)
  • html
<button id='dict'>Ajax加载字典</button>
<p id="dict_result"></p>

<script>
    $(document).ready(function(){
        //字典dict
        $('#dict').click(function(){
            $.getJSON('{% url 'ajax-dict' %}',function(ret){
                $.each(ret,function(i,item){
                    $('#list_result').append(i+'   '+item.name+'   '+item.age+'</br>');
                });
            });
        }); 
    });
</script>

更复杂的例子,Ajax加载图片。

图片是放置在与APP目录同层次的common_static文件夹的pics文件夹之中的。

  • html
<p>请输入</p>
<form action="/add/" method="get">
    color:
    <input type="text" id="color" name="color" value="red">
    <br> number:
    <input type="text" id="number" name="number" value="1">
    <br>
    <p>result: <span id='result'></span></p>
    <button type="button" id='sum'>提交</button>
    <img src="" alt="">
</form>
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    $("#sum").click(function() {
        var color = $("#color").val();
        var number = $("#number").val();

        $.get("{% url 'get-pic' %}", {
            'color': color,
            'number': number
        }, function(ret) {
            $('#result').html('') //清空前面的结果
            $.each(ret, function(index, item) {
                $('#result').append('<img src="/static/pics/' + item + '">');
            })
        })
    });
});
</script>
  • view.py
def get_pic(request):
    color = request.GET.get('color')
    number = request.GET.get('number')
    name = '{}_{}'.format(color, number)

    print "!"*100
    # 过滤出符合要求的图片,假设是以输入的开头的都返回
    result_list = filter(lambda x: x.startswith(name), PICS)

    print 'result_list', result_list

    return HttpResponse(
        json.dumps(result_list),
        content_type='application/json')

注意filter和format的使用

  • urls.py
from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    url(r'^$', 'tools.views.index', name='home'),
    url(r'^get_pic/$', 'tools.views.get_pic', name='get-pic'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
)

Django Ajax CSRF认证

CSRF通过伪装来自受信任用户的请求来利用受信任的网站

Django 中自带了 防止CSRF攻击的功能,但是一些新手不知道如何使用,给自己编程带来了麻烦。常常会出现下面django csrf token missing or incorrect的错误。

GET 请求不需要 CSRF 认证,POST 请求需要正确认证才能得到正确的返回结果。一般在POST表单中加入 {% csrf_token %}

<form method="POST" action="/post-url/">
    {% csrf_token %}

    <input name='zqxt' value="自强学堂学习Django技术">
</form>

使用Ajax调用的时候,就要麻烦一些。需要注意以下几点:

  • 在视图中使用 render (而不要使用 render_to_response)
  • 使用 jQuery 的 ajax 或者 post 之前 加入这个 js 代码:
jQuery(document).ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

更为优雅简洁的代码(不能写在 .js 中,要直接写在模板文件中):

  • csrf.js放在static文件夹里面
/*====================django ajax ======*/
jQuery(document).ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});
/*===============================django ajax end===*/
  • html

csrfmiddlewaretoken: ‘{{ csrf_token }}’

    <script src="/static/jquery.min.js"></script>
    <script>
    /*
          自强学堂 学习更多IT技术
      http://www.ziqiangxuetang.com
    */
    $('document').ready(function(){
      $.ajaxSetup({
          data: {
              csrfmiddlewaretoken: '{{ csrf_token }}'
          },
      });  
    })


    function select_drug() {
        var drug = document.forms[0].drug;
        var drugs = "";
        var i;
        for (i = 0; i < drug.length; i++) {
            if (drug[i].checked) {
                drugs = drugs + drug[i].value + " ";
            }
        }

        $.post('{% url "exam2014" %}', {
            'drugs': drugs
        }, function(ret) {
            $('#result').html(ret);
        })
    }
    </script>

Django Sitemap 站点地图

Django 中自带了 sitemap框架,用来生成 xml 文件
sitemap 很重要,可以用来通知搜索引擎页面的地址,页面的重要性,帮助站点得到比较好的收录

自强学堂参考链接

单元测试技术

Django中有完善的单元测试,我们可以对开发的每一个功能进行单元测试,这样只要运行一个命令 python manage.py test,就可以测试功能是否正常。

Python中的单元测试

关键格式

import unittest

#目标函数
def 测试函数(参数值1,参数值2,....):
    函数体

#编写测试类
class 测试类名(unittest.TestCase):
    def 测试函数名(self):
        self.assertEqual(测试函数(参数值1,参数值2,....),目标对比参数1)

    def 测试函数名1(self):
        self.assertEqual(测试函数(参数值1,参数值2,....),目标对比参数2)

    def 测试函数名2(self):
        self.assertEqual(测试函数(参数值1,参数值2,....),目标对比参数3)  

#执行测试单元
if __name__ == '__main__':
    unittest.main()

实例:测试一个除法功能

  • 编写测试实例
import unittest

def division_funtion(x, y):
    return x / y

class TestDivision(unittest.TestCase):
    def test_int(self):
        self.assertEqual(division_funtion(9, 3), 3)

    def test_int2(self):
        self.assertEqual(division_funtion(9, 4), 2.25)

    def test_float(self):
        self.assertEqual(division_funtion(4.2, 3), 1.4)

if __name__ == '__main__':
    unittest.main()
  • 运行测试效果

    有两个错误9/4=2!=2.25 以及4.2/3=1.4000000000000001 != 1.4

C:\Users\Tony\Desktop\VueTest>python UniteTest.py
.FF
======================================================================
FAIL: test_int1 (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "UniteTest.py", line 13, in test_int1
    self.assertEqual(division_function(9,4),2.25)
AssertionError: 2 != 2.25

======================================================================
FAIL: test_int2 (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "UniteTest.py", line 16, in test_int2
    self.assertEqual(division_function(4.2,3),1.4)
AssertionError: 1.4000000000000001 != 1.4

----------------------------------------------------------------------
Ran 3 tests in 0.002s

FAILED (failures=2)
  • 进行相应的修改

设置浮点运算,保留到小数点后六位。

def division_funtion(x, y):
    return round(float(x) / y, 6)
  • 输出结果

    完全没有错误了

C:\Users\Tony\Desktop\VueTest>python UniteTest.py
...
-------------------------------------------------
Ran 3 tests in 0.001s

OK

Django中的单元测试

简单测试例子

from django.test import TestCase
from myapp.models import Animal

class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

Django上下文渲染器

简介

有时候我们想把一个变量在多个模板之间共用,这时候就可以用 Django 上下文渲染器。

  • setting.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

这里的 context_processors 中放了一系列的渲染器,上下文渲染器其实就是函数返回字典,这些值可以用在模板中。
request函数就是在返回一个字典,每一个模板中都可以使用这个字典中提供的 request 变量。

比如 在template 中 获取当前访问的用户的用户名:

User Name: {{ request.user.username }}

动手写个上下文渲染器

  • 1、新建一个项目,基于 Django 1.9,我们新建了 zqxt 项目和 blog 这个应用。把 blog 这个app 加入到
    settings.py 中

  • 2、我们在 zqxt/zqxt/ 这个目录下(与settings.py 在一起)新建一个 context_processor.py

# -*- coding: utf-8 -*-

from django.conf import settings as original_settings

def settings(request):
    return {'settings': original_settings}


def ip_address(request):
    return {'ip_address': request.META['REMOTE_ADDR']}
  • 3、我们把新建的两个 上下文渲染器 加入到 settings.py 中:
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',

                'zqxt.context_processor.settings',
                'zqxt.context_processor.ip_address',
            ],
        },
    },
]
  • 4、修改 blog/views.py
from django.shortcuts import render

def index(reuqest):
    return render(reuqest, 'blog/index.html')

def columns(request):
    return render(request, 'blog/columns.html')
  • 5、新建两个模板文件,放在 zqxt/blog/template/blog/ 中

index.html

<h1>Blog Home Page</h1>

DEBUG: {{ settings.DEBUG }}

ip: {{ ip_address }}

columns.html

<h1>Blog Columns</h1>

DEBUG: {{ settings.DEBUG }}

ip: {{ ip_address }}
  • 6、修改 zqxt/urls.py
from django.conf.urls import include, url
from django.contrib import admin
from blog import views as blog_views

urlpatterns = [
    url(r'^blog_home/$', blog_views.index),
    url(r'^blog_columns/$', blog_views.columns),
    url(r'^admin/', include(admin.site.urls)),
]
  • 7、打开开发服务器并访问这两个网址就能够查看到相同的数据了

Django 通用视图

通用视图就是在view中创造类,利用类本身的方法和继承自父类的方法来完成相关的功能。

Base Views

django.views.generic.base.View

这个类是通用类的基类,其它类都是继承自这个类,一般不会用到这个类,感觉用函数更简单些。
在urls.py中使用类视图的时候都是调用它的 .as_view() 函数

# views.py
from django.http import HttpResponse
from django.views.generic import View

class MyView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Hello, World!')

# urls.py
from django.conf.urls import patterns, url

from myapp.views import MyView

urlpatterns = patterns('',
    url(r'^mine/$', MyView.as_view(), name='my-view'),
)

django.views.generic.base.TemplateView

在 get_context_data() 函数中,可以传一些 额外内容 到 模板

# views.py

from django.views.generic.base import TemplateView

from articles.models import Article

class HomePageView(TemplateView):

    template_name = "home.html"

    def get_context_data(self, **kwargs):
        context = super(HomePageView, self).get_context_data(**kwargs)
        context['latest_articles'] = Article.objects.all()[:5]
        return context


# urls.py

from django.conf.urls import patterns, url

from myapp.views import HomePageView

urlpatterns = patterns('',
    url(r'^$', HomePageView.as_view(), name='home'),
)

django.views.generic.base.RedirectView

用来进行跳转, 默认是永久重定向(301),可以直接在urls.py中使用,非常方便:

from django.conf.urls import patterns, url
from django.views.generic.base import RedirectView

urlpatterns = patterns('',
    url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
    url(r'^go-to-ziqiangxuetang/$', RedirectView.as_view(url='http://www.ziqiangxuetang.com',permant=False), name='go-to-zqxt'),
)

另外的用法,可以用来记录文章的点击次数

# views.py
from django.shortcuts import get_object_or_404
from django.views.generic.base import RedirectView

from articles.models import Article

class ArticleCounterRedirectView(RedirectView):

    url = ' # 要跳转的网址,
    # url 可以不给,用 pattern_name 和 get_redirect_url() 函数 来解析到要跳转的网址

    permanent = False #是否为永久重定向, 默认为 True
    query_string = True # 是否传递GET的参数到跳转网址,True时会传递,默认为 False
    pattern_name = 'article-detail' # 用来跳转的 URL, 看下面的 get_redirect_url() 函数


    # 如果url没有设定,此函数就会尝试用pattern_name和从网址中捕捉的参数来获取对应网址
    # 即 reverse(pattern_name, args) 得到相应的网址,
    # 在这个例子中是一个文章的点击数链接,点击后文章浏览次数加1,再跳转到真正的文章页面
    def get_redirect_url(self, *args, **kwargs):
         If url is not set, get_redirect_url() tries to reverse the pattern_name using what was captured in the URL (both named and unnamed groups are used).
        article = get_object_or_404(Article, pk=kwargs['pk'])
        article.update_counter() # 更新文章点击数,在models.py中实现
        return super(ArticleCounterRedirectView, self).get_redirect_url(*args, **kwargs)


# urls.py
from django.conf.urls import patterns, url
from django.views.generic.base import RedirectView

from article.views import ArticleCounterRedirectView, ArticleDetail

urlpatterns = patterns('',

    url(r'^counter/(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
    url(r'^details/(?P<pk>\d+)/$', ArticleDetail.as_view(), name='article-detail'),
)

Generic Display View

参考链接

django.views.generic.detail.DetailView

DetailView有以下方法

  1. dispatch()
    根据发送给view的请求类型分配post()方法或者get()方法,一般在默认情况下,带head的请求都是分配给get()方法处理的,但是如果你想向view发送带head的请求,而且在view处理请求的不是get()方法,可以重写get()方法。

  2. http_method_not_allowed()
    如果发送给view的方法是django不支持的,那么就会执行这个函数,它会返回django支持的http请求方法

  3. get_template_names()
    返回一个模板名字的列表,第一个找到的要渲染的目标模会被渲染

  4. get_slug_field()
    返回一个slug field的名字。然后slug会根据这个名字去查阅slug filed的值。

  5. get_queryset()
    返回一个QuerySet,包含了要展示在页面上的数据。

  6. get_object(queryset=None)
    返回单个对象,用于这个view将要展示的数据,如果返回的queryset是有效的,就用这个queryset,否则调用get_queryset()方法,去寻找一个pk_url_kwarg主键参数,如果有这个主键参数,就以此为基础去寻找相关的值,否则就用slug field方法去找相关的值

  7. get_context_object_name(obj)
    返回一个context variable name,包含页面要操作的数据。当 context_object_name没有被设置的时候,会自动使用model的小写作为 context_object_name,例如Article,article。

  8. get_context_data(**kwargs)
    返回一个上下文数据(context data),作为列表展示的对象。

  9. get()

  10. render_to_response()
    返回一个self.response_class对象,如果获得任何一个keyword参数,那么keyword参数就会被传递给self.response_class的构造函数。

自动生成当前时间,使用DetailView将额外的数据传输到页面

# views.py
from django.views.generic.detail import DetailView
from django.utils import timezone

from articles.models import Article

class ArticleDetailView(DetailView):

    model = Article # 要显示详情内容的类

    template_name = 'article_detail.html' 
    # 模板名称,默认为 应用名/类名_detail.html(即 app/modelname_detail.html)

    # 在 get_context_data() 函数中可以用于传递一些额外的内容到网页
    def get_context_data(self, **kwargs):
        context = super(ArticleDetailView, self).get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context


# urls.py
from django.conf.urls import url

from article.views import ArticleDetailView

urlpatterns = [
    url(r'^(?P<slug>[-_\w]+)/$', ArticleDetailView.as_view(), name='article-detail'),
]

article_detail.html文件新建在app下的templates文件夹里面

<h1>标题:{{ object.title }}</h1>
<p>内容:{{ object.content }}</p>
<p>发表人: {{ object.reporter }}</p>
<p>发表于: {{ object.pub_date|date }}</p>


<p>日期: {{ now|date }}</p>

django.views.generic.list.ListView

ListView有以下几种方法:

  1. dispatch()
  2. http_method_not_allowed()
  3. get_template_names()
  4. get_queryset()
  5. get_context_object_name()
  6. get_context_data()
  7. get()
  8. render_to_response()
# views.py
from django.views.generic.list import ListView
from django.utils import timezone

from articles.models import Article

class ArticleListView(ListView):

    model = Article

    def get_context_data(self, **kwargs):
        context = super(ArticleListView, self).get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context



# urls.py:

from django.conf.urls import url

from article.views import ArticleListView

urlpatterns = [
    url(r'^$', ArticleListView.as_view(), name='article-list'),
]
<h1>文章列表</h1>
<ul>
{% for article in object_list %}
    <li>{{ article.pub_date|date }} - {{ article.headline }}</li>
{% empty %}
    <li>抱歉,目前还没有文章。</li>
{% endfor %}
</ul>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值