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>
這個包的下載地址在這里,點擊下載!
未完待續...