目录

    主要记录最近遇到的一些开发问题,解决方法。

    1. Python 中的序列化与反序列化

    序列化,将内存对象转化为可存储或传输序列的过程。反序列化,把序列化序列重新转化为内存对象的过程。Json 和 Pickle 是 Python 中常用的两个序列化处理模块。

    Json VS Pickle:

    • Json 实现的是内存对象与 Json 字符串的转换,Pickle 实现的是内存对象与字节对象的转换
    • Json 格式广泛应用于除 Python 外的其他领域,Pickle 是 Python 独有的
    • Json 只能序列化 Python 内置的基本数据类型对象,Pickle 可以序列化一切对象,包括函数
    • cPickle 是 Pickle 的 C 语言实现,常用来替换 Pickle 以提升性能

    使用示例:

    # -*- coding: utf-8 -*-
    import json
    import pickle
    
    
    obj = {'a': 'b', 'c': 'd'}
    ps = pickle.dumps(obj)
    print ps
    # "(dp0\nS'a'\np1\nS'b'\np2\nsS'c'\np3\nS'd'\np4\ns."
    js = json.dumps(obj)
    print js
    # {"a": "b", "c": "d"}
    print pickle.loads(ps)
    # {'a': 'b', 'c': 'd'}
    print json.loads(js)
    # {u'a': u'b', u'c': u'd'}
    

    除此之外,对字符串还可以进行压缩,以节省存储:

    import cPickle as pickle
    import zlib
    
    # 序列化,并压缩
    compressed = zlib.compress(pickle.dumps(obj))
    
    # 解压缩,反序列化
    obj = pickle.loads(zlib.decompress(compressed))
    

    2. Django CSRF

    django.middleware.csrf.CsrfViewMiddleware 处理逻辑:

    进入 views 函数处理之前,如果 cookies 里面有 csrf token,则将其设置在 request.Meta 中,否则生成一个 token。如果不是 GET、HEAD 等方法则,校验 CSRF。

    校验规则:从 Cookie 中取出 csrf token 与 POST 请求中的 csrfmiddlewaretoken 或者 HTTP_X_CSRFTOKEN 进行比较。如果两者相等,则通过校验,否则返回 403。

    在返回响应之前,如果设置过 CSRF_COOKIE_USED,则会将 csrf token 设置到 Cookie 中。通常有两种方式设置 CSRF_COOKIE_USED:

    • 直接使用 django.middleware.csrf.get_token 函数获取 csrf token
    • 配置 django.template.context_processors.csrf 上下文处理器,在模板里面渲染 csrf token,实际上还是调用了 get_token 函数

    下面是摘取的部分 Django 源码:

    class CsrfViewMiddleware(object):
        def process_view(self, request, callback, callback_args, callback_kwargs):
            try:
                csrf_token = _sanitize_token(
                    request.COOKIES[settings.CSRF_COOKIE_NAME])
                request.META['CSRF_COOKIE'] = csrf_token
            except KeyError:
                request.META["CSRF_COOKIE"] = _get_new_csrf_key()
            if request.method == "POST":
                    request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
                if request_csrf_token == "":
                    request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
                if not constant_time_compare(request_csrf_token, csrf_token):
                    return self._reject(request, REASON_BAD_TOKEN)
    
        def process_response(self, request, response):
            if request.META.get("CSRF_COOKIE") is None:
                return response
    
            if not request.META.get("CSRF_COOKIE_USED", False):
                return response
            response.set_cookie(settings.CSRF_COOKIE_NAME,
                                request.META["CSRF_COOKIE"],
                                max_age=settings.CSRF_COOKIE_AGE,
                                domain=settings.CSRF_COOKIE_DOMAIN,
                                path=settings.CSRF_COOKIE_PATH,
                                secure=settings.CSRF_COOKIE_SECURE,
                                httponly=settings.CSRF_COOKIE_HTTPONLY
                                )
            return response
    

    参数解释:

    • max_age: cookie的生命长度,默认为None,浏览器关闭 cookie 立即失效
    • expires: cookie 过期时间时间点,默认为None,浏览器关闭 cookie 立即失效
    • path: Cookie 生效的路径,/ 表示根路径,根路径的cookie可以被任何url的页面访问
    • domain: 默认值为 None,设置该 Cookie 的网页所在的域名None
    • secure: 用来设置 Cookie 只在确保安全的请求中才会发送。当请求是 HTTPS 或者其他安全协议时,包含 secure 选项的 Cookie 才能被保存到浏览器或者发送至服务器。
    • httponly: 只能 http 协议传输,无法被 JavaScript 获取(不是绝对,底层抓包可以获取到也可以被覆盖)默认值 False。

    Django 中关于 CSRF 的默认配置:

    # Settings for CSRF cookie.
    CSRF_COOKIE_NAME = 'csrftoken'
    CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52
    CSRF_COOKIE_DOMAIN = None
    CSRF_COOKIE_PATH = '/'
    CSRF_COOKIE_SECURE = False
    CSRF_COOKIE_HTTPONLY = False
    

    3. djcelery_crontabschedule already exists 错误

    使用版本:

    • Django==1.8.3
    • celery==3.1.18
    • django-celery==3.1.16

    升级 django-celery==3.2.2 时,执行 $ python manage.py migrate,报错:

    File "/app/.heroku/python/lib/python2.7/site-packages/pymysql/err.py", line 115, in _check_mysql_exception
        raise InternalError(errno, errorvalue)
    django.db.utils.InternalError: (1050, u"Table 'djcelery_crontabschedule' already exists")
    

    在 django-celery 的 GitHub 更新日志中提到:

    在 3.1.17 更新版本之后,新增了 Django migrations。
    

    如果已经存在 djcelery_* 等表,则会导致执行 $python manage.py migrate 时报错。升级依赖的版本库时,需要注意版本的兼容性。

    4. 如何合并两个 fork 仓库

    查看本地远程源:

    $ git remote -v
    origin  https://github.com/yourname/celery.git (fetch)
    origin  https://github.com/yourname/celery.git (push)
    

    添加需要合并的远程源:

    $ git remote add upstream https://github.com/celery/celery.git
    

    查看本地远程源:

    $ git remote -v
    origin  https://github.com/yourname/celery.git (fetch)
    origin  https://github.com/yourname/celery.git (push)
    upstream        https://github.com/celery/celery.git (fetch)
    upstream        https://github.com/celery/celery.git (push)
    

    获取远程源的最新更新:

    $ git fetch upstream
    From https://github.com/celery/celery
     * [new branch]          var.ci                 -> upstream/var.ci
     * [new branch]          workhorse-pool         -> upstream/workhorse-pool
    

    合并远程 fork 分支到当前分支:

    $ git merge upstream/master
    

    5. 监控 Celery 的工具 - Flower

    Flower 是基于 Web 的 Celery 监控和管理工具。提供的功能有:

    • 查看 worker 状态和统计信息
    • 关闭和重启 worker 实例
    • 控制 worker 池的大小
    • 显示 task 详细信息
    • 查看当前运行的 task
    • 查看 tasks 的调度
    • 唤醒和终止tasks

    安装:

    $ pip install flower
    

    运行:

    $ python manage.py celery flower
    

    打开:http://localhost:5555,访问: