// 原生ajax,XMLHttpRequest
如下两函数功能同样。
function Ajax1()
{
$.ajax({
url:'index.html',
type:'GET',
data:{'p':123},
success:function(arg){
console.log(arg);
}
})
}
function Ajax2()
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
//状态‘4’表示将服务器的返回的数据所有接收完毕
console.log(xhr.responseText); //xhr.responseText表示服务器端返回的数据
}
}
xhr.open('GET','/index.html?p=123'). //GET请求只能将data数据写在url中
xhr.send(null)
}
而POST请求而只要改动如下
xhr.open('POST','/index.html')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
xhr.send('p=123')
//send中字符串参数是post请求,并且必须带请求头,不然django不会解析,即request.POST会无数据,
//可是request.body中能够查看到数据确实发过去了。jquery的ajax已经帮咱们封装好请求头了。而且可能有其余框架会指定要特有的请求头。
//注意,若前端使用Formdata封装数据(由于不须要头,jquery的ajax中也要作特殊处理),则不用请求头也可
HTTP请求头
首先理解一下HTTP协议
1.创建在tcp协议之上,属于应用层的协议
2.我把它理解为一个规范的传输数据格式,即请求和响应(请求头\r\n\r\n请求体、响应头\r\n\r\n响应体)
3.无状态,短连接(一次请求一次响应,而后断开连接)
经常使用请求头php
content-type :服务器端给客户端发送的数据类型
accept :服务器端告诉客户端要接收数据类型
User-agent
COOKIE
django的request.POST里面有值要求有
1.请求头要求:
若是请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值,django会去request.body中解析数据
2.数据格式要求:
即便请求头中的 Content-Type为 application/x-www-form-urlencoded。request.POST也不必定有值,还要request.body要知足name=alex&age=18&gender=男 这种格式
form表单提交默认的 Content-Type为 application/x-www-form-urlencoded
ajax能够序列化和定制请求头css
// 默认:(和form表单同样的头)
$.ajax({
url:...
type:POST,
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
})
// 状况二:(定制了请求头,Content-Type 再也不是 application/x-www-form-urlencoded)
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
})
# body有值;POST无
// 状况三:(序列化,数据格式不知足,并且头也不知足)
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
})
// body有值;POST无
// json.loads(request.body)
ajax上传文件
使用ajax文件上传,ajax配置必须带如下参数
1.processData:false
2.contentType:false
3.data必须是formData()html
// #avatar是文件上传按钮
$('#avatar').click(function () {
if (!$('#avatar-img').val()) {
// $('#avatar-img').val() 是文件路径名
// $('#avatar-img')[0].files[0] 是文件对象,后台从request.FILES里取出就是它
}
var formData = new FormData();
formData.append('csrfmiddlewaretoken', $('[name = "csrfmiddlewaretoken"]').val());
formData.append('img', $('#avatar-img')[0].files[0]);
$.ajax({
url: '/account/upload_avatar/',
type: 'post',
processData: false,
contentType: false,
data: formData,
dataType: 'json',
success: function (data) {
console.log(typeof(data), data);
}
})
});
jsonp跨域
同源策略是检测网页js脚本是否是向同一个域的url发送请求,ajax恰好受到同源策略限制,而script标签则没有。
而script标签的工做原理是向src指向的url发送GET请求,而后将请求到的数据包裹在标签里面。
因此咱们能够建立一个标签<script src=‘http://xxx.com/fuck'> <script>
例如:
<script> egon </script>
#return HttpResponse('egon')
可是,因为数据是直接被包裹在标签里面,至关于声明一个变量名,而js不支持因此报错
因此常见的作法是,本地声明一个回调函数,jsonp返回该函数包裹的跨域数据字符串。
例如:
HttpResponse("func({'a':1,'b':2})")
更深一层,咱们能够经过传递GET参数告诉服务器毁掉函数,实现动态的返回指定包裹函数
例如:127.0.0.1:9999?callback=myfunc前端
jquery版小技巧:不写JsonpCallback,这样作jquery会随机生成一串字符串做为callback函数名,这样jquery会直接将返回的数据取出做为success函数的参数
视图:
HttpResponse("%s('%s')"%(callback,data))
java
URL
设计
多个url对应一个视图
利用是‘子类’在前,‘父类’在后,多余的参数使用**kwargs接收node
url(r'^(?P<type>type_[1-3])', views.index),
url(r'^$', views.index),
在URL传递搜索条件
通常是以GET参数传递,这个了解一下也好python
# 该正则表示按标签或者日期或者种类过滤
url(r'^(?P<site>\w+)/(?P<condition>((tag)|(date)|(category))/(?P<val>\w+).html$',home.filter)
分发
url匹配有两种,一种是路径对视图,另外一种则是分发
- url分发的格式mysql
url('^yuan',([],None,None))
正则对应再也不是一个视图函数,而是一个元组。
元组的第一个元素是列表,里面有多个url,通常url对象里面是路径对视图,可是你若喜欢能够再嵌套分发
第二元素为App名,第三元素为名字空间
jquery
url参数编码
将空格等特殊字符编码,经常使用于处理GET传参的特别参数
from django.utils.http import urlquote
def parse_filter_kwargs(kwargs):
# kwargs: {'title':1,'price':12000}
# return: title=1&price=12000
l = []
for k, v in kwargs.items():
l.append('%s=%s' % (k, urlquote(v)))
return '&'.join(l)
反向生成url
视图
- CBV装饰器使用
要在CBV视图中使用咱们上面的check_login装饰器,有如下三种方式:
1. 加在CBV视图的get或post方法上
from django.utils.decorators import method_decorator
class HomeView(View):
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
@method_decorator(check_login)
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
2. 加在dispatch方法上,由于CBV中首先执行的就是dispatch方法,因此这么写至关于给get和post方法都加上了登陆校验。
from django.utils.decorators import method_decorator
class HomeView(View):
@method_decorator(check_login)
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
3. 直接加在视图类上,但method_decorator必须传 name 关键字参数,若是get方法和post方法都须要登陆校验的话就写两个装饰器。
from django.utils.decorators import method_decorator
@method_decorator(check_login, name="get")
@method_decorator(check_login, name="post")
class HomeView(View):
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
cookie与session
详细
# cookie
request.COOKIES.get('username111')
res = render(request,'index.html')
res.set_cookie('key',"value",max_age=10) # 设置cookie, N秒后失效
#session
x = request.session['xx']
request.session['xx'] = 1
request.session.set_expiry(value)
# * 若是value是个整数,session会在些秒数后失效。
# * 若是value是个datatime或timedelta,session就会在这个时间后失效。
# * 若是value是0,用户关闭浏览器session就会失效。
# * 若是value是None,session会依赖全局session失效策略。
自定义404页面
1.全局urls.py上
# 指明函数路径
handler404 = 'users.views.page_not_found'
2.编写函数
def page_not_found(request):
from django.shortcuts import render_to_response
response = render_to_response('404.html',{})
response.status_code = 404
return response
3.设置settings.py的DEBUG=false,ALLOW_HOSTS=['*']
4.注意:DEBUG=false以后,django的static静态文件处理会实现。因此,要跟处理media同样,本身编写url路由
url(r'^static/(?P<path>.*)$,serve,{"document_root":STATIC_ROOT})
ORM
详细
# django setting里面的database的初始化命令
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'imoocc',
'USER': 'imoocc',
'PASSWORD': 'imoocccom',
'HOST': '127.0.0.1',
'PORT': '',
'OPTIONS': {},
'init_command': 'SET storage_engine=INNODB,'
'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED, autocommit=1, names "utf8";',
}
}
字段
注意:定义数据库字段时,当心那些冲突的关键字,例如date, datetime这些就不要取了
- obj.get_xxx_display能够显示将原本数据库存储的数字按照choice的定义转换为相应字符串
status = models.IntegerField(choices=[(-1, '未处理'), (0, '待处理'), (1, '已处理')])
# obj.get_status_choice
- default参数能够传入一个无参函数,当对象建立时便会调用
send_time = models.DateTimeFIeld(default=datetime.now)
-一对一
和ForeignKey相似,可是多了一个不能重复的约束
OnetoOne经常使用于分表
即当一张表的某一些字段查询的比较频繁,另一些字段查询的不是特别频繁,把不怎么经常使用的字段 单独拿出来作成一张表 而后用过一对一关联起来,
好处是既保证数据都完整的保存下来,又能保证大部分的检索更快
- 多对多
注意:ManytoMany 一般设置在正向查询多的那边
两种方式混合定义多对多
# 若是有额外的字段的时候
tags=models.ManyToManyField(
to='Tag',
through = 'Article2Tag',
through_fields = ('article','tag'),
)
注意:使用此种方式建立多对多表的时候,没有 add() remove() 等方法
即建立时候就须要直接使用第三张表
models.Author2Book.objects.get(author_id=1,book_id=1).delete()
- 文件字段
文件字段都设置了upload_to参数,upload_to指向的字符串,若存在'/',则表示文件夹,若文件夹不存在则会自动建立
例如:upload_to = 'head'表示放在settings配置uploads文件下的head文件夹里面
也能够指向一个函数,该函数返回一个动态的文件夹路径
# 例如:为每一个用户建立一个指定的文件夹
def upload_to(instance, filename):
import uuid
filename = '%s-%s' % (str(uuid.uuid4())[:8], filename)
return '/'.join(['head', instance.username, filename])
这个参数可让django内部帮助咱们处理文件上传,只要将数据库的文件字段指向文件对象便可
UserInfo.objects.create(img=request.FILES)
# 或者:
username = form.cleaned_data['username']
headImg = form.cleaned_data['headImg']
#写入数据库
user = User()
user.username = username
user.headImg = headImg
user.save()
查询
QuerySet的查询时惰性的,即当咱们去执行all,filter,get等时是不会去执行sql的,只有当咱们调用查询结果集的时候才会真正执行sql
管理器自带api
- get()
返回model对象,若没有符合条件的对象,触发DoesNotExist异常
try:
aricle = Article.objects.get(pk=id)
except Article.DoesNotExist:
return render(request,'failure.html',{'reason':'没有找到对应的文章'})
- 跨表
‘__’能够在filter和values或者相关函数中使用,而且是双向的,只要存在关系便可
# 注:两种用法,能够作条件,也能够取值
BookInfo.object.filter(heroinfo__hgender__exact = False)
#取值
v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
- values与values_list
values 能够提取反向的关联对象
注意:反向查询的参数是小写的orm类名,能够看做是关联表id另外一种表示法
a = CategoryInfo.objects.values('articleinfo')
# a = CategoryInfo.objects.filter(articleinfo=1) #能够看做articleinfo__id
values 能够提取外键字段
注意:经过双下划线连接
ArticleInfo.objects.values('category__title') #即便是manytomany也行
- 复杂查询extra()函数
extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
select和select_params是一组,用于子查询
where和params是一组,用于附加过滤条件
tables表示要引入哪些新表(会有笛卡尔积),order_by表示数据条目按本表的哪一个字段排序
# 附加where条件
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# 先filter筛选出指定博客掉文章,再按指定年月过滤
article_list = modles.Article.objects.filter(blog=blog).extra(where=['strftime("%%Y-%%m",create_time)=%s'],params=[val,])
# 自定义一个“子查询”字段
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
# 查询语句对比:
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
"""
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
"""
- 统计函数 aggregate
没有groupby效果,而且查出来以后就不死queryset对象了,是一个字典。适用于没有groupby的统计
TableInfo.objects.aggregate(c=Count('category_id'))
- 统计聚合函数annotate
注意:annotate前面必须有个的value来指定group by后面的字段,默认是以本身的id分组
from django.db.models import Sum, Count
Comment.objects.values(‘article’).annotate(comment_count=Count(‘article’)).order_by(‘-comment_count)。
# select count(article) as c from Evainfo group by aricle ordey by c
# annotate前面的value指定group by后面的字段,默认是以本身的id分组
# 注:comment_count是自定义查询字典的key
annotate统计操做默认以发起表的id进行group by,配合反向查询很是好用
# 查询不一样种类对应的书籍数目
CategoryInfo.objects.filter(blog_id=1).values(c=Count('articleinfo')).values('title', 'c')
# 按日期归档
archive_list = models.Article.objects.filter(user=user).extra(
select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c")
# 标签,种类归档
category_list = models.Category.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
- 其余不经常使用
# like查询
filter(sname__contains='黄') #也适用于整型等其余类型,而且要配合比较运算符使用 等同于select * from table where sname like "黄%"
#字段排序
order_by('-price') #支持多字段排序 order_by('id','-price','title')
#判断查询集中是否有数据,返回bool值
exists()
#返回查询集的总条数
count()
# 去重
distinct()
# 排除指定条件,经常使用做不等于
exclude()
obj = Permission2Action.objects.filter().exclude(p__menu_isnull =True)
#根据字段排序,支持多字段排序
order_by('id','-price','title')
# 附:查看执行的sql原生语句
#obj.query
django提供api(get_object_or_404 与 get_list_or_404)
通常都会有这么一个需求,若查找不出指定对象,返回404页面
from django.http import Http404
try:
product = Product.objects.get(pk=1)
except MyModel.DoesNotExist:
raise Http404
get_object_or_404 会简化这个操做
from django.shortcuts import get_object_or_404
product = get_object_or_404(Product, pk=1)
原生查询
- Manger.extra()
经常使用于附加复杂条件,子查询
# 查询语句对比:
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
"""
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
"""
- Manager.raw()
注意,raw查询,查询结果必定要包含主键
Topic.objects.raw('select * form forum_topic')
# 异常,由于返回的结果没有包含主键
blog_article.objects.raw(‘select distinct date_format(r‘blog_article’.’date_publist’,’%Y-%m’) from blog_article’)
- DB-API
直接使用了MySQLbd的api,使用原生语句查询 )
from django.db import connection, transaction
cursor = connection.cursor()
# cursor = connections['default'].cursor()
# 数据修改操做——提交要求
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
transaction.commit_unless_managed()
# 数据检索操做,不须要提交
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
连表查询
注意的是:selected_related不支持ManytoMany,而prefetch_related支持全部关系

