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);