添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
REDASH_LOG_LEVEL: "INFO" REDASH_REDIS_URL: "redis://redis:6379/0" REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"

其中Server作为主容器,加载/opt/redash文件夹到./app文件夹。依赖于redis和Postgres两个存储容器。
Server中主要将npm、flask等等进程

worker:
    build: .
    command: scheduler
    volumes_from:
      - server
    depends_on:
      - server
    environment:
      PYTHONUNBUFFERED: 0
      REDASH_LOG_LEVEL: "INFO"
      REDASH_REDIS_URL: "redis://redis:6379/0"
      REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
      QUEUES: "queries,scheduled_queries,celery"
      WORKERS_COUNT: 2

Worker主要启动scheduler计划,各种数据库的定时调用计划等等都在worker中执行。

redis:
    image: redis:3.0-alpine
    restart: unless-stopped
postgres:
    image: postgres:9.5.6-alpine
    # The following turns the DB into less durable, but gains significant performance improvements for the tests run (x3
    # improvement on my personal machine). We should consider moving this into a dedicated Docker Compose configuration for
    # tests.
    command: "postgres -c fsync=off -c full_page_writes=off -c synchronous_commit=OFF"
    restart: unless-stopped

数据库采用redis和postgres。

redash在默认安装的时候,导入的是 requirement.txt 和 requirement_dev.txt , 这样只会安装几个基本的DataSource,所有的DataSource的列表包在requirement_all_ds.txt中,安装好之后就可以加载所有的datasouce了。

从安装脚本可以看到加载了所有的Requirement

这是一个法国人写的python的小抄,确实总结和整理的非常好,python的基本语法在这个上面基本上都有了,同时这个作者在网站上分享了非常多的相关内容。唯一美中不足的是法语的。 不过可以反映成英文慢慢的看

https://perso.limsi.fr/pointal/python:memento

访问之后看这个网站有很多的内容

python技术列表清单

比较好用的几个工具包含 notes来查看当前django的所有的标注和注释。同时扩展的模型类也比较好用。