用于查询的对象
Q对象
from django.db.models import Q
Q(Q(account='fuck_man') & Q(status=0) | Q(birthday_lte=ctime))
# 多个Q对象组装复杂条件
q = Q()
q1 = Q()
q1.connector = 'AND'
q1.children.append('account', 'fuck_man')
q1.children.append('status', 0)
q1.children.append('birthday_lte', ctime)
q2 = Q()
q2.connector = 'AND'
q2.children.append('title', 'fuck')
q.add(q1, 'OR')
q.add(q2, 'OR')
- filter 的 != 条件应该使用Q查询
from django.db.models.query import Q
queryset = UserInfo.objects.filter(~Q(name=""))
- 条件的“或”操做- 动态实现Q条件
好比简单的搜索功能(搜索一个文章的标题或内容或做者名称包含某个关键字):
import operator
from django.db.models.query import Q
# 用户要查询的值
q = request.GET.get('q', '').strip()
# 可能的查询键值列表
key_list = ['title__contains', 'content__contains','author__name__contains']
# 实现Q(title__contains=q)|Q(content__contains=q)|Q(author__name__contains=q)
l = [Q(**{key:q}) for key in key_list]
#reduce至关于c++的accumulate,累加函数, operator.or_(x,y)是二进制的'或'操做, 从列表里将全部Q对象一块儿'或'起来,获得最终的结果
queryset = Entry.objects.filter(reduce(operator.or_, l))
F对象
- 字段间比较
例如:查询书id小于价格的书籍
models.Book.objects.filter(id__lt=F("price"))
- django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操做
models.Book.objects.filter(id__lt=F("price")/2)
- 将对象的一个字段通过修改(加减乘除),赋值给另外一个字段,以此进行更新
models.Book.objects.all().update(price=F("price")+30)
序列化
一、serializers
from django.core import serializers
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)
二、json.dumps
import json
#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
ret=list(ret)
result = json.dumps(ret)
因为json.dumps时没法处理datetime日期,因此能够经过自定义处理器来作扩展,如:
import json
from datetime import date
from datetime import datetime
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, date):
return o.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, field)
# ds = json.dumps(d, cls=JsonCustomEncoder)
自定义Manager
class MyCustomManager(models.Manager):
# 链接查询时也生效
use_for_related_fields = True
def get_queryset(self):
return super(MyCustomManager, self).get_queryset().filter(isDelete=False)
# 配置 use_for_related_fields = True,必定要写在外面。不知道是否是bug,必定要再类的外面的实例化
custom_manager = MyCustomManager()
class BaseModel(models.Model):
create_time = models.DateTimeField(verbose_name='建立时间', auto_now=True)
isDelete = models.BooleanField(verbose_name='是否已经删除', default=False)
default_objects = models.Manager() # objects = BaseManger会被覆盖
objects = custom_manager
class Meta:
abstract = True
def delete(self, using=None, keep_parents=False):
"""重写数据库删除方法实现逻辑删除"""
self.isDelete = True
self.save()
print('isDelete = false')
模型继承
详细
数据库事务
详细
资源共享问题
数据资源共享问题,须要借助返回值(受影响的行数)
row = Trouble.objects.filter(id=nid,status=1).update(**form.cleaned_data)
if not row:
return HttpResponse('来晚一步,被人抢走了')
# 须要考虑多个用户抢同一个资源的问题,update返回的是数字,若返回是0,表示单子被抢走了。
else:
return redirect('/backend/trouble-list.html')
分库
分库的方法
- 手动路由
# 查询,使用using函数,参数就是要查询的数据库
User.objects.using('user1').all()
# 保存或者更新,使用save的using参数,值就是要使用的数据库
my_object.save(using='user1')
#删除,使用delete的using参数
user_obj.delete(using='user1')
- 自动路由
详细
分库的思路
- 垂直分库
即一个app对应一个数据库,上面自动路由的例子就是一个垂直分库的例子,auth1使用user1数据库,auth2使用user2数据库。固然也可使用手动路由。
- 水平分库
水平分库建议使用手动路由,由于每一个model的分库机制可能都不同,自动路由实现起来有些麻烦会形成性能不高,而手动路由,每一个model根据本身的规则来得到不一样的数据库。
模板
- 注意点
1. 函数在模版里面不能加括号
2. 模版里面统计反向集合数据条数 要用{{ article.commentinfo_set.count }} #{{article.commentinfo_set|length}}不行
3. 默认request对象已经传入模版里面,例如:
{% if request.session.user|default:'' != '' %}
4.
在模版语言中,变量类型不会由于输出语句,例如{{name}},而改变类型,好比说,int类型仍是int类型,不回变为字符串
-
模板html文件加载顺序
Django会依次到如下目录查找模板文件,若是都找不到,则报错:
1.项目配置的模板目录
2.admin应用的templates模板目录
3.auth应用的templates模板目录
4. 应用自己的templates模板目录
内置过滤器
# 注意的是,模版的传入的参数都是为字符串,而且传参时都要带‘:’
{{courses | length}} #集合的长度
{{ time_obj|date:"Y-m-d H:i:s"}} # 对传入转为日期时间格式
{{ str_obj|truncatewords:"30" }} # 截取前三十个字符,没法截取中文
{{str_obj| slice:"30"}} #截取中文
{{ my_list|first|upper }} # 把第一个字符大写
{{ article.summary |default:'' }} #这个能够保障模版不输出None,由于从数据库取出的值若没有则为None
{{ name|lower }} # 把传入值小写
{{ article.date_publish | date:"Y-m-d" }}
{{html_str|safe}} #让html_str给浏览器解析,默认出于安全考虑不解析
{% autoescape off %} #批量转义
{{ course.detail }}
{% endautoescape %}
注:还有另外一种方式可让浏览器解析,
from django.utils.safestring import mark_safe
html_str=mark_safe(html_str)
- 循环计数
{% for row in v1 %} # 假设3条数据
{{ forloop.counter}} # 依次为:一、二、3
{{ forloop.counter0}} # 依次为:0、一、2
{{ forloop.revcounter}} # 依次为:三、二、1
{{ forloop.revcounter0}} # 依次为:二、一、0
{{ forloop.last }} # 是否最后一个,是为True,其余False
{{ forloop.first }} # 是否第一个
{{ forloop.parentloop }} # 嵌套循环,表示上层循环的上面的六个值别是多少。
{% endfor %}
自定义过滤器
from django import template
register = template.Library()
# 定义一个将日期中月份转换为大写的过滤器,如8转换为八
@register.filter
def month_to_upper(key):
return ['一','二','三','四','五','六','七','八','九','十','十一','十二'][key]
# 使用,模版文件要load一下才能使用,filter是filter.py文件的文件名
{%load filter%}
<div class='month'>{{article.date_publish | month_to_upper}}</div> # article.date_publish会被当作month_to_upper的参数
{{ cls_verbose_name |test:app_name }} # 若是过滤器有参数时,必定要肯定没有空格,即过滤器test后面必定要紧贴':'
# 配置
注意的是:必定要在指定App目录下创建templatetags目录,而且要在setting.py设置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'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',
],
'libraries': {
'my_tags': 'web.templatetags.filter', #my_tags为自定义键值, web.templatyetags.filter为路径 }
},
},
自定义simple_tag
过滤器最多只能带1个额外参数只能带1个额外参数,而自定义simple_tag能够带无数个
# 能够接收多个参数
@register.simple_tag
def create_filter_str(order, q, filter_dict, field, value=''):
d = copy.copy(filter_dict)
remove_previous_field(d, field)
d[field] = value
order_str = 'order=%s' % order
q_str = 'q=%s' % q
filter_str = parse_filter_kwargs(d)
return '?' + '&'.join([order_str, q_str, filter_str])
#调用
<a href="{% create_filter_str args.order args.q args.filter data_dict.field %}">所有</a>
#或者{% create_filter_str args.order args.q args.filter data_dict.field as filter_str%}
{{filter_str}}
页面分离与导入
传入参数通过计算渲染装饰器所指定的页面,这个页面能够被其余模板经过调用引入
@register.inclusion_tag('web/sub/common_bar.html')
def get_common_bar(blog):
tag_statistics = TagInfo.objects.filter(blog=blog). \
annotate(article_count=Count('articleinfo')).values('id', 'article_count', 'title')
category_statistics = CategoryInfo.objects.filter(blog=blog). \
annotate(article_count=Count('articleinfo')).values('id', 'article_count', 'title')
date_statistics = ArticleInfo.objects.filter(blog_id=1). \
extra(select={'create_date': 'date_format(time,"%%Y-%%m")'}). \
values('create_date').annotate(article_count=Count('id')).values('article_count', 'create_date')
# 粉丝数
fans_count = Star2Fans.objects.filter(star_user=blog.user).count()
# 关注数
star_count = Star2Fans.objects.filter(fans_user=blog.user).count()
# 模版只会根据这个函数返回的上下文进行渲染
return locals()
- 使用
{% get_common_bar blog %}
settings.py配置
多个setting文件
详细
静态文件与上传文件配置
# 原理就是url拼配成功后,截取剩下的路径去指定目录下查找
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
MEDIA_URL = '/uploads/'
MEDIA_ROOT = os.path.join(BASE_DIR,'uploads')
# 注意:upload_to 设置的路径是相对于MEDIA_ROOT下建立
# 模板中引用静态文件
<link href ="{% static 'css/base.css' %}" rel="stylesheet"}
#配置能访问upload的文件
from django.views.static import serve
from django.conf import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^uploads/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
]
#配置在模版中使用{{MEDIA_URL}} 表明 ‘/uploads/'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'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',
'django.template.context_processors.media',
],
日志配置
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]'
},
'simple': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
},
'collect': {
'format': '%(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'SF': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,根据文件大小自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M
'backupCount': 3, # 备份数为3 xx.log --> xx.log.1 --> xx.log.2 --> xx.log.3
'formatter': 'standard',
'encoding': 'utf-8',
},
'TF': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler', # 保存到文件,根据时间自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件
'backupCount': 3, # 备份数为3 xx.log --> xx.log.2018-08-23_00-00-00 --> xx.log.2018-08-24_00-00-00 --> ...
'when': 'D', # 天天一切, 可选值有S/秒 M/分 H/小时 D/天 W0-W6/周(0=周一) midnight/若是没指定时间就默认在午夜
'formatter': 'standard',
'encoding': 'utf-8',
},
'error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"), # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 50M
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
},
'collect': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M
'backupCount': 5,
'formatter': 'collect',
'encoding': "utf-8"
}
},
'loggers': {
'': { # 默认的logger应用以下配置
'handlers': ['SF', 'console', 'error'], # 上线以后能够把'console'移除
'level': 'DEBUG',
'propagate': True,
},
'collect': { # 名为 'collect'的logger还单独处理
'handlers': ['console', 'collect'],
'level': 'INFO',
}
},
}
# 用户使用logging.getLogger([name])获取logger实例,若是没有名字,返回logger层级中的根logger(root logger)
# django名字的logger多是默认终端的输出,未验证
logger = logging.getLogger(‘django’)
logger.info(“This is an error msg”)
sql查询语句显示配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
django内置组件
详细
字段
# 经常使用字段
# CharField
# IntegerField
# DeimalField
# DateField
# DateTimeField
# EmailField
# ChoiceField
# FileField
# RegexField
# GenericIPAddressField
from django import forms
from django.forms import fields
#forms的单选框
auto = forms.CharField(
label='一个月内自动登陆',
widget=widgets.CheckboxInput(attrs={'id': "auto", 'name': 'auto'}),
required=False,
)
例如:手机号码定义
phone = fields.CharField(
min_length=11,
max_length=12,
error_messages={'required': 'server:手机号码不能为空',
'min_length': 'server:号码必须为11位或者12位',
'max_length': 'server:号码必须为11位或者12位',
},
validators=[RegexValidator(r'^1[0-9]{10,11}$', '手机号码格式错误'), ],
label='电话号码',
widget=widgets.TextInput(attrs={'name': 'phone', 'placeholder': "请输入手机号码", \
'class': 'form-control', 'id': 'phone'}),
)
例如:根据model的choices字段生产选项组框
city = forms.ChoiceField(
choices =models.CityInfo.objects.all().values_list('id','name'),
# choices = [(1,"东莞"),(2,"广州"),(3,"深圳")],
label = "城市",
initial = 1,
widget = forms.widgets.Select
)
#注意:以上的作法,当数据库更新时,不会实时更新。这时候须要自定义form的构造方法
class MyForm(Form):
user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
验证顺序
先验证了min_length那些参数字段以后再执行clean_xx钩子函数,最后执行总体验证clean()
- 不管以前定义的min_length那些验证成功与否,都会执行clean_xx函数,因此在里面操做时应当使用request.POST,而不是self.clean_data。(由于当验证失败时会取不到值)
- 若是验证成功须要return self.clean_data['xx']回去,否则clean_data里面那操做过的那个字段会是None
#单一字段的验证,clean_xxx()
def clean_verify(self):
user_verify_code = self.request.POST.get('verify', '').upper()
verify_code = self.request.session.get('verify_code').upper()
if user_verify_code != verify_code:
raise ValidationError(message='验证码错误', code='invalid')
# 总体验证例子
def clean(self):
value_dict = self.clean_data
v1 = value_dict.get('username')
v2 = value_dict.get('user_id')
if v1!='root' and v2 !=1:
raise ValidationError('总体错误信息')
return self.cleaned_data
后续增长错误信息
# form.errors['username'] = ['用户名不存在或者密码错误', ]
form.add_error(field, error)
只读字段处理
#form.fields['title'].widget.attr['readonly']=True
#form.fields['title'].widget.attr.update(['disabled':'true'})
form.fields['title'].disabled = True
使用接口
form = UserForm(request.POST,request.FILE)
if form.is_valid():
form.cleaned_data #验证过的数据,字典形式,键是name属性
else:
# 错误数据,字典形式,键是name属性,不过它的__str__默认返回errors.as_ul(),也就是字符串。html的ul代码
form.errors
注意:使用字典参数做为自定义form的data参数,传入的的值会被本身定义的规则所验证。若不想验证,则传入参数为initial
form = TroubleMaker(initial={'title':'标题1','detail':'XXXX'})
模板中使用
errors内部格式
{'username:['错误信息1','错误信息2'],'password':['错误信息1,]}
取错误列表里面的第一个
{{ form.errors.username.0}}
在模板中显示Django’__all__’形式的错误,即总体错误信息
{{ form.non_field_errors }}
验证器
Django将验证器定义为一个可调用的对象,它接受一个值,并在不符合一些规则时抛出ValidationError
异常。
换句话说,知足以上定义的都是验证器,那咱们就能够欢快地来自定义验证器吧!
例如,这个验证器只容许偶数:
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError('%s is not an even number' % value)
你能够经过字段的validators
参数将它添加到模型字段中:
from django.db import models
class MyModel(models.Model):
even_field = models.IntegerField(validators=[validate_even])
因为值在验证器运行以前会转化为Python,你能够在表单上使用相同的验证器:
from django import forms
class MyForm(forms.Form):
even_field = forms.IntegerField(validators=[validate_even])
你也可使用带有 __call__()
方法的类,来实现更复杂或可配置的验证器。例如,RegexValidator
就用了这种技巧。
RegexValidator
class RegexValidator([regex=None, message=None, code=None, _inversematch=None, flags=0])[source]
regex
用于搜索提供的value的正则表达式,或者是预编译的正则表达式对象。一般在找不到匹配时抛出带有 message 和code的 ValidationError异常。这一标准行为能够经过设置inverse_match 为True来反转,这种状况下,若是找到匹配则抛出 ValidationError异常。一般它会匹配任何字符串(包括空字符串)。
message
验证失败时ValidationError所使用的错误信息。默认为"Enter a valid value"。
code
验证失败时ValidationError所使用的错误代码。默认为"invalid"。
inverse_match
New in Django 1.7.regex的匹配模式。默认为False。
flags
New in Django 1.7.编译正则表达式字符串regex时所用的标识。若是regex是预编译的正则表达式,而且覆写了flags,会产生TypeError异常。默认为 0
modelform
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='密码', widget=forms.PasswordInput(attrs={'class': 'form-control'}), \
max_length=32, min_length=8)
password2 = forms.CharField(label='重复密码', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
class Meta:
model = UserInfo
fields = ('email', 'username')
labels = {
'username': '用户名',
'email': '邮箱',
}
error_messages = {
'__all__': {},
'email': {
'required': '邮箱不能为空',
'invalid': '邮箱格式错误',
},
'username': {
'required': '用户名不能为空',
'invalid': '用户名格式错误..',
}
}
widgets = {
'username': widgets.TextInput(attrs={'class': 'form-control'}),
'email': widgets.TextInput(attrs={'class': 'form-control'})
}
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("两次密码输入不一致")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
视图中使用
# instance参数表示和data参数共同融合
form = form_cls(instance=obj, data=request.POST)
if form.is_valid():
obj = form.save() #存储对象,返回model对象
模板中使用
{%for field in form %}
{{ field.label }} # verbose_name
{{ field.name}} # modelinfo原始定义的字段名,例如price
{{ field }} # input组件
{{ field.errors.0 }} #错误信息
{%endfor%}
动态modelform
class FormClassFactory(object):
@classmethod
def from_admin(cls, admin):
class Meta:
model = admin.cls_info
fields = '__all__'
exclude = admin.readonly_fields
def __new__(cls, *args, **kwargs):
for field_name in cls.base_fields:
field_obj = cls.base_fields[field_name]
field_obj.widget.attrs.update({'class': 'form-control'}) # 传入组件属性
return ModelForm.__new__(cls)
form_cls = type('DynamicModelForm', (ModelForm,), {'Meta': Meta, '__new__': __new__})
return form_cls
缓存
from django.core.cache import cache
is_had_read = cache.get(article_id)
if not is_had_read:
article.read_num += 1
article.save()
cache.set(article_id, 1, 60 * 60 * 24 * 30) #一个月过时
中间件
详细
-中间件执行流程。
a. - 请求到来走wsgi,wsgi负责按照HTTP协议的规则解析请求成请求头
b. 先执行全部安装中间件的process_request
c. - 绕回去匹配url
d. 执行全部中间件的process_view #这里和process_request的区别是,这里已经找到了要执行的视图函数
e. -执行视图函数返回
f. 执行全部中间件的process_response
g. 若报错,执行全部的process_excepiton
h. 若用render返回,则执行全部的process_render方法
异常