那些在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)
View Code

注:其中的ensure屬性是為了解決中文編碼問題

② 在django中有專門禁止轉義的方式,只需在js函數用標簽圍住相關代碼塊即可:

<script>
    function test_fun() {
        {% autoescape off %}
        var json_obj = {{ dict_json }}
        {% endautoescape %}
    }
</script>
View Code

 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()
View Code 

文件下載則相對容易很多,以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
View Code

另外提供一個js獲取文件后綴名的函數:

<!-- 假設file的id為upload_file -->
<script>
    function getFileType {
        //這里是jquery的用法,自行去了解
        var file = $('#upload_file').val();
        return file.replace(/.+\./, '');
    }
<script>
View Code

 4. python跨目錄imprt模塊

之前我寫了一些模塊,在別的目錄下,想引用它們卻一直出錯,找了一些資料,總結了幾個實用的Tips:

① 若被引用的模版在更低的目錄中,如子目錄(sub_dir),則需要在該子目錄中建立__init__.py的空文件,則可以直接在文件中:

 import sub_dir.model_name 

② 若其在父目錄,則需要:

import sys
sys.path.append("..") 
import model_name
View Code

 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>
View Code

  后台只需要執行(若是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,
        },
    }
}
View Code

 重要的是handlers和loggers,handler定義了log文件所在位置,而一個logger可以將多個handler加入其中,我們在代碼中使用只需:

import logging
logger = logging.getLogger("django") # 為loggers中定義的名稱
logger.info("some info...")
View Code

② 不使用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
View Code

 使用的時候直接這樣用:

from ... import  * #自己引入那個函數模塊

logger  = initRotate("/path/somepath/file.log")
logger.info("lalala")
View Code

 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)
View Code

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>    
View Code

這個包的下載地址在這里,點擊下載

 

  未完待續...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM