context_processors用于给Django模版添加context,例如django.template.context_processors.media用于添加MEDIA_URL,django.template.context_processors.static用于添加STATIC_URL。之所以使用context_processors, 是想全局添加某个变量,而不需要在每个View中添加。

查看django.template.context_processors.static的实现,就可以模仿着写自己的context_processors

1
2
3
4
5
6
def static(request):
"""
Adds static-related context variables to the context.

"""

return {'STATIC_URL': settings.STATIC_URL}

联系作者

在git操作时,需要输入完整命令比较麻烦,此时可以配置别名。

配置status的别名

git config –global alias.st status

配置check的别名

git config –global alias.ch check

联系作者

最近在看Django文档Making queries时看到descriptor, 发现描述器很强大.

Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and super(). They are used used throughout Python itself to implement the new style classes introduced in version 2.2. Descriptors simplify the underlying C-code and offer a flexible set of new tools for everyday Python programs.

但是我还是不懂为什么需要描述符,直到看了Python描述符(descriptor)解密, 才知道描述符的用途。按照视频中的讲解,在理解一遍,加深印象。

假设在做一个农产品销售系统,每个订单是一个产品,每个产品有description, weight, price三个字段

1
2
3
4
5
6
7
8
9
class LineItem(object):

def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price

def subtotal(self):
return self.weight * self.price

但这里存在一个问题,即weight可以为负的。

1
2
3
4
5
6
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.subtotal()
69.5
>>> raisins.weight = -20 #负值
>>> raisins.subtotal()
-139.0

这是一个严重的问题,亚马逊刚起步时就有这个问题。 在Jeff Bezos and Amazon: Birth of a Salesman里有描述。

传统的做法是添加getter和setter方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class LineItem(object):

def __init__(self, description, weight, price):
self.description = description
self.set_weight(weight)
self.price = price

def subtotal(self):
return self.get_weight() * self.price

def get_weight(self):
return self.__weight

def set_weight(self, value):
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0')


if __name__ == "__main__":
raisins = LineItem('Golden raisins', 10, 6.95)
print raisins.subtotal()
raisins.weight
print raisins.subtotal()

这种方法存在一些问题,

  • 之前可以使用raisins.weight, 现在不能使用
  • 和以前的代码不兼容,以前可以使用raisins.weight, 现在必须raisins.get_weight和set_weight

好在Python提供更好的解决办法, property是其中一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class LineItem(object):

def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price

def subtotal(self):
return self.weight * self.price

@property
def weight(self):
return self.__weight

@weight.setter
def weight(self, value):
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0')


if __name__ == "__main__":
raisins = LineItem('Golden raisins', 10, 6.95)
print raisins.subtotal()
print raisins.weight
print raisins.subtotal()
raisins.weight = -2.0

使用property存在一个问题是,当我们需要对price也做非0限制时,需要重复setter设置。此时Descriptor派上用场了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Quantity(object):
__counter = 0
def __init__(self):
prefix = '_' + self.__class__.__name__
key = self.__class__.__counter
self.target_name = '%s_%s' % (prefix, key)
self.__class__.__counter += 1

def __get__(self, instance, owner):
return getattr(instance, self.target_name)

def __set__(self, instance, value):
if value > 0:
setattr(instance, self.target_name, value)
else:
raise ValueError('value must be > 0')

class LineItem(object):
weight = Quantity()
price = Quantity()

def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price

def subtotal(self):
return self.weight * self.price


if __name__ == "__main__":
raisins = LineItem('Golden raisins', 10, 6.95)
print raisins.subtotal()
print raisins.weight
print raisins.subtotal()
raisins.weight = -2.0
raisins.price = -1

在Quantity类里, instance是指LineItem实例, owner指LineItem类。

具体查看视频,非常不错。

参考资料

联系作者

因为服务器上HTTP和HTTPS一起存在,所以分享接口这里出了一些问题。

在调用分享接口生成签名时,签名用的url必须是调用JS接口页面的完整URL,但因为存在http和https两种,而Django其实不知道客户端到底是访问http还是https,所以产生了问题。如果写死http, 则访问https时,微信分享签名出错;相反的,如果写死https, 则访问https时,微信分享签名会出错。有什么解决的办法?

查看request对象,知道scheme这个属性,于是看到SECURE_PROXY_SSL_HEADER设置。

大意就是当在settings里配置了SECURE_PROXY_SSL_HEADER,Django就会到request.META里读取相关参数,如果有设置https,则这是一个https请求。而相关参数需要代理服务器设置,我这里使用Nginx。

于是在Nginx配置里,当访问的是https时,就加上

1
proxy_set_header HTTP_X_FORWARDED_PROTO https;

然后在Django的settings配置里加上

1
SECURE_PROXY_SSL_HEADER = ('HTTP_HTTP_X_FORWARDED_PROTO', 'https')

这里之所以会多一个HTTP_是因为Django默认会给request.ME如此配置后,request.scheme就会返回http, 当请求是https时,则会返回https。分享的签名正确,问题得到解决。

联系作者

前几天同事推荐了djano-extensions,说是用来执行一些Django脚本特别爽,尝试之后,果然如此。

RunScript里有编写脚步的方法,有了它,之后写脚本时不需要导入Django环境,因为django-extensions帮你做了,很不错。

1
2
3
4
5
import os
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
django.setup()

联系作者

在PDF中需要添加签名信息,在网上找到
JSignPdf, 虽然两年多没更新了,但至少还能工作。

查看文档,发现美中不足的一点是只能一页一页的签名。

需要注意的一点是,图片必须是透明的,要不能签到PDF上会覆盖文字。

尝试之后,一个可行的签名如下

1
java -jar JSignPdf.jar -kp 111111 -ksf temp.pfx -llx 100 -lly 100 -urx 200 -ury 200 --bg-path Wechat2.png --l2-text '' -V 007_overview.pdf

参数的具体意义可以看文档。

联系作者