那些在django开发中遇到的坑
1. 关于csrf错误
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
django中自带了防止CSRF攻击的手段,在form表单的action属性中,GET时不需要CSRF认证,而POST时需要。
一般而言,有两种解决办法:
① 启用csrf认证
• 在settings.py中启用中间件django.middleware.csrf.CsrfViewMiddleware
• 在views.py的render_to_response中,用RequestContext代替默认的Context,如下:
return render_to_response('a.html', data, context_instance=RequestContext(request, processors=[my_fun]))
注:若想要重定向到一个action,则用HttpResponseRedirect
• 在模板文件中的 form 表单内添加 {% csrf_token %}
② 关闭csrf认证
• 注释掉django.middleware.csrf.CsrfViewMiddleware即可
2. 后台传列表或者字典给js函数
这里容易遇到两个难题,一是中文会显示成unicode形式,二是引号会被转义,使得js函数出错
① 无论是字典还是列表,传给js都可以用json来处理:
import json test_dict = { "weather": ["sun", "rainy", "windy"], "mood": ["happy", "sad"] } json_str = json.dumps(test_dict, ensure_ascii=False)
注:其中的ensure属性是为了解决中文编码问题
② 在django中有专门禁止转义的方式,只需在js函数用标签围住相关代码块即可:
<script> function test_fun() { {% autoescape off %} var json_obj = {{ dict_json }} {% endautoescape %} } </script>
3. 文件上传与下载
进行文件上传的时候我遇到很多错误,以下是简单的总结:
• 在form中方法必须问POST
• 在form中要加入:enctype="multipart/form-data"
• 后台用name属性接受前端的文件,以下为一种简单接受文件的写法:
myfile = request.FILES.get("file_name", None) if myfile != None: des_dir = "/home/me/path/filename" des_file = open(des_dir, 'wb+') for chunk in myfile.chunks(): des_file.write(chunk) des_file.close()
文件下载则相对容易很多,以excel为例:
def write_excel(): #返回未保存的workbook pass def main_process(request): response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename=test.xlsx' wb = write_excel() wb.save(response) return response
另外提供一个js获取文件后缀名的函数:
<!-- 假设file的id为upload_file --> <script> function getFileType { //这里是jquery的用法,自行去了解 var file = $('#upload_file').val(); return file.replace(/.+\./, ''); } <script>
4. python跨目录imprt模块
之前我写了一些模块,在别的目录下,想引用它们却一直出错,找了一些资料,总结了几个实用的Tips:
① 若被引用的模版在更低的目录中,如子目录(sub_dir),则需要在该子目录中建立__init__.py的空文件,则可以直接在文件中:
import sub_dir.model_name
② 若其在父目录,则需要:
import sys sys.path.append("..") import model_name
5. 后台获取前端select multiple的数据
前端代码如下:
<select mutiple="multiple" id="select"> <option value="0">0</option> ... </select> <!-- 隐藏的表单,用来传给后台 --> <input type="text" style="display:none" id="select_input "name="select_str"> <!-- js/jquery部分--> <script> $(document).ready(function(){ $('#select').change(function(){ var objs = $('#select').val(); var result = ""; for(var key in objs) result = result + "," + objs[key]; if(result == "") $('#select_input').val(""); else $('#select_input').val(result.substr(1)); }); }); </script>
后台只需要执行(若是GET方法):
select = request.GET.get("select_str", "")
注:有时候用到getElementsByName的时候,需要主要得到的是一个数组,因为html中名字可以出现多次
6. 善于使用logging模块
① django中在settings.py中有个LOGGING的配置
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, # 针对 DEBUG = True 的情况 }, 'formatters': { 'standard': { 'format': '%(levelname)s %(asctime)s %(pathname)s %(filename)s %(module)s %(funcName)s %(lineno)d: %(message)s' }, # 对日志信息进行格式化 # INFO 2016-09-03 16:25:20,067 /home/ubuntu/mysite/views.py views.py views get 29: some info... }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'formatter':'standard' }, 'file_handler': { 'level': 'DEBUG', 'class': 'logging.handlers.TimedRotatingFileHandler', 'filename': '/tmp/byod/byodadmin/byod.admin.log', 'formatter':'standard' }, # 用于文件输出 'console':{ 'level': 'INFO', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', 'formatter': 'standard' }, }, 'loggers': { 'django': { 'handlers' :['file_handler', 'console'], 'level':'DEBUG', 'propagate': True # 是否继承父类的log信息 }, # handlers 来自于上面的 handlers 定义的内容 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, } }
重要的是handlers和loggers,handler定义了log文件所在位置,而一个logger可以将多个handler加入其中,我们在代码中使用只需:
import logging logger = logging.getLogger("django") # 为loggers中定义的名称 logger.info("some info...")
② 不使用django中的配置,直接封装成一个函数:
def initRotateLog(log_file="/path/name.log" , log_name="" , level=logging.DEBUG, max_bytes=100*1024*1024 , backup_count = 5 ): handler = logging.handlers.RotatingFileHandler(log_file, max_bytes, backupCount) fmt = '...' #自己去查相关资料 formatter = logging.Formatter(fmt) handler.setFormatter(formatter) logger = logging.getLogger(log_name) logger.addHandler(handler) logger.setLevel(log_level) return logger
使用的时候直接这样用:
from ... import * #自己引入那个函数模块 logger = initRotate("/path/somepath/file.log") logger.info("lalala")
7. xlwt的坑
在将大量数据(超过10w)写入到excel中,xlwt让我痛苦不堪,因为xlwt只支持写到的记录为65535条,多了便需要新建一个sheet或者重新写一个文件,但是我还是想直接写在一页,这个时候,我发现了另外一个模块:openpyxl(官方文档)
怎么安装就不说了,很容易,这里介绍一下它的很常见的用法:
from openpyxl import load_workbook #读 from openpyxl import Workbook #写 #读的方法 workbook = load_workbook(file_name) sheet1 = workbook.active #正在活动的sheet #遍历得到每行第一个单元格的值 for row in sheet1.rows: cell = row[0].value #写的方法 workbook = Workbook() sheet1 = workbook.active sheet1.title = "Sheet1" #写入100行100列数据,全是1 for row_index in range(1, 101): for col_index in range(1, 101): sheet1.cell(row=row_index, column=col_index).value = 1 #写入文件 workbook.save(file_name)
8. 关于datetimepicker的使用
官方的datetimepicker貌似能够显示秒,但是那个秒不能用户操纵,于是我找了好久,找到一个包,里面有js和css等文件,还有一个demo.html的demo文件,效果如下:
插入一个示例代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>datetimepicker示例</title> <link type="text/css" href="css/jquery-ui-1.11.4.css" rel="stylesheet" /> <link type="text/css" href="css/jquery-ui-timepicker-addon.css" rel="stylesheet" /> <script type="text/javascript" src="js/jquery-1.11.1.js"></script> <script type="text/javascript" src="js/jquery-ui-1.11.4.js"></script> <script type="text/javascript" src="js/jquery-ui-timepicker-addon.js"></script> <script type="text/javascript" src="js/jquery-ui-timepicker-zh-CN.js"></script> </head> <body> <input type="text" id="time_test" class="ui_timepicker"> <script> jQuery(document).ready(function() { $(function() { $.datepicker.regional['zh-CN'] = { changeMonth: true, changeYear: true, clearText: '清除', clearStatus: '清除已选日期', closeText: '关闭', closeStatus: '不改变当前选择', prevText: '<上月', prevStatus: '显示上月', prevBigText: '<<', prevBigStatus: '显示上一年', nextText: '下月>', nextStatus: '显示下月', nextBigText: '>>', nextBigStatus: '显示下一年', currentText: '今天', currentStatus: '显示本月', monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], monthStatus: '选择月份', yearStatus: '选择年份', weekHeader: '周', weekStatus: '年内周次', dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], dayNamesMin: ['日', '一', '二', '三', '四', '五', '六'], dayStatus: '设置 DD 为一周起始', dateStatus: '选择 m月 d日, DD', dateFormat: 'yy-mm-dd', firstDay: 1, initStatus: '请选择日期', isRTL: false }; }); $(function() { $.datepicker.setDefaults($.datepicker.regional['zh-CN']); $(".ui_timepicker").prop("readonly", true).datetimepicker({ defaultDate: $('.ui_timepicker').val(), dateFormat: "yy-mm-dd", showSecond: true, timeFormat: 'HH:mm:ss', stepHour: 1, stepMinute: 1, stepSecond: 1, forceParse: true }); }); }); </script> </body> </html>
这个包的下载地址在这里,点击下载!
未完待续...
作者: vachester
出处:http://www.cnblogs.com/vachester/
邮箱:xcchester@gmail.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。