django-extensions 这个 Django 包非常受欢迎,全是有用的工具,比如下面这些管理命令:

  • shell_plus 打开 Django 的管理 shell,这个 shell 已经自动导入了所有的数据库模型。在测试复杂的数据关系时,就不需要再从几个不同的应用里做导入操作了。
  • clean_pyc 删除项目目录下所有位置的 .pyc 文件
  • create_template_tags 在指定的应用下,创建模板标签的目录结构。
  • describe_form 输出模型的表单定义,可以粘贴到 forms.py 文件中。(需要注意的是,这种方法创建的是普通 Django 表单,而不是模型表单。)
  • notes 输出你项目里所有带 TODO、FIXME 等标记的注释。
  • Django-extensions 还包括几个有用的抽象基类,在定义模型时,它们能满足常见的模式。当你需要以下模型时,可以继承这些基类:

  • TimeStampedModel:这个模型的基类包含了 created 字段和 modified 字段,还有一个 save() 方法,在适当的场景下,该方法自动更新 created 和 modified 字段的值。
  • ActivatorModel:如果你的模型需要像 status、activate_date 和 deactivate_date 这样的字段,可以使用这个基类。它还自带了一个启用 .active() 和 .inactive() 查询集的 manager。
  • TitleDescriptionModel 和 TitleSlugDescriptionModel:这两个模型包括了 title 和 description 字段,其中 description 字段还包括 slug,它根据 title 字段自动产生。
  • django-environ

    用于配置当前django的各种不同环境下的配置,很好的解决了在开发和部署情况下面的配置不同。

    因子应用配置

    在 Django 项目的配置方面,django-environ 提供了符合 12 因子应用 方法论的管理方法。它是另外一些库的集合,包括 envparse 和 honcho 等。安装了 django-environ 之后,在项目的根目录创建一个 .env 文件,用这个文件去定义那些随环境不同而不同的变量,或者需要保密的变量。(比如 API 密钥,是否启用调试,数据库的 URL 等)

    然后,在项目的 settings.py 中引入 environ,并参考官方文档的例子设置好 environ.PATH() 和 environ.Env()。就可以通过 env('VARIABLE_NAME') 来获取 .env 文件中定义的变量值了。

    处理有限状态机:django-fsm

    django-fsm 给 Django 的模型添加了有限状态机的支持。如果你管理一个新闻网站,想用类似于“写作中”、“编辑中”、“已发布”来流转文章的状态,django-fsm 能帮你定义这些状态,还能管理状态变化的规则与限制。

    Django-fsm 为模型提供了 FSMField 字段,用来定义模型实例的状态。用 django-fsm 的 @transition 修饰符,可以定义状态变化的方法,并处理状态变化的任何副作用。

    虽然 django-fsm 文档很轻量,不过 Django 中的工作流(状态) 这篇 GitHub Gist 对有限状态机和 django-fsm 做了非常好的介绍。

    用户注册和认证:django-allauth

    django-allauth 是一个 Django 应用,它为用户注册、登录/注销、密码重置,还有第三方用户认证(比如 GitHub 或 Twitter)提供了视图、表单和 URL,支持邮件地址作为用户名的认证方式,而且有大量的文档记录。第一次用的时候,它的配置可能会让人有点晕头转向;请仔细阅读安装说明,在自定义你的配置时要专注,确保启用某个功能的所有配置都用对了。

    处理 Django REST 框架的用户认证:django-rest-auth

    如果 Django 开发中涉及到对外提供 API,你很可能用到了 Django REST Framework(DRF)。如果你在用 DRF,那么你应该试试 django-rest-auth,它提供了用户注册、登录/注销,密码重置和社交媒体认证的端点(是通过添加 django-allauth 的支持来实现的,这两个包协作得很好)。

    Django REST 框架的 API 可视化:django-rest-swagger

    Django REST Swagger 提供了一个功能丰富的用户界面,用来和 Django REST 框架的 API 交互。你只需要安装 Django REST Swagger,把它添加到 Django 项目的已安装应用中,然后在 urls.py 中添加 Swagger 的视图和 URL 模式就可以了,剩下的事情交给 API 的 docstring 处理。

    简化 Django 开发的八个 Python 包

    django中时间存储的是UTC格式,在项目中settings.py中配置的TIME_ZONE = 'Asia/Shanghai'并不能改变时间在数据库中的存储形式。

    USE_TZ = True 设置了全局是否使用TimeZone模式,如果FALSE的话就会全局使用统一的模式,但是这种在其他有时区的电脑上就会发生错误。

    可以看到当USE_TZ使用了True的时候,数据库实际上存储的时间是UTC时间,但是当外部获取的时候,django将所有的时间也使用UTC格式表示,这样就在各个不同的系统中可以获取准确的带时区的UTC时间,时间格式为: 2018-10-15T10:28:10.521988+08:00 这样的时间在外面显示的时候就可以更清楚了。

    使用这个流程,所以中间获取token不能使用老的获取token的方法。

    检测所有的rest接口,如果token失效就调用登陆获取微信凭证重新刷新token,然后刷新页面,或者跳转到首页两种方式都可以。
    当用户token超时的时候,再次调用登陆即可获取新的token。

    简单的处理可以使用用户的信息来作为密码,但是这样极不安全。只有用微信的方式最 安全,但是这样需要调用微信服务接口。

    安装依赖软件

    Python (2.7), PostgreSQL (9.3 or newer), Redis (2.8.3 or newer) and Node.js (v6 or newer)

        apt-get -y update
        # Base packages
        apt install -y python-pip python-dev nginx curl build-essential pwgen
        # Data sources dependencies:
        apt install -y libffi-dev libssl-dev libmysqlclient-dev libpq-dev freetds-dev libsasl2-dev
        # SAML dependency
        apt install -y xmlsec1
        # Storage servers
        apt install -y postgresql redis-server
        apt install -y supervisor
    

    安装node 和 npm

    apt-get install nodejs
    apt-get install npm
    apt-get install nodejs-legacy
    
    npm install
    npm run build
    

    配置env

    在redash目录下增加文件 .env

    export REDASH_LOG_LEVEL="INFO"
    export REDASH_REDIS_URL=redis://localhost:6379/0
    export REDASH_DATABASE_URL="postgresql:///redash"
    export REDASH_COOKIE_SECRET=5ceD36XZNRNyo5Y2lzG0MLwsrkYsjoOZ
    export REDASH_MAIL_SERVER="smtp.hansap.com"
    export REDASH_MAIL_PORT="25"
    export REDASH_MAIL_USE_TLS="false"
    export REDASH_MAIL_USE_SSL="false"
    export REDASH_MAIL_USERNAME="[email protected]"
    export REDASH_MAIL_PASSWORD="Fire@123"
    export REDASH_MAIL_DEFAULT_SENDER="[email protected]"
    export ERDASH_HOST="http://bi.hansap.com"
    

    检查设置:

    bin/run ./manage.py check_settings
    

    创建数据库

    #需要安装redispy
    pip install redispy  
    adduser --system --no-create-home --disabled-login --gecos "" redash
    sudo -u postgres createuser redash --no-superuser --no-createdb --no-createrole
    sudo -u postgres createdb redash --owner=redash
    sudo -u redash bin/run ./manage.py database create_tables
    

    使用root安装,如果用redash用户安装的话可能会造成无法在virtualenv下面安装的问题

    sudo -u postgres createuser root
    sudo -u postgres createdb redash --owner=root
    sudo -u redash bin/run ./manage.py database create_tables
    

    ubuntu 18 下配置补充

  • ubuntu 18 默认的posgtresql的版本较高,所以在requirement.txt文件中的psycopg版本最好改到2.2.7.5
  • ubuntu下requirement.txt的cryptography版本修改到2.2.2版本,否则会报错。
  • "access_token": "JbAornhQy4Qt5nsP1OZreZ4ZrEeyCf", "expires_in": 36000, "token_type": "Bearer", "scope": "read write groups", "refresh_token": "3Mb7hKGhTmn71jxeSqaIhUPk92Yics"

    2. 调用失败情况

    "code": 401, "desc": "Authentication credentials were not provided."

    3. 调用成功情况

    "code": 200, "desc": "get category list success", "data": [ "id": 1, "category_name": "活动", "category_color": "bg-success" "id": 2, "category_name": "课程", "category_color": "bg-info" "id": 3, "category_name": "咨询", "category_color": "bg-primary"

    utils.py

    from rest_framework.views import exception_handler
    from django.utils import six
    from rest_framework.response import Response
    from rest_framework.serializers import Serializer
    def custom_exception_handler(exc, context):
        # Call REST framework's default exception handler first,
        # to get the standard error response.
        response = exception_handler(exc, context)
        # Now add the HTTP status code to the response.
        if response is not None:
            response.data['code'] = response.status_code
            response.data['desc'] = response.data['detail']
            del response.data['detail']  # 删除detail字段
        return response
    class JsonResponse(Response):
        An HttpResponse that allows its data to be rendered into
        arbitrary media types.
        def __init__(self, data=None, code=None, desc=None,
                     status=None,
                     template_name=None, headers=None,
                     exception=False, content_type=None):
            Alters the init arguments slightly.
            For example, drop 'template_name', and instead use 'data'.
            Setting 'renderer' and 'media_type' will typically be deferred,
            For example being set automatically by the `APIView`.
            super(Response, self).__init__(None, status=status)
            if isinstance(data, Serializer):
                msg = (
                    'You passed a Serializer instance as data, but '
                    'probably meant to pass serialized `.data` or '
                    '`.error`. representation.'
                raise AssertionError(msg)
            self.data = {"code": code, "desc": desc, "data": data}
            self.template_name = template_name
            self.exception = exception
            self.content_type = content_type
            if headers:
                for name, value in six.iteritems(headers):
                    self[name] = value
    

    setting.py中的配置

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.IsAuthenticated',
        'EXCEPTION_HANDLER': (
            'scaffolding.utils.custom_exception_handler'
    

    调用中的配置:

    @api_view(['GET'])
    def category_list(request):
        查询当前用户可以看到的所有活动目录
        :param request:
        :return:
        if request.method == 'GET':
            category_list = [];
            if request.user.is_staff:
                category_list = TimeCategory.objects.all()
            else:
                # 后续可以给用户一些专用的category
                category_list = TimeCategory.objects.filter(id=2)
            serializer = CategoryListSerializer(category_list, many=True)
            return JsonResponse(data=serializer.data, code=status.HTTP_200_OK, desc="get category list success")
                            	

    参考页面:
    http://txjdsk.iteye.com/blog/1861210
    https://blog.csdn.net/educast/article/details/69230054
    https://www.jb51.net/article/84604.htm
    https://blog.csdn.net/WongRu1/article/details/80953314
    https://www.cnblogs.com/dong-xu/p/6075705.html

    oauth的token超时之后,用户登陆的session其实还在,只是调用ajax的时候由于token超时而无法获取相关的数据。

    解决方法是:

    $.ajaxSetup({
                beforeSend: function (xhr, settings) {
                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    xhr.setRequestHeader('Authorization', 'Bearer ' + $.cookie('access_token'));
                error: function (xhr,textStatus,errorThrown){
                    if (xhr.status == 401){
                        window.location.href = "/logout/"
    

    网上比较多的方式说是使用dataFilter来绑定,这种方法不是太适合当前模式的OAuth2。由于dataFilter需要ajax返回成功的时候才FIlter他的data数据,但是这种情况下的用户无权限实际上是返回的error。所以需要通过error来进行绑定。

    这里的token超时之后是直接跳转到logout,然后跳转到login待用户登陆。实际上还可以使用token refresh,这种后面可以研究一下怎么做。

    模态框问题

    modal 模态框中写事件要用a标签,不能用button标签,modal绑定了botton事件的关闭模态框,如果使用button来绑定事件的话,事件执行完毕之后会关闭模态框,使用a标签就不会。

    advanced-form使用指南

    datetime-picker

    <link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet">
    <script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
    <div class="form-group row">
        <label class="control-label col-sm-4">Auto Close</label>
        <div class="col-sm-8">
            <div class="input-group">
                <input type="text" class="form-control" placeholder="mm/dd/yyyy" id="datepicker-autoclose">
                <div class="input-group-append">
                    <span class="input-group-text"><i class="ti-calendar"></i></span>
            </div><!-- input-group -->
    jQuery('#datepicker-autoclose').datepicker({
        autoclose: true,
        todayHighlight: true
    

    touch-spin

    <link href="{% static 'plugins/bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.css' %}" rel="stylesheet"
              type="text/css"/>
    <script src="{% static 'plugins/bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.js' %}"
                type="text/javascript"></script>          
    <div class="col-md-8">
             <label class="control-label">星期</label>
              <input type="text" value="" name="day_of_week">
    $("input[name='day_of_week']").TouchSpin({
                initval: 1,
                min: 1,
                max: 7,
                buttondown_class: "btn btn-primary",
                buttonup_class: "btn btn-primary"
                            	

    curl -X POST -d "grant_type=password&username=pengjunjie&password=stella" -u"G65f6XQQ1FkqEm5LBMW4PEleBqQxfHdq9WPKjjaA:Pdf9nfta886KT2wGV9hJyN4tn37pSEGGHkXLpqcLYPvu8vzQkLAPBLbNXNyKy3MBHQCQT3vYA9Wniyg4bnFBqrqHJAChshz4WNQyFHOmByu4NA8S6Au6PgjUdWmbFrce" http://localhost:8000/token/

    使用 Django OAuth Toolkit 工具包

    注意:当前我使用的django 1.11版本,python3.6版本。如果直接使用pip安装django-oauth-toolkit,会默认去下载django2.0版本,所以这里我选择安装了django-oauth-toolkit==1.1.2版本,在这个版本下会默认使用本地的django1.11版本

    官网上的操作手册有两个,一个是直接使用的Tutorias,另外一个是django rest framework,最好还是去看django rest framwork的版本,这样结合得更紧密一些。

    使用了oauth toolkit,配置完成后,其实对整个django的侵入不算大,依然可以使用request.user来判断用户,这样节省了一个比较大的时间来写公共方法。这样也解决了如果需要绑定用户信息的话,如果不带token就没法使用postman的情况,方便用户长期使用。

    获取token

    curl -X POST -d "grant_type=password&username=pengjunjie&password=stella" -u"l3iNxSydVcHvTeKS9PZJgp2xKgWisaPS60STiWtq:dkyyvEo6rBn2HJRfQThC8Bi5uHVgD2dw8D9r2V5ZsXD8Zrqd0jbhaTXfwgFRZsDZnnLkw6uKra4lrkj6YmeoW8noWRFohMaVXlrADh5wpnNcRSru7prvIZRNwDMJhKMw" http://localhost:8000/token/
    

    在postman上配置如下

    ^o/ ^authorize/$ [name='authorize']
    ^o/ ^token/$ [name='token']
    ^o/ ^revoke_token/$ [name='revoke-token']
    ^o/ ^introspect/$ [name='introspect']
    ^o/ ^applications/$ [name='list']
    ^o/ ^applications/register/$ [name='register']
    ^o/ ^applications/(?P<pk>[\w-]+)/$ [name='detail']
    ^o/ ^applications/(?P<pk>[\w-]+)/delete/$ [name='delete']
    ^o/ ^applications/(?P<pk>[\w-]+)/update/$ [name='update']
    ^o/ ^authorized_tokens/$ [name='authorized-token-list']
    ^o/ ^authorized_tokens/(?P<pk>[\w-]+)/delete/$ [name='authorized-token-delete']
    

    上面就是自带的所有接口情况

      <input type="text" name="peoName"/>   <input type="text" name="peoName"/>   <input type="text" name="peoName"/>

    在后台处理中可以通过以下代码获取参数:

    peoNames = request.POST.getlist('peoName',[])
    获取到的peoNames是一个数组,遍历就可以得到所有元素的值,注意,request.POST的类型是QueryDict,和普通的Dict不同的是,如果使用request.POST.get方法,只能获得数组的最后一个元素,必须使用getlist才能获取整个数组,以Python列表的形式返回所请求键的数据。 若键不存在则返回空列表。 它保证了一定会返回某种形式的list。

    关于django的POST常见方法

    1.用post方法去取form表单的值

    在取值前,先得判断是否存在这个key

      if not request.POST.has_key(strName):
          return "" 
      if request.POST[strName]:
          return request.POST[strName] 
      else:
          return ""
    

    2.用post方法获取[]类型的数据

    常见的,例如,每行数据前面都带个checkbox的操作。这时候可能会选多个checkbox,传入到后台时,如果用request.POST[strname]获取,那么只能获取到一个值。用下面的方法,可以获取到多值

      if not request.POST.has_key(strName):
          return "" 
      if request.POST[strName]:
          return ','.join(request.POST.getlist(strName)) 
      else:
          return ""
                            	

    Django中的模型类维护了一对多和多对多的关系。在保存这些关联对象时,只要按照属性字段为其设置相对应的管理对象,然后调用save()方法即可。比如下面代码中,一个Blog对应多个Entry对象;多个Entry对应多个Auther对象:

    class Blog(models.Model):
    name=models.CharField(max_length=100)
    tagline=models.TextField()
    def unicode(self):
    return self.name
    class Author(models.Model):
    name=models.CharField(max_length=50)
    email=models.EmailField()
    def unicode(self):
    return self.name

    class Entry(models.Model):
    blog=models.ForeignKey(Blog)
    headline=models.CharField(max_length=255)
    body_text=models.TextField()
    pub_date=models.DateTimeField()
    mod_date=models.DateTimeField()
    authors=models.ManyToManyField(Author)
    n_comments=models.IntegerField()
    n_pingbacks=models.IntegerField()
    rating=models.IntegerField()

    def __unicode__(self):
        return self.headline
    

    现在我们开始保存Entry对象,先来看如何保存一对多关联对象:

    $ python manage.py shell
    Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
    win32
    Type “help”, “copyright”, “credits” or “license” for more information.
    (InteractiveConsole)

    from myapp.models import *
    import datetime
    e1=Entry(headline=‘Hello’,body_text='Hello World’,pub_date=datetime.datetime
    .now(),mod_date=datetime.datetime.now(),n_comments=1,n_pingbacks=1,rating=1) # 创建一个Entry对象
    b=Blog.objects.get(pk=1) # 取得一个已经存在的Blog对象
    e1.blog=b #设置这个Entry属于取得的Blog
    e1.save() # 保存这个Entry对象
    由于Blog和Entry之间是一对多关系,所以这里我们保存这个Entry的Blog属性时,只要将它的blog字段赋值即可。
    下面再来看看如何保存多对多关系:

    eric=Author.objects.create(name='Eric’,email='[email protected]’) # 用create方法创建一个Author对象
    e1.authors.add(eric) #把这个Author加入到Entry中
    e1.save() #保存这个Entry对象
    保存多对多关系时,需要通过create方法创建一个多对多管理的对象,这里是Author。由于一个Entry有多个Author,所以我们这里用到了add()方法,来把这个Author添加到Entry中。我们在使用create()、get()方法时都是使用了相应对象的objects这个对象。这里的objects是对象的管理器,它是Django为每个模型对象提供的默认管理器对象,需要注意的是,objects对象只能是类级别的对象,而不是实力级别的对象。也就是说如果你构造了一个模型对象的实例,是不能通过实例来获取objects的,如果这样使用会抛出异常。

    django默认开启了CSRF验证,在form的提交或者rest framework里面的POST的请求的时候,会开启CSRF的验证。

    如果用户没有登录的情况下,可以不需要使用csrf就调用,但是如果有用户信息的时候,会经过CSRF,在调用请求的时候可以看到相应的返回。

    这一篇blog讲的比较清楚 http://www.liujiangblog.com/course/django/179

    如果在javascript的代码中修改,需要增加

    // using jQuery
    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;
    var csrftoken = getCookie('csrftoken');
    function csrfSafeMethod(method) {
        // 这些HTTP方法不要求CSRF包含
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);