標籤:

【記錄】Django學習26

【記錄】Django學習26

來自專欄 Python與Web

這一篇沒有新的,來解決幾個遺留問題。

1、為什麼要有外鍵?

1)假設A表與B表是一對多的關係。如果規定這種對應關係,即兩個問題:A表的一條記錄對應B表的哪幾條記錄?以及B表的一條記錄如何對應A表唯一的一條記錄?

2)其實,只要B表的一條記錄如何對應A表唯一的一條記錄, 反過來A表的一條記錄對應B表的哪幾條記錄就是板上釘釘的事情,通過遍歷B表可以算出來。於是只剩下一個問題:B表的一條記錄如何對應A表唯一的一條記錄?

3)當我們說兩條記錄對應的時候,並不是說這兩條記錄的內容一模一樣或者高度相似,而是指"有關聯",所謂有關聯是指有公共的「欄位」作為橋接,公共欄位以外的其他欄位毫不相干,這也是關聯兩條記錄的意義——獲得更多的信息。抓住這個本質,問題就成為了B表的哪一個欄位與A表的哪一個欄位關聯?(或者哪幾個)

4)每一個表都有主鍵,每個記錄取值是唯一的。既然B到A是唯一的,B的一條記錄只要包含了A的主鍵欄位的取值,就可以憑藉這個取值查詢,由於這個值在A中是唯一的,等同於關聯到唯一的一條A記錄。A的主鍵在B中叫「外鍵」——別人的主鍵。而為了在B中有位置記錄這個取值,B會額外有一個欄位,用於保存外鍵的值。B這個欄位的取值類型應該和A的主鍵一模一樣,因為他們存在的是同一類型的對象。

2、解決頁面布局問題

瀏覽器檢查元素,找到原因:

2本來應該在1的下方,現在重疊了!

仔細看頁面結構。body下面就是一個container,container下面4個div:

現在對比另一個類似結構但是顯示正常的網頁:

body下面2個container,其中第2個就是footer!

這個正常頁面的布局雖然看起來沒什麼問題,但看html,footer和header兩個區域沒有並列,footer單獨提出來作為一個層次……感覺並不合理,早晚有問題。

不知道是不是寫書的作者在這裡留了一個bug,反正最後我是自己修改了article/base.html的結構,具體的html也隨之有修改,讓body裡面是上中下3個container,裝的分別是header、body和footer:

body裡面是上中下3個container

修改後的base.html:

13 <body> 14 {% include article/header.html %} 15 <div class="container"> 16 <div class="col-md-2"> 17 {% include article/leftslider.html %} 18 </div> 19 <div class="col-md-10"> 20 {% block content %} 21 {% endblock %} 22 </div> 23 </div>

修改前:

<body><div class="container"> {% include article/header.html %} <div class="col-md-2"> {% include article/leftslider.html %} </div> <div class="col-md-10"> {% block content %} {% endblock %} </div> {% include footer.html %}</div>

布局到此看著還順眼,將就了!

3、into_env.sh的改進。

每次登陸伺服器就在家目錄,以前都是source activate環境-cd目錄-啟動Django……每次都要手動敲好幾個命令。終於有一天受不了了,把命令用&&串起來寫成了一個腳本into_env.sh。

腳本可以執行,但唯一的問題是如果django服務已經啟動,最後一行的啟動命令會執行報錯。這個報錯是意料之中的,但我以為不會影響前面幾條。

但實際情況是:如果最後一個失敗了,前面所有的命令等於沒有執行——目錄沒有切換,環境沒有進入。

而我希望得到的效果是:沒有啟動就幫我啟動,已經啟動還是幫我切換目錄、進入環境。

為了得到這個效果,加入了if判斷。總之怎麼樣都要讓腳本正常退出,不能報錯。

感謝 @shenJin 。

用8000埠號是否listen來作判斷條件:

grace@Grace:~/web_dev/mysite/templates/article/column$ netstat -ltnActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN

-l是listen,-t是tcp, -n是no alias,盡量不顯示別名。

修改後的腳本是:

