在腳本中如何進行Django的運行 if __name__ == '__main__': import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE","orm69.settings") django.setup() #... ...
在腳本中如何進行Django的運行 if __name__ == '__main__': import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE","orm69.settings") django.setup() # 進行的操作 一.搭建環境 設置pip install 的源 創建虛擬環境 mkvirtualenv py6_django -p python3 查看當前目錄下的虛擬環境 workon 設置Django的版本 pip install django==1.11.11 # 虛擬環境的指令 mkvirtualenv # 創建虛擬環境 rmvirtualenv # 刪除虛擬環境 workon # 進入虛擬環境、查看所有虛擬環境 deactivate # 退出虛擬環境 ctrl + h 查看隱藏文件 # pip pip install # 安裝依賴包 pip uninstall # 卸載依賴包 pip list # 查看已安裝的依賴包 pip freeze # 凍結當前環境的依賴包 workon + py6_django 進入當前的虛擬環境 切換到桌面下的code中,將創建的項目放到裡面 cd ~/Desktop/code django-admin startproject demo 查看樹狀圖 執行 tree,結果顯示如下的目錄結構 與項目同名的目錄,此處為demo settings.py 是項目的整體配置文件 urls.py 是項目的URL配置文件 wsgi.py 是項目與WSGI相容的Web伺服器入口 manage.py 是項目管理文件,通過它管理項目 運行開發伺服器 python manage.py runserver 創建子應用(類似於藍圖) python manage.py startapp testdatabase 執行 tree,結果顯示如下的目錄結構 admin.py 文件跟網站的後臺管理站點配置相關 apps.py 文件用於配置當前子應用的相關信息 migrations 目錄用於存放資料庫遷移歷史文件 models.py 文件用戶保存資料庫模型類 tests.py 文件用於開發測試用例,編寫單元測試 views.py 文件用於編寫Web應用視圖 創建完子應用時需要進行三個步驟: 第一步:設置路由 在工程配置文件settings.py中,INSTALLED_APPS項保存了工程中已經註冊安裝的子應用, 將剛創建的users子應用添加到工程中,可在INSTALLED_APPS列表中添加 'testdatabase.apps.TestdatabaseConfig' 第二步:在views中寫視圖函數 from django.http import HttpResponse # 寫視圖函數時需要註意必須要傳request參數和返回一個HttpResponse的響應 def index(request): """ index視圖 :param request: 包含了請求信息的請求對象 :return: 響應對象 """ return HttpResponse("hello the world!") 第三步:在子應用中創建urls.py,保存子應用中的地址 在users/urls.py文件中定義路由信息 from django.conf.urls import url from . import views # urlpatterns是被django自動識別的路由列表變數 urlpatterns = [ # 每個路由信息都需要使用url函數來構造 # url(路徑, 視圖) # ^ / $ 需註意在子應用的urls.py中一定要有, url(r'^index/$', views.index), ] 在該項目的(demo)urls.py中 from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), # django預設包含的 # 添加 # ^ / 需註意在子應用的urls.py中一定要有 url(r'^testdatabase/', include('testdatabase.urls')), ] 重新啟動django程式 python manage.py runserver 配置文件 BASE_DIR DEBUG 將語言和時區修改為中國大陸信息 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' 靜態文件 在項目根目錄下創建static_files目錄來保存靜態文件。 在demo/settings.py中修改靜態文件的兩個參數為 STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static_files'), ] 此時在static_files添加的任何靜態文件都可以使用網址 /static/文件在static_files中 的路徑來訪問了 用127.0.0.1:8000/static/index.html來訪問 註意: Django 僅在調試模式下(DEBUG=True)能對外提供靜態文件 當DEBUG=False工作在生產模式時,Django不再對外提供靜態文件,需要是用 collectstatic命令來收集靜態文件並交由其他靜態文件伺服器來提供 路由說明 路由定義位置 路由解析順序 從上至下 註意:需要註意定義路由的順序,避免出現屏蔽效應。 路由命名與reverse反解析(逆向) 路由信息 在定義路由的時候,可以為路由命名,方便查找特定視圖的具體路徑信息。 1) 在使用include函數定義路由時,可以使用namespace參數定義路由的命名空間,如 url(r'^users/', include('users.urls', namespace='users')), 命名空間表示,凡是users.urls中定義的路由,均屬於namespace指明的users名下。 命名空間的作用:避免不同應用中的路由使用了相同的名字發生衝突,使用 命名空間區別開。 2) 在定義普通路由時,可以使用name參數指明路由的名字,如 urlpatterns = [ url(r'^index/$', views.index, name='index'), url(r'^say', views.say, name='say'), ] reverse反解析 使用reverse函數,可以根據路由名稱,返回具體的路徑,如: from django.core.urlresolvers import reverse # 註意導包路徑 def index(request): return HttpResponse("hello the world!") def say(request): url = reverse('users:index') # 返回 /users/index/ print(url) return HttpResponse('say') 對於未指明namespace的,reverse(路由name) 對於指明namespace的,reverse(命名空間namespace:路由name) 路徑結尾斜線的說明 Django中定義路由時,通常以斜線/結尾,其好處是用戶訪問不以斜線/結尾的相同路徑時, Django會把用戶重定向到以斜線/結尾的路徑上,而不會返回404不存在。如 urlpatterns = [ url(r'^index/$', views.index, name='index'), ] 用戶訪問 index 或者 index/ 網址,均能訪問到index視圖。 說明: 雖然路由結尾帶/能帶來上述好處,但是卻違背了HTTP中URL表示資源位置路徑的設計理念。 是否結尾帶/以所屬公司定義風格為準。 請求和響應 請求 利用HTTP協議向伺服器傳參有幾種途徑? 提取URL的特定部分,如/weather/beijing/2018,可以在伺服器端的路由中用正則表達式截取 查詢字元串(query string),形如key1=value1&key2=value2; 請求體(body)中發送的數據,比如表單數據、json、xml; 在http報文的頭(header)中 具體如下: request 傳遞數據的方式 a、以查詢字元串的方式將參數放到了url中 http://127.0.0.1:8000/login?username=zhangsan&pwd=12345 b、將數據放到了請求體中 http://127.0.0.1:8000/login c、路徑參數:將數據放到了路徑中 http://127.0.0.1:8000/login/zhangsan/123456 d、請求頭:將數據放到了請求頭中 URL路徑參數 未命名參數傳遞 url(r'^weather/([a-z]+)/(\d{4})/$', views.weather), def weather(request, city, year): print('city=%s' % city) print('year=%s' % year) return HttpResponse('OK') 命名參數按名字的傳遞 url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather), def weather(request, year, city): print('city=%s' % city) print('year=%s' % year) return HttpResponse('OK...') Django中的QueryDict對象 定義在django.http.QueryDict HttpRequest對象的屬性GET、POST都是QueryDict類型的對象 與python字典不同,QueryDict類型的對象用來處理同一個鍵帶有多個值的情況 方法get():根據鍵獲取值 如果一個鍵同時擁有多個值將獲取最後一個值 如果鍵不存在則返回None值,可以設置預設值進行後續處理 dict.get('鍵',預設值) 可簡寫為 dict['鍵'] 方法getlist():根據鍵獲取值,值以列表返回,可以獲取指定鍵的所有值 如果鍵不存在則返回空列表[],可以設置預設值進行後續處理 dict.getlist('鍵',預設值) 查詢字元串Query String 獲取請求路徑中的查詢字元串參數(形如?k1=v1&k2=v2),可以通過request.GET屬性獲取, 返回QueryDict對象 # /qs/?a=1&b=2&a=3 def qs(request): a = request.GET.get('a') b = request.GET.get('b') alist = request.GET.getlist('a') print(a) # 3 print(b) # 2 print(alist) # ['1', '3'] return HttpResponse('OK') 重要:查詢字元串不區分請求方式,即假使客戶端進行POST方式的請求,依然可以 通過request.GET獲取請求中的查詢字元串數據。 請求體 表單類型 Form Data 前端發送的表單類型的請求體數據,可以通過request.POST屬性獲取,返回QueryDict對象 def get_body(request): a = request.POST.get('a') b = request.POST.get('b') alist = request.POST.getlist('a') print(a) print(b) print(alist) return HttpResponse('OK') 非表單類型 Non-Form Data 非表單類型的請求體數據,Django無法自動解析,可以通過request.body屬性獲取 最原始的請求體數據,自己按照請求體格式(JSON、XML等)進行解析。request.body 返回bytes類型。 例如要獲取請求體中的如下JSON數據 {"a": 1, "b": 2} 可以進行如下方法操作: import json def get_body_json(request): json_bytes = request.body json_str = json_bytes.decode() json_data = json.loads(json_str) print(json_data['a']) print(json_data['b']) return HttpResponse('OK') 請求頭 通過request.META屬性獲取請求頭headers中的數據,request.META為字典類型 常見的請求頭如: CONTENT_LENGTH – The length of the request body (as a string). CONTENT_TYPE – The MIME type of the request body. HTTP_ACCEPT – Acceptable content types for the response. HTTP_ACCEPT_ENCODING – Acceptable encodings for the response. HTTP_ACCEPT_LANGUAGE – Acceptable languages for the response. HTTP_HOST – The HTTP Host header sent by the client. HTTP_REFERER – The referring page, if any. HTTP_USER_AGENT – The client’s user-agent string. QUERY_STRING – The query string, as a single (unparsed) string. REMOTE_ADDR – The IP address of the client. REMOTE_HOST – The hostname of the client. REMOTE_USER – The user authenticated by the Web server, if any. REQUEST_METHOD – A string such as "GET" or "POST". SERVER_NAME – The hostname of the server. SERVER_PORT – The port of the server (as a string). 具體使用如: def get_headers(request): print(request.META['CONTENT_TYPE']) return HttpResponse('OK') 其他常用HttpRequest對象屬性 method:一個字元串,表示請求使用的HTTP方法,常用值包括:'GET'、'POST' user:請求的用戶對象 path:一個字元串,表示請求的頁面的完整路徑,不包含功能變數名稱和參數部分 encoding:一個字元串,表示提交的數據的編碼方式 如果為None則表示使用瀏覽器的預設設置,一般為utf-8 這個屬性是可寫的,可以通過修改它來修改訪問表單數據使用的編碼, 接下來對屬性的任何訪問將使用新的encoding值 FILES:一個類似於字典的對象,包含所有的上傳文件 響應 HttpResponse 可以使用django.http.HttpResponse來構造響應對象。 HttpResponse(content=響應體, content_type=響應體數據類型, status=狀態碼) 也可通過HttpResponse對象屬性來設置響應體、響應體數據類型、狀態碼: content:表示返回的內容。 status_code:返回的HTTP響應狀態碼。 content_type:指定返回數據的的MIME類型。 響應頭可以直接將HttpResponse對象當做字典進行響應頭鍵值對的設置: response = HttpResponse() response['Itcast'] = 'Python' # 自定義響應頭Itcast, 值為Python 示例: from django.http import HttpResponse def demo_view(request): return HttpResponse('itcast python', status=400) 或者 response = HttpResponse('itcast python') response.status_code = 400 response['Itcast'] = 'Python' return response HttpResponse子類 Django提供了一系列HttpResponse的子類,可以快速設置狀態碼 HttpResponseRedirect 301 HttpResponsePermanentRedirect 302 HttpResponseNotModified 304 HttpResponseBadRequest 400 HttpResponseNotFound 404 HttpResponseForbidden 403 HttpResponseNotAllowed 405 HttpResponseGone 410 HttpResponseServerError 500 JsonResponse 若要返回json數據,可以使用JsonResponse來構造響應對象,作用: 幫助我們將數據轉換為json字元串 設置響應頭Content-Type為 application/json from django.http import JsonResponse def demo_view(request): return JsonResponse({'city': 'beijing', 'subject': 'python'}) redirect重定向 from django.shortcuts import redirect def demo_view(request): return redirect('/index.html') Cookie Cookie的特點 Cookie以鍵值對的格式進行信息的存儲。 Cookie基於功能變數名稱安全,不同功能變數名稱的Cookie是不能互相訪問的,如訪問itcast.cn時 向瀏覽器中寫了Cookie信息,使用同一瀏覽器訪問baidu.com時,無法訪問到 itcast.cn寫的Cookie信息。當瀏覽器請求某網站時,會將瀏覽器存儲的 跟網站相關的所有Cookie信息提交給網站伺服器。 設置Cookie 可以通過HttpResponse對象中的set_cookie方法來設置cookie。 HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期) max_age 單位為秒,預設為None。如果是臨時cookie,可將max_age設置為None。 代碼如下: def demo_view(request): response = HttpResponse('ok') response.set_cookie('itcast1', 'python1') # 臨時cookie response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小時 return response 讀取Cookie 可以通過HttpRequest對象的COOKIES屬性來讀取本次請求攜帶的cookie值。 request.COOKIES為字典類型。 代碼如下: def demo_view(request): cookie1 = request.COOKIES.get('itcast1') print(cookie1) return HttpResponse('OK') Session 啟用Session(Django預設啟用) 存儲方式 在settings.py文件中,可以設置session數據的存儲方式,可以保存在資料庫、本地緩存等 本地緩存 存儲在本機記憶體中,如果丟失則不能找回,比資料庫的方式讀寫更快。 SESSION_ENGINE='django.contrib.sessions.backends.cache' 混合存儲 優先從本機記憶體中存取,如果沒有則從資料庫中存取。 SESSION_ENGINE='django.contrib.sessions.backends.cached_db' Redis 在redis中保存session,需要引入第三方擴展,我們可以使用django-redis來解決 安裝擴展 pip install django-redis 配置 在settings.py文件中做如下設置 "default" 只要兩次設置的名字一樣就可以啦 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default" 如果redis的ip地址不是本地迴環127.0.0.1,而是其他地址,訪問Django時,可能出現 Redis連接錯誤 解決方法: 修改redis的配置文件,添加特定ip地址。 打開redis的配置文件 sudo vim /etc/redis/redis.conf 進行修改 在如下配置項進行修改(如要添加10.211.55.5地址) bind 127.0.0.1 10.211.55.5 重新啟動redis服務 sudo service redis-server restart Seesion操作 通過HttpRequest對象的session屬性進行會話的讀寫操作。 以鍵值對的格式寫session。 request.session['鍵']=值 根據鍵讀取值。 request.session.get('鍵',預設值) 清除所有session,在存儲中刪除值部分。 request.session.clear() 清除session數據,在存儲中刪除session的整條數據。 request.session.flush() 刪除session中的指定鍵及值,在存儲中只刪除某個鍵及對應的值。 del request.session['鍵'] 設置session的有效期 request.session.set_expiry(value) 如果value是一個整數,session將在value秒沒有活動後過期。 如果value為0,那麼用戶session的Cookie將在用戶的瀏覽器關閉時過期。 如果value為None,那麼session有效期將採用系統預設值,預設為兩周, 可以通過在settings.py中設置SESSION_COOKIE_AGE來設置全局預設值。 類視圖 類視圖的好處: 代碼可讀性好 類視圖相對於函數視圖有更高的復用性,如果其他地方需要用到某個類視圖的某個特定邏輯, 直接繼承該類視圖即可 代碼如下: 在上面的子應用中 from django.views import View def wrapper(func): def inner(*args,**kwargs): print("獲取頁面數據1") ret = func(*args,**kwargs) print("獲取頁面數據2") return ret return inner class RegisterView(View): @wrapper def get(self,*args,**kwargs): print("test get page") return HttpResponse("get page") def post(self,*args,**kwargs): print("test post page") return HttpResponse("post page") urlpatterns = [ url(r"^register/$", views.RegisterView.as_view()), ] 中間件 中間件的定義方法 在testdatabase應用中新建一個middleware.py文件, def my_middleware(get_response): print('init 被調用') def middleware(request): print('before request 被調用') response = get_response(request) print('after response 被調用') return response return middleware 定義好中間件後,需要在settings.py 文件中添加註冊中間件 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'testdatabase.middleware.my_middleware', # 添加中間件 ] 定義一個視圖進行測試 def demo_view(request): print('view 視圖被調用') return HttpResponse('OK') 多個中間件的執行順序 在請求視圖被處理前,中間件由上至下依次執行 在請求視圖被處理後,中間件由下至上依次執行 代碼如下: 定義兩個中間件 def my_middleware(get_response): print('init 被調用') def middleware1(request): print('before request 被調用') response = get_response(request) print('after response 被調用') return response return middleware def my_middleware2(get_response): print('init2 被調用') def middleware(request): print('before request 2 被調用') response = get_response(request) print('after response 2 被調用') return response return middleware 註冊添加兩個中間件 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'testdatabase.middleware.my_middleware1', # 添加中間件 'testdatabase.middleware.my_middleware2', # 添加中間件 ] 執行結果為: init2 被調用 init 被調用 before request 被調用 before request 2 被調用 view 視圖被調用 after response 2 被調用 after response 被調用 資料庫 ORM的關係 一對多 ForeignKey 在多的一方設置外鍵 多對多 ManyToMany 一般設置在處理業務邏輯比較多的一方 配置 在settings.py中保存了資料庫的連接配置信息,Django預設初始配置使用sqlite資料庫。 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } 使用mysql資料庫 pip install PyMySQL 在Django的工程同名子目錄的__init__.py文件中添加如下語句 # 將PyMySQL裝飾成MySQLdb,方便Django識別 from pymysql import install_as_MySQLdb install_as_MySQLdb() 修改DATABASES配置信息 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 資料庫主機 'PORT': 3306, # 資料庫埠 'USER': 'root', # 資料庫用戶名 'PASSWORD': 'mysql', # 資料庫用戶密碼 'NAME': 'py6_django' # 資料庫名字 } } 在MySQL中創建資料庫 create database py6_django default charset=utf8; 進行資料庫的遷移操作 遷移文件的生成 python manage.py makemigrations 同步到資料庫中 python manage.py migrate 如果不成功使用強制遷移 強制遷移 python manage.py migrate testdatabase --fake 查看MySQL資料庫日誌 ...... tail -f /var/log/mysql/mysql.log # 可以實時查看資料庫的日誌內容 利用ORM創建表並手動增加數據 # 添加數據 book = BookInfo( btitle='西游記', bpub_date=date(1988, 1, 1), bread=10, bcomment=10) book.save() hero = HeroInfo( hname="孫悟空", hgender=0, hcomment="七十二變", hbook=book, is_delete=0, ) hero.save() hero = HeroInfo( hname="豬八戒", hgender=0, hbook_id=book.id, is_delete=0, ) hero.save() HeroInfo.objects.create( hname='沙悟凈', hgender=0, hbook=book ) # 基本查詢 print(BookInfo.objects.all()) print(BookInfo.objects.get(btitle='雪山飛狐').id) print(BookInfo.objects.get(id=3)) print(BookInfo.objects.get(pk=3)) # 將ID=3的排除,顯示其他的 print(BookInfo.objects.exclude(id=3)) print(111111111) try: print(BookInfo.objects.get(id=12)) except Exception as e: print("未找到對應的ID") print(BookInfo.objects.count()) # 關於values的查詢 print("values".center(80,"*")) print(BookInfo.objects.values("btitle","bpub_date")) print("values_list".center(80,"#")) print(BookInfo.objects.values_list("btitle","