内容整理自自强学堂
数据迁移
导出的方法
比如我们有一个项目叫 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
需要注意两点:
views.py中返回的函数中的值要用 json.dumps()处理
在网页上要加一个 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有以下方法
dispatch()
根据发送给view的请求类型分配post()方法或者get()方法,一般在默认情况下,带head的请求都是分配给get()方法处理的,但是如果你想向view发送带head的请求,而且在view处理请求的不是get()方法,可以重写get()方法。http_method_not_allowed()
如果发送给view的方法是django不支持的,那么就会执行这个函数,它会返回django支持的http请求方法get_template_names()
返回一个模板名字的列表,第一个找到的要渲染的目标模会被渲染get_slug_field()
返回一个slug field的名字。然后slug会根据这个名字去查阅slug filed的值。get_queryset()
返回一个QuerySet,包含了要展示在页面上的数据。get_object(queryset=None)
返回单个对象,用于这个view将要展示的数据,如果返回的queryset是有效的,就用这个queryset,否则调用get_queryset()方法,去寻找一个pk_url_kwarg主键参数,如果有这个主键参数,就以此为基础去寻找相关的值,否则就用slug field方法去找相关的值get_context_object_name(obj)
返回一个context variable name,包含页面要操作的数据。当 context_object_name没有被设置的时候,会自动使用model的小写作为 context_object_name,例如Article,article。get_context_data(**kwargs)
返回一个上下文数据(context data),作为列表展示的对象。get()
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有以下几种方法:
- dispatch()
- http_method_not_allowed()
- get_template_names()
- get_queryset()
- get_context_object_name()
- get_context_data()
- get()
- 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>