一、高级View和UrlController
RequestContext 和 Context Processors
使用render_to_reponse代替loader.get_tempalte(),上面的代码可以这样写
django提供全局的上下文处理器。settings.py中的TEMPLATE_CONTEXT_PROCESSOR指定哪个上下文处理器将会总是应用到RequestContext
默认的
django.core.context_processor.auth
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包含这些变量
user:django.contrib.auth.model.User的实例
message:一系列登录用户的信息,它会调用request.user.get_and_delete_message()
perms:一个django.core.context_processors.PermWrapper的实例,即登入用户的允许权利
django.core.context_processors.debug
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
debug:是否是debug模式
sql_queries:这样的列表{'sql':...,'time':...}
django.core.context_processors.i18n
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
LANGUAGES:language设置
LANGUAGE_CODE:即request.LANGUAGE_CODE
django.core.context_processors.request
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
request:当前的HttpRequest对象,默认不启用,需要激活
启用后,可以很容易低调用到ip
{{ request.REMOTE_ADDR}}
定制自己的上下文处理器
1.确保每个上下文处理器的功能最小化,这样就容易复用
2.settings.py中的TEMPALTE_CONTEXT_PROCESSORS是全局的,将会对所有的模版文件,注意不要冲突了
3.为了方便,也可以将处理器配置保存在自己另外配置的文件,如context_processors.py
自动HTML转义
使用缘由:在有根据用户输入然后展示的页面中,如果用户输入<script>alert("shit");</script>等会造成风险
解决方法:
1.使用escape过滤器
2.使用HTML转义(这在django的模版中是自动的)
关掉它
对于单独变量:加上safe过滤器,例如:{{ data|safe }}
对于模版块:
{{% autoescape off %}}
Hello {{ name }}
{{% endautoescape %}}
打开用
{{% autoescape on %}}
Hello {{ name }}
{{% endautoescape %}}
注意:可以嵌套使用
对于字符串转移:{{ data|default:"3 < 2" }}
加载模版
方式:
1.使用django.template.loader.get_template(template_name)
2.使用django.tempalte.laoder.select_tempalte(tempalte_name_list)
后者将选择模版名列表的第一个,如果不存在则抛出TempalteDoesNotExist异常
加载器(有3种)
1.django.template.loaders.filesystem.load_template_source这个是默认启用的。它会加载TEMPLATE_DIRS指定的目录下的模版
2.django.tempalte.laoders.app_directories.load_template_source这个也是默认启用的。它会加载INSTALLED_APPS指定应用下的目录。
例如INSTALLED_APPS包含('myproject.polls'),那么get_template(‘foo.html’)就会在下列目录寻找模版
/path/to/myproject/polls/templates/foo.html
3.django.template.loaders.eggs.load_template_source
扩展模版系统
1.创建模版库
(1)将应用配置添加到INSTALLED_APP
(2)创建一个templatetags目录到djano应用包,与models.py和views.py同级。创建两个空文件在templatetags目录:__init__.py(指定python这个包包含python代码和包含定制的tag/filter定义)、(标签名).py。这样就可以用了{% load 标签名 %},但是它只加载INSTALLED_APP中指定应用的模版库。为了验证标签库,模块必须包含一个模块级别的变量register
from django import template
register = template.Library()
2.写定制模版过滤器
定制过滤器包含1~2个参数
变量值
参数值
这样在模版就可以用了
{{ somevariable|cut:" "}}
如果没有参数,就移除arg
当写过滤器定义时候,需要用Library实例注册它,让它在django模版语言中可用
register.filter('cut', cut)
如果python2.4以上,支持注释,可以这样写
from django import template
register = template.Library()
@register.filter(name='cut')
def cut(value, arg):
return value.replace(arg, '')
3.写定制模版标签
标签比过滤器更加复杂,功能也更加强大
django编译一个模版,会拆分模版文本成为一个个节点,每个节点是一个django.template.Node实例,Node实例有个render()方法。当在一个编译的模版文件调用render(),就会调用Node的render()方法
例如要实现 <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
1.写编译方法
2.编写模版节点
3.注册标签
不指定name就会使用方法名
4.设置一个上下文的变量
改写上面的CurrentTimeNode
有时,为了防止冲突,标签需要使用别名
{% get_current_time "%Y-%M-%d:%I%M%p" as my_current_time%}
<p>The curent time is {{ my_current_time }}.</p>
解析直至另一个标签
{% comment %}的标准实现
解析知道另一个标签并且保存内容
简单标签的快捷方式
python2.4以上可以用注释
@register.simple_tag
def current_time(token):
#...
Inclusion标签
这种标签是在模版中展示数据用的比较多
写定制模版加载器
一个模版加载器是每一个TEMPLATE_LOADER的设置,是一个有下面接口的回调对象
load_tempalte_source(template_name, template_dirs=None)
二、 高级Model
使用django database api
1)访问外键
实体类Book有个属性publisher = models.Foreignkey(Publisher),那么可以这样访问:
from mysite.books.models import Book
b = Book.objects.get(id=50)
b.publiser.website
这样就可以访问Publisher实体类的website属性了
反过来,要从Publisher实体类访问Book实体类的属性,要使用 实体类小写_set 来访问
p = Publisher.objects.get(name='jerk-off')
p.book_set.all()
这样就可以访问所有book
p.book_set.filter(bookname='django')
2)访问多对多
Book实体类有个属性authors= models.ManyToManyField(Author)
b = Book.objects.get(id=50)
b.authors.all()
b.authors.filter(firstname='pan')
反过来
a = Author.objects.get(firstname='pan', lastname='jian')
a.book_set.all()
3)manager method
简而言之,管理器就是一个对象通过Django模型实现数据库查询。每个django至少有个管理器,可以定制一个个性化的数据库访问
增加额外的管理器方法
这样,就可以对Book使用title_count这个方法了,实际上manage method可以理解成给某个实体类添加的方法
Book.objects.title_count('django')
修改初始化查询结果集
DahManager是Manager的子类,因为get_query_set()返回查询结果集,所以可以用filter和exclude()等所有QuerySet方法
例如:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='pan')
Book.dahl_objects.count()
4)model method
model method允许定义自定义模型到一个模型去增加一个定制的低等级的函数到对象,可以将一些逻辑放到modellimian
5)原生sql
RequestContext 和 Context Processors
当渲染一个模版的时候,会用到django.template.Context的实例,但还有另外一个选择,django.template.RequestContext,它是Context的子类,它默认带上了一系列的变量到模版的context。
from django.template import loader, RequestContext
def custom_proc(request):
return {
'app': 'my app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR']
}
def view_1(request):
t = loader.get_template('template1.html')
c = RequestContext(request, {'message': 'template1 message'},
processors=[custom_proc])
return t.render(c)
def view_2(request):
t = loader.get_template('template2.html')
c = RequestContext(request, {'message': 'template2 message'},
processor=[custom_proc])
return t.render(c)
使用render_to_reponse代替loader.get_tempalte(),上面的代码可以这样写
from django.shortcuts import render_to_reposne
from django.template import RequestContext
def custom_proc(request):
return {
'app': 'my app',
'user': request.user
'ip_address': request.META['REMOTE_ADDR']
}
def view_1(request):
return_to_response('tempalte1.html',
{'message': 'template1 message'},
Context_instance=RequestContext(request, processors=[custom_proc]))
def view_2(request):
return_to_response('tempalte2.html',
{'message': 'template2 message'},
Context_instance=RequestContext(request, processors=[custom_proc]))
django提供全局的上下文处理器。settings.py中的TEMPLATE_CONTEXT_PROCESSOR指定哪个上下文处理器将会总是应用到RequestContext
默认的
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
)
django.core.context_processor.auth
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包含这些变量
user:django.contrib.auth.model.User的实例
message:一系列登录用户的信息,它会调用request.user.get_and_delete_message()
perms:一个django.core.context_processors.PermWrapper的实例,即登入用户的允许权利
django.core.context_processors.debug
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
debug:是否是debug模式
sql_queries:这样的列表{'sql':...,'time':...}
django.core.context_processors.i18n
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
LANGUAGES:language设置
LANGUAGE_CODE:即request.LANGUAGE_CODE
django.core.context_processors.request
如果TEMPLATE_CONTEXT_PROCESSORS中包含这个,那么RequestContxt就会包
request:当前的HttpRequest对象,默认不启用,需要激活
启用后,可以很容易低调用到ip
{{ request.REMOTE_ADDR}}
定制自己的上下文处理器
1.确保每个上下文处理器的功能最小化,这样就容易复用
2.settings.py中的TEMPALTE_CONTEXT_PROCESSORS是全局的,将会对所有的模版文件,注意不要冲突了
3.为了方便,也可以将处理器配置保存在自己另外配置的文件,如context_processors.py
自动HTML转义
使用缘由:在有根据用户输入然后展示的页面中,如果用户输入<script>alert("shit");</script>等会造成风险
解决方法:
1.使用escape过滤器
2.使用HTML转义(这在django的模版中是自动的)
关掉它
对于单独变量:加上safe过滤器,例如:{{ data|safe }}
对于模版块:
{{% autoescape off %}}
Hello {{ name }}
{{% endautoescape %}}
打开用
{{% autoescape on %}}
Hello {{ name }}
{{% endautoescape %}}
注意:可以嵌套使用
对于字符串转移:{{ data|default:"3 < 2" }}
加载模版
方式:
1.使用django.template.loader.get_template(template_name)
2.使用django.tempalte.laoder.select_tempalte(tempalte_name_list)
后者将选择模版名列表的第一个,如果不存在则抛出TempalteDoesNotExist异常
加载器(有3种)
1.django.template.loaders.filesystem.load_template_source这个是默认启用的。它会加载TEMPLATE_DIRS指定的目录下的模版
2.django.tempalte.laoders.app_directories.load_template_source这个也是默认启用的。它会加载INSTALLED_APPS指定应用下的目录。
例如INSTALLED_APPS包含('myproject.polls'),那么get_template(‘foo.html’)就会在下列目录寻找模版
/path/to/myproject/polls/templates/foo.html
3.django.template.loaders.eggs.load_template_source
扩展模版系统
1.创建模版库
(1)将应用配置添加到INSTALLED_APP
(2)创建一个templatetags目录到djano应用包,与models.py和views.py同级。创建两个空文件在templatetags目录:__init__.py(指定python这个包包含python代码和包含定制的tag/filter定义)、(标签名).py。这样就可以用了{% load 标签名 %},但是它只加载INSTALLED_APP中指定应用的模版库。为了验证标签库,模块必须包含一个模块级别的变量register
from django import template
register = template.Library()
2.写定制模版过滤器
定制过滤器包含1~2个参数
变量值
参数值
def cut(value, arg):
return value.repalce(arg, '')
这样在模版就可以用了
{{ somevariable|cut:" "}}
如果没有参数,就移除arg
当写过滤器定义时候,需要用Library实例注册它,让它在django模版语言中可用
register.filter('cut', cut)
如果python2.4以上,支持注释,可以这样写
from django import template
register = template.Library()
@register.filter(name='cut')
def cut(value, arg):
return value.replace(arg, '')
3.写定制模版标签
标签比过滤器更加复杂,功能也更加强大
django编译一个模版,会拆分模版文本成为一个个节点,每个节点是一个django.template.Node实例,Node实例有个render()方法。当在一个编译的模版文件调用render(),就会调用Node的render()方法
例如要实现 <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
1.写编译方法
from django import template
register = template.Library()
def do_current_time(parser, token):
try:
tag_name, format_string = token.split_content()
except ValueError:
msg = '%r tag requires a single argument' %token.split_contents()[0]
raise tempalte.TemplateSyntaxError(msg)
return CurrentTimeNode(format_string[1:-1])
2.编写模版节点
import datetime
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = str(format_string)
def render(self, context):
now = datetime.datetime.now()
return now.strftime(self.format_string)
3.注册标签
register.tag('current_time', do_current_time)
python高于2.4可以用注释
@register.tag(name="current_time")
def do_current_time(parser, token):
#...
@register.tag
def shout(parse, token):
#...
不指定name就会使用方法名
4.设置一个上下文的变量
改写上面的CurrentTimeNode
class CurrentTimeNode2(template.Node):
def __init__(self, format_string):
self.format_stirng = str(format_string)
def render(self, context):
now = datetine.datetime.now()
context['current_time'] = now.strftime(self.format_string)
return ''
注意必须返回哪怕是空字符串
有时,为了防止冲突,标签需要使用别名
{% get_current_time "%Y-%M-%d:%I%M%p" as my_current_time%}
<p>The curent time is {{ my_current_time }}.</p>
imoprt re
class CurrentTimeNode3(template.Node):
def __init__(self, format_string, var_name):
self.format_string = str(format_string)
self.var_name = var_name
def render(self, context):
now = datetime.datetime.now()
context[self.var_name] = now.strftime(self.format_string)
return ''
def do_current_time(parser, token):
try:
tag_name, arg = token.contents.split(Node, 1)
except ValueError:
msg = '%r tag requires arguments'% token.contents[0]
raise template.TemplateSyntaxError(msg)
m = re.search(r'(.*?) as (\w+)', arg)
if m:
fmt, var_name = m.groups()
else:
msg = '%r tag had invalid arguments' %tag_name
raise template.TemplateSyntaxError(msg)
if not (fmt[0] == fmt[-1] and fmt[0] in ('""', "'")):
msg = "%r tag's argument should be in quotes" %tag_name
raise template.TemplateSyntaxError(msg)
return CurrentTimeNode3(fmt[1:-1], var_name)
解析直至另一个标签
{% comment %}的标准实现
def do_comment(parser, token):
nodelist = parse.parse(('endcomment',))
parser.delete_first_token()
return CommentNode()
class CommentNode(template.Node):
def render(self, context):
return ''
解析知道另一个标签并且保存内容
{% upper %}
This will appear in uppercase, {{ user_name }}
{% endupper %}
def do_upper(parser, token):
nodelist = parser.parse(('endupper',))
parser.delete_first_token()
return UpperNode(nodelist)
class UpperNode(template.Node):
def __init__(self, nodeList):
self.nodelist = nodelist
def render(self, context):
output = self.nodelist.render(context)
return output.upper()
简单标签的快捷方式
def current_time(format_string):
try:
return datetime.datetime.now().strftime(str(format_string))
except UnicodeEncodeError:
return ''
register.simple_tag(current_time)
python2.4以上可以用注释
@register.simple_tag
def current_time(token):
#...
Inclusion标签
这种标签是在模版中展示数据用的比较多
{% books_for_author author %}
<ul>
{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
register.inclusion_tag('book_snippet.html')(books_for_author)
def books_for_author(author):
books = Book.objects.filter(authors_id = authod.id)
return {'books': books}
python2.4以上
@register.inclusion_tag('book_snippet.html')
def books_for_author(author):
#...
写定制模版加载器
一个模版加载器是每一个TEMPLATE_LOADER的设置,是一个有下面接口的回调对象
load_tempalte_source(template_name, template_dirs=None)
from django.conf import settings
from django.template import TemplateDoesNotExist
import zipfile
def load_template_source(template_name, template_dirs=None):
template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])
for fname in template_zipfiles:
try:
z = zipfile.ZipFile(fname)
source = z.read(template_name)
except (IOError, KeyError):
continue
z.close()
template_path = "%s:%s" %(fname, template_name)
return (source, template_path)
load_template_source.is_usable=True
二、 高级Model
连接数据库最基本例子
from django.shortcuts import render_to_response
import MySQLdb
def book_list(request):
db = MySQLdb.connect(user='root', db='first', passwd='123', host='localhost')
cursor = db.cursor()
cursor.execute('select name from books order by name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})
使用django database api
from django.shortcuts import render_to_respones
from mysite.books.models import Book
def book_list(request):
books = Book.objects.order_by('name')
return render_to_response('book_list.html', {'books': books})
1)访问外键
实体类Book有个属性publisher = models.Foreignkey(Publisher),那么可以这样访问:
from mysite.books.models import Book
b = Book.objects.get(id=50)
b.publiser.website
这样就可以访问Publisher实体类的website属性了
反过来,要从Publisher实体类访问Book实体类的属性,要使用 实体类小写_set 来访问
p = Publisher.objects.get(name='jerk-off')
p.book_set.all()
这样就可以访问所有book
p.book_set.filter(bookname='django')
2)访问多对多
Book实体类有个属性authors= models.ManyToManyField(Author)
b = Book.objects.get(id=50)
b.authors.all()
b.authors.filter(firstname='pan')
反过来
a = Author.objects.get(firstname='pan', lastname='jian')
a.book_set.all()
3)manager method
简而言之,管理器就是一个对象通过Django模型实现数据库查询。每个django至少有个管理器,可以定制一个个性化的数据库访问
增加额外的管理器方法
from django.db import models
class BookManager(models.Manger):
def title_count(self, keyword):
return self.filter(title)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
objects = BookManger()
def __unicode__(self):
return self.title
这样,就可以对Book使用title_count这个方法了,实际上manage method可以理解成给某个实体类添加的方法
Book.objects.title_count('django')
修改初始化查询结果集
from django.db import models
class DahManager(models.Manager):
def get_query_set(self):
return super(DahManager, self).get_query_set().filter(author='pan')
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
DahManager是Manager的子类,因为get_query_set()返回查询结果集,所以可以用filter和exclude()等所有QuerySet方法
例如:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='pan')
Book.dahl_objects.count()
4)model method
model method允许定义自定义模型到一个模型去增加一个定制的低等级的函数到对象,可以将一些逻辑放到modellimian
from django.contrib.localflavor.us.models import USStateField
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
state = USStateField() # Yes, this is US-centric...
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date \
and self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
return "Post-boomer"
def is_midwestern(self):
"Returns True if this person is from the Midwest."
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self):
"Returns the person's full name."
return u'%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
5)原生sql
from django.db import connenction
cursor = connection.cursor()
cursor.execute("""
select distinct first_name
from person
where firstname=%s""", ['pan'])
row=cursor.fetchone()
print row
可以将查询语句放到manager method中
from django.db import connection, models
class PersonManager(models.Manager):
def first_names(self, last_name):
cursor = connection.cursor()
cursor.execute("""
select distinct first_name
from person
where lastname=%s""", [last_name])
return [row[0] for row in cursor.fetchone()]
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
objects = PersonManager()