1 #!/bin/bash 2 source activate django_env && 3 echo "activate django_env done" && 4 cd web_dev/mysite/ && 5 echo "You are now at ~/web_dev/mysite/" && 6 if netstat -ltn | grep 8000 > /dev/null 7 then 8 echo "django process on port 8000 already exists" 9 else 10 python manage.py runserver 0.0.0.0:8000 & 11 fi~

注意if netstat -ltn | grep 8000 > /dev/null,> /dev/null不會影響if的判斷,只是讓netstat的命令不要在屏幕上顯示一遍。

4、最後理一下csrf跨域的實現。

[了解csrf攻擊]

一些理論:

【基本功】 前端安全系列之二:如何防止CSRF攻擊?(我看完了)

JS中的跨域問題 - 泳少爺 - 博客園

CSRF 攻擊的應對之道?

www.ibm.com圖標

這個視頻比較直觀(是php演示,但是很簡單,好懂),首先演示了GET和POST兩種請求被利用之後的效果,然後介紹了2種應對方法(用csrf_token應對POST、用referer應對GET):

CSRF攻擊之原理以及防禦實戰 - 網易雲課堂

不過在目前我在Django裡面,接觸的csrf_token還局限在POST提交表單。

1)最常用的一種辦法(在article應用下)是:在<form>之後帶上{% csrf_token %}:

<form class="form-horizontal" action="." method="post">{% csrf_token %}

之後{% csrf_token %}會被render成這樣,表示提交表單時會帶上這個token一起:

伺服器返回頁面時多返回了一個不易猜測的隨機數,也就是csrf_token,POST的時候只有把它納入參數一起提交,服務端才承認這次POST。

之所以能夠防範csrf攻擊,重點是csrf_token不在coockie而是在第一個頁面的請求中,也就是說拿到csrf_token必須是要先有第一個請求得到包含表單的頁面和POST介面,在這個表單上點擊提交然後再向POST介面發送第2個請求。而對於發起攻擊的網站,它會直接像POST介面發起請求,所以永遠拿不到token……

當然,如果發起攻擊的網站能夠先進行第一次,再進行第二次……就像正常訪問房展的操作一樣,似乎也能攻擊成功。不過那好像不是瀏覽器層面可以辦到的事情而是爬蟲可以辦到的事情…

2)也有另一種比較特殊:在視圖函數前引入csrf_exempt裝飾器,用它裝飾含有POST的視圖函數:

3 from django.views.decorators.csrf import csrf_exempt... 11 @login_required(login_url=/account/login/) 12 @csrf_exempt 13 def article_column(request):

帶上裝飾器,意思是關閉默認開啟的csrf驗證,所以這時提交的表單不帶csrf_token也不報錯。

但這似乎是一個權宜之計,並不安全。

為此還是把這句話刪去,把針對csrf的驗證開啟吧。

打開驗證後,還需要讓表單提交時帶上csrf_token。

比較特殊的是,不同於<form>標籤觸發POST動作,這裡是js函數裡面執行.ajax()來觸發,於是token也不再加在<form>後面。

可以引入一個csrf.js文件,也可以這樣在$.ajax之前加一個$.ajaxSetup:

42 $.ajaxSetup({ 43 data: {csrfmiddlewaretoken: {{ csrf_token }} }, 44 }); 45 $.ajax({ 46 url:{% url "article:article_column" %}, 47 type:"POST", 48 data:{"column":column_name}, 49 success:function(e){ 50 if(e=="1"){ 51 parent.location.reload(); 52 layer.msg("good"); 53 }else{ 54 layer.msg("此欄目已有,請更換名稱"); 55 } 56 }, 57 });

參考: Django Ajax CSRF 認證

5、總結提交的套路

  1. <form>裡面的<input type="submit">表單提交到<form> action屬性指定的頁面(大多本頁面),<input type="button" onclick="js_func()",提交到另外一個頁面。
  2. <a href="javascript:" onclick="jsfunc()">以及<button onclick="js_func()">都是點擊觸發一段js函數。

推薦閱讀:

TAG:Django框架 |