Python學習筆記——pycharm web伺服器搭建(9):其他知識點
一、靜態檔案
我們要在網頁中載入一張圖,如何做?
settings中新增程式碼:
STATIC_URL = '/static/'(已經存在)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
在專案目錄下(應用同級),建立static檔案,然後在static檔案下建立應用名稱的資料夾(原理類似templates),然後把圖片放在對應的應用資料夾下
按照之前的流程建立伺服器,至能訪問網頁為止,在模板中新增標籤
<img src="/static/day20180522/timg.jpg" alt="圖片">
到這步,圖片已經可以顯示出來了
這時候我們看
STATIC_URL 和 img 中的都是有static目錄的出現
其實STATIC_URL完成的 圖片路徑的對映
我們將這兩個地方的static改成abc 試一試
發現仍然可以進行訪問
我們在新建目錄的時候建立的確實是static資料夾
這就是對映,虛擬目錄對映物理目錄
在訪問的時候你看到的目錄是abc,其實這是對映過的就是static目錄
這樣做對物理目錄一定程度上進行了保護
當STATIC_URL 與 img的頭層目錄 相等的時候
解析器會去STATICFILES_DIRS中設定的目錄中去找你要的圖片或者檔案
當然可以不用static名稱,只要物理目錄和STATICFILES_DIRS中設定的目錄相同即可
當然我們有更好的處理方法,不用去關係這亂七八糟的對映,直接利用反向解析的思想
讓Django幫我們生成路徑
在模板的開頭引入
{% load static from staticfiles %}{# 在圖片使用的地方 #}
<img src="{% static 'day20180522/timg.jpg' %}" alt="">
當我們這樣寫:
settings:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
模板:
{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<img src="/static/day20180522/timg.jpg" alt="圖片"
width="200" height="100">
<br>
<img src="{% static 'day20180522/timg.jpg' %}" alt=""
width="200" height="100">
</body>
</html>
這時候,我們更改 STATIC_URL = ‘sy’
重新整理網頁,會發現上面的圖片不顯示了
原始碼:
我們使用反向解析的img成功解析到圖片,而絕對路徑的圖片還是原來的static,與STATIC_URL不符合,所以找不到圖片。
綜上,在settings中 STATIC_URL 配置的邏輯路徑
STATICFILES_DIRS 配置是物理路徑
二、中介軟體
settings中的MIDDLEWARE_CLASSES
就是一個輕量級底層的外掛系統,讓我們干擾Django的輸入或者輸出
一般情況下,訪問過程如下:
網路請求 →(①)→ ② → URL → ③ → View → ④ → html → ⑤ → 返回
① : __init__(在第一次請求的時候執行,其他時間不執行)
② : process_request()
③ : process_view()
④ : process_template_response()
⑤ : process_response()
還有一種是process_exception() 是檢視出錯才會執行這個方法,執行完後,返回
每個中介軟體,都是一個獨立的python類。
三、上傳圖片
模板中定義
<input type='file' name='xx'>
View檢視中接收
request.FILES[xx]
上傳檔案只能在post中使用
並且form表單提交時要新增enctype = 'mulipart/form-data'才能包含資料
否則FILES中是空的QueryDict物件
實現順序:
1、安裝pillow
2、配置檔案路徑
settings中
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media/')
3、定義上傳模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload</title>
</head>
<body>
<form action="{% url 'day20180522:uploadHandle' %}"
method="post" enctype="multipart/form-data">
<input type="file" name="pic1">
<br>
<input type="submit" value="上傳">
{% csrf_token %}
</form>
</body>
</html>
這裡跳轉頁面用到了反向解析,不太明白的請在(8)中搜索反向解析
4、定義儲存檢視
# 訪問檢視
def uploadPic(request):
return render(request, 'day20180522/uploadpic.html')
# 儲存檢視
def uploadHandle(request):
var = request.FILES['pic1']
fname = os.path.join(settings.MEDIA_ROOT, var.name)
with open(fname, 'wb') as pic:
for c in var.chunks():
pic.write(c)
return HttpResponse('<img src="%s">' %
(settings.STATIC_URL + 'media/' + var.name))
這裡最後一句話,因為使用者上傳的圖片也是靜態檔案(CSS,JS,圖片),在訪問靜態檔案的時候如果做過對映的話就需要按照對映的虛擬路徑在取圖片,直接引用虛擬路徑即可。
四、分頁
Paginator物件
Paginator(列表,int)
int :一頁放多少物件
屬性
- count:物件總數
- num_pages:頁面總數
- page_range:頁碼列表,從1開始,例如[1,2.3]
方法
page(num):得到某一頁的資料,下表從1開始,如果頁碼不存在,丟擲InvalidPage異常
異常execption
- InvalidPage:當向page()傳入無效頁碼時丟擲
- PageNotAnlnteger:當page()傳入一個不是整數的值,時候丟擲
- EmptyPage:當向page()傳入有效值,但是當前頁碼沒有任何物件,丟擲
page物件
page(int)
int整數
屬性
- object_list:當前頁面上所有物件的列表
- number:當前頁的序號,從1開始
- paginator:獲得當前頁的paginator物件
方法
- has_next():如果有下一頁返回True
- has_previous():如果有上一頁返回True
- has_other_pages():如果有上一頁或者下一頁返回True
- next_page_number():返回下一頁的頁碼,如果不存在丟擲InvalidPage異常
- previous_page_number():返回上一頁的頁碼,如果不存在丟擲InvalidPage異常
- len():返回當前頁面的物件個數
- 迭代頁面物件:訪問當前頁面中的每個物件
到這裡我們就可以寫個demo了
首先資料庫得有吧(這裡使用預設的sqlite)
class UserInfo(models.Model):
uname = models.CharField(max_length=10)
upwd = models.CharField(max_length=40)
isDelete = models.BooleanField()
寫好資料庫以後進行遷移,不用多說吧。
# 生成遷移檔案
python manage.py makemigrations
# 遷移
python manage.py migrate
寫好資料庫了,那我總得新增點資料吧,怎麼新增?當然是用admin
用admin就要在admin.py中引入定義好的model類吧
from .models import *
admin.site.register(UserInfo)
好了,啟動伺服器.
…什麼情況 (╯‵□′)╯︵┻━┻
別慌,我們建立一個賬戶
還是在pycharm中的Terminal中
python manage.py createsuperuser
然後一次輸入賬號郵箱密碼就好了,然後我們登入
這是我們登入以後的介面,快去新增資料,這個部分略。
配置總URL什麼的請參照之前的教程自行配置
我們下來直接寫分頁的
urls.py中新增
path('user/<int:index>/', userList, name='userList'),
注意總url中必須寫spacename
分url中必須寫name
這兩個是用於反向解析的,用慣了會發現這個反向解析省很多事兒。
views.py中新增方法
# 分頁測試
def userList(request):
# 取資料庫所有資料
list = UserInfo.objects.all()
# 列表資料放入paginator物件,每頁5條
paginator = Paginator(list, 5)
# 獲得當前頁物件page
page = paginator.page(int(index))
# 字典
c = {'page': page}
# 渲染
return render(request, 'day20180522/userinfoshow.html', c)
模板userinfoshow.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for user in page %}
<li>{{ user.uname }}</li>
{% endfor %}
</ul>
</body>
</html>
注意這裡我們在輸入的時候還需要user/2/這樣輸入才能顯示對應的頁數
來,我們加一個頁碼跳轉功能
上面說paginator有一個方法可以返回所有頁碼。
{% for index in page.paginator.page_range %}{# 如果頁碼和當前頁碼相同,則不能跳轉 #}{% if page.number == index %}{{ index }}{% else %}{# 這裡用到了反向解析 #}
<a href="{% url 'day20180522:userList' index %}">{{ index }}</a>
{% endif %}{% endfor %}
頁面跳轉的功能就出來了
這時候有個尷尬的地方,我們不可能以上來就訪問的是user/1
往往我們訪問的是192.168.1.1::8080/user 他就應該顯示第一個頁面
這時候就需要設定預設頁碼了
因為學習的是python2 + django低版本,我使用的又是python3.6 + django 2.0 各種問題
經過百度,找到了django的官方文件
# URLconf
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.
...
他是這樣寫的,兩個正則跳轉到一個view方法中去,頁碼引數預設等於1
根據他的說明寫出了完整的檢視方法程式碼:
urls.py
path('user/<int:index>/', userList, name='userList'),
path('user/', userList, name='userList'),
views.py
# 分頁測試
def userList(request, index=1):
list = UserInfo.objects.all()
paginator = Paginator(list, 5)
page = paginator.page(int(index))
c = {'page': page}
return render(request, 'day20180522/userinfoshow.html', c)
五、省市縣三級聯查
我們要進行聯查,首先要有資料支撐,資料我們就單純的放在一個表中進行檢索吧。
首先建立模型類
class AreaInfo(models.Model):
title = models.CharField(max_length=20)
parea = models.ForeignKey('self', null=True, blank=True
, on_delete=models.CASCADE)
def __str__(self):
return self.title
這裡用到了外來鍵,外來鍵連的就是自己的id
然後在admin.py中註冊
生成遷移檔案,執行遷移
admin.py
admin.site.register(AreaInfo)
terminal
python manage.py makemigrations
python mannge.py migrate
啟動伺服器後,去admin中去新增資料吧,這個資料新增的過程就不演示了。
新增完成後
寫兩個檢視,
一個檢視是顯示三級聯查的網頁
另一個檢視是請求對應的資料
# 三級查詢測試
def area(request):
return render(request, 'day20180522/area.html')
# area.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<select name="" id="pro">
<option value="">請選擇省</option>
</select>
<select name="" id="city">
<option value="">請選擇市</option>
</select>
<select name="" id="dis">
<option value="">請選擇區縣</option>
</select>
</body>
</html>
網頁就是這樣
請求對應資料的檢視方法:
def areapro(request, pro_id):
if pro_id == 0:
data = AreaInfo.objects.filter(parea__isnull=True)
else:
data = [{}]
l = []
for areaitem in data:
print(areaitem)
l.append([areaitem.pk, areaitem.title])
return JsonResponse({'l': l})
在設計表結構的時候我們應該清楚的知道,區縣的上級(parea)是市,市的上級是省,省的上級是null
這時候我們通過查詢parea = null 拿出省物件,並放入列表中,構造成json物件返回
通過瀏覽器訪問獲得
{"l":
[
[1, "\u5317\u4eac\u5e02"],
[2, "\u6cb3\u5317\u7701"],
[3, "\u6cb3\u5357\u7701"],
[4, "\u5c71\u4e1c\u7701"]
]
}
我們如何在瀏覽器開啟的時候將省資料放在第一個select中,如何在選中省後獲得市資料放在第二個select中,等等操作
接下來用到的就是網頁相關的知識了,ajax動態重新整理頁面的知識
首先我們下載 jquery-1.12.4.min.js 放入專案中 (CSS、JS、圖片都是靜態檔案)
為了保護物理檔案需要做虛擬路徑和物理路徑的對映,詳情請看靜態檔案
STATIC_URL = '/sy/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
在網頁中引入JS
{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="{% static 'day20180522/jquery-1.12.4.min.js' %}"></script>
<script>
{# $() 頁面載入完成,執行括號中的操作 #}
$(function () {
{# 查詢省資訊 #}
{#$.get('{% url 'day20180522:areapro' 0 %}')#}
pro = $('#pro')
$.get('{% url "day20180522:areapro" 0 %}', function (arealist) {
$.each(arealist.l, function (index, item) {
pro.append('<option value="' + item[0] + '">' + item[1] + '</option>')
})
})
})
</script>
</head>
<body>
<select name="" id="pro">
<option value="">請選擇省</option>
</select>
<select name="" id="city">
<option value="">請選擇市</option>
</select>
<select name="" id="dis">
<option value="">請選擇區縣</option>
</select>
</body>
</html>
這樣以來,在開啟網頁的時候就可以將省資料載入第一個select了
接下在選了省以後,我們要非同步載入該省對應的市的資料
這時候我們應該給省select繫結一個change事件
在選定市的時候,我們就要非同步載入該市對應的區縣的資料
這時候我們應該給市select繫結一個change事件
所以得到以下程式碼
{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="{% static 'day20180522/jquery-1.12.4.min.js' %}"></script>
<script>
$(function () {
{#省select#}
pro = $('#pro');
{#市select#}
city = $('#city')
{#區縣select#}
dis = $('#dis')
{# 查詢省資訊 #}
{#$.get('{% url 'day20180522:areapro' 0 %}')#}
$.get('{% url "day20180522:areapro" 0 %}', function (arealist) {
$.each(arealist.l, function (index, item) {
pro.append('<option value="' + item[0] + '">' + item[1] + '</option>')
})
});
{#查詢市的資訊#}
pro.change(function () {
city.empty().append('<option value="">請選擇市</option>');
dis.empty().append('<option value="">請選擇區縣</option>');
i = $(this).val();
{#$.get('{% url "day20180522:areapro" i %}', function (arealist) {#}
$.get('/area/' + i + "/", function (arealist) {
$.each(arealist.l, function (i, item) {
city.append('<option value="' + item[0] + '">' + item[1] + '</option>')
})
})
});
{#查詢縣#}
city.change(function () {
dis.empty().append('<option value="">請選擇區縣</option>');
i = $(this).val();
$.get('/area/' + i + "/", function (arealist) {
$.each(arealist.l, function (i, item) {
dis.append('<option value="' + item[0] + '">' + item[1] + '</option>')
})
})
})
})
</script>
</head>
<body>
<select name="" id="pro">
<option value="">請選擇省</option>
</select>
<select name="" id="city">
<option value="">請選擇市</option>
</select>
<select name="" id="dis">
<option value="">請選擇區縣</option>
</select>
</body>
</html>