1. 程式人生 > 實用技巧 >路由與檢視

路由與檢視

目錄

資料的查,改,刪

# 先講資料庫中的資料全部展示到前端 然後給每一個數據兩個按鈕 一個編輯一個刪除

# 檢視
def userlist(request):
    # 查詢出使用者表裡面所有的資料
    # 方式1
    # data = models.User.objects.filter()
    # print(data)
    # 方式2
    user_queryset = models.User.objects.all()
    # return render(request,'userlist.html',{'user_queryset':user_queryset})
    return render(request,'userlist.html',locals())

# 編輯功能
	# 點選編輯按鈕朝後端傳送編輯資料的請求
  """
  如何告訴後端使用者想要編輯哪條資料?
  	將編輯按鈕所在的那一行資料的主鍵值傳送給後端
  	利用url問號後面攜帶引數的方式
  	
  	{% for user_obj in user_queryset %}
                        <tr>
                            <td>{{ user_obj.id }}</td>
                            <td>{{ user_obj.username }}</td>
                            <td>{{ user_obj.password }}</td>
                            <td>
                                <a href="/edit_user/?user_id={{ user_obj.id }}" class="btn btn-primary btn-xs">編輯</a>
                                <a href="" class="btn btn-danger btn-xs">刪除</a>
                            </td>
                        </tr>
                    {% endfor %}
  """
  # 後端查詢出使用者想要編輯的資料物件 展示到前端頁面供使用者檢視和編輯
  def edit_user(request):
    # 獲取url問號後面的引數
    edit_id = request.GET.get('user_id')
    # 查詢當前使用者想要編輯的資料物件
    edit_obj = models.User.objects.filter(id=edit_id).first()

    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 去資料庫中修改對應的資料內容
        # 修改資料方式1
        # models.User.objects.filter(id=edit_id).update(username=username,password=password)
        """
            將filter查詢出來的列表中所有的物件全部更新            批量更新操作
            只修改被修改的欄位
        """

        # 修改資料方式2
        edit_obj.username = username
        edit_obj.password= password
        edit_obj.save()
        """
            上述方法當欄位特別多的時候效率會非常的低
            從頭到尾將資料的所有欄位全部更新一邊 無論該欄位是否被修改
        """
        
        # 跳轉到資料的展示頁面
        return redirect('/userlist/')



    # 將資料物件展示到頁面上
    return render(request,'edit_user.html',locals())

# 刪除功能
	"""
	跟編輯功能邏輯類似
	def delete_user(request):
    # 獲取使用者想要刪除的資料id值
    delete_id = request.GET.get('user_id')
    # 直接去資料庫中找到對應的資料刪除即可
    models.User.objects.filter(id=delete_id).delete()
    """
        批量刪除
    """
    # 跳轉到展示頁面

    return redirect('/userlist/')	
	
	"""
# 真正的刪除功能應該需要二次確認 我們這裡先不做後面會講
# 刪除資料內部其實並不是真正的刪除 我們會給資料新增一個標識欄位用來表示當前資料是否被刪除了,如果資料被刪了僅僅只是講欄位修改一個狀態
	username password	is_delete
  jason			123				0
  egon			123				1

django orm中如何建立表關係

"""
表與表之間的關係
	一對多
	
	多對多
	
	一對一
	
	沒有關係

判斷表關係的方法:換位思考
"""
圖書表

出版社表

作者表

作者詳情表


"""
圖書和出版社是一對多的關係 外來鍵欄位建在多的那一方 book
	
圖書和作者是多對多的關係 需要建立第三張表來專門儲存

作者與作者詳情表是一對一
"""
from django.db import models

# Create your models here.


# 建立表關係  先將基表創建出來 然後再新增外來鍵欄位
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    # 總共八位 小數點後面佔兩位
    """
    圖書和出版社是一對多 並且書是多的一方 所以外來鍵欄位放在書表裡面
    """
    publish = models.ForeignKey(to='Publish')  # 預設就是與出版社表的主鍵欄位做外來鍵關聯
    """
    如果欄位對應的是ForeignKey 那麼會orm會自動在欄位的後面加_id
    如果你自作聰明的加了_id那麼orm還是會在後面繼續加_id
    
    後面在定義ForeignKey的時候就不要自己加_id
    """


    """
    圖書和作者是多對多的關係 外來鍵欄位建在任意一方均可 但是推薦你建在查詢頻率較高的一方
    """
    authors = models.ManyToManyField(to='Author')
    """
    authors是一個虛擬欄位 主要是用來告訴orm 書籍表和作者表是多對多關係
    讓orm自動幫你建立第三張關係表
    """


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    """
    作者與作者詳情是一對一的關係 外來鍵欄位建在任意一方都可以 但是推薦你建在查詢頻率較高的表中
    """
    author_detail = models.OneToOneField(to='AuthorDetail')
    """
    OneToOneField也會自動給欄位加_id字尾
    所以你也不要自作聰明的自己加_id
    """

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()  # 或者直接字元型別
    addr = models.CharField(max_length=32)


"""
	orm中如何定義三種關係
		publish = models.ForeignKey(to='Publish')  # 預設就是與出版社表的主鍵欄位做外來鍵關聯
		
		authors = models.ManyToManyField(to='Author')
		
		author_detail = models.OneToOneField(to='AuthorDetail')
		
		
		ForeignKey
		OneToOneField
			會自動在欄位後面加_id字尾
"""

# 在django1.X版本中外來鍵預設都是級聯更新刪除的
# 多對多的表關係可以有好幾種建立方式 這裡暫且先介紹一種
# 針對外來鍵欄位裡面的其他引數 暫時不要考慮 如果感興趣自己可以百度試試看

django請求生命週期流程圖(必會)

# 每個人都要會畫 這個圖是你們後期複習django最好的一個梳理方式


# 擴充套件知識點
	"""
	快取資料庫
		提前已經將你想要的資料準備好了 你來直接拿就可以
		提高效率和響應時間
		
	當你在修改你的資料的時候 你會發現資料並不是立刻修改完成的
	而是需要經過一段時間才會修改
		部落格園
	
	瞭解即可
	"""

路由層

路由匹配

# 路由匹配
url(r'test',views.test),
url(r'testadd',views.testadd)
"""
url方法第一個引數是正則表示式
	只要第一個引數正則表示式能夠匹配到內容 那麼就會立刻停止往下匹配
	直接執行對應的檢視函式

你在輸入url的時候會預設加斜槓
	django內部幫你做到重定向
		一次匹配不行
		url後面加斜槓再來一次
"""
# 取消自動加斜槓
APPEND_SLASH = False/True	# 預設是自動加斜槓的


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首頁
    url(r'^$',views.home),
    # 路由匹配
    url(r'^test/$',views.test),
    url(r'^testadd/$',views.testadd),
    # 尾頁(瞭解)
    url(r'',views.error),
]

無名分組

"""
分組:就是給某一段正則表示式用小括號擴起來
"""
url(r'^test/(\d+)/',views.test)

def test(request,xx):
    print(xx)
    return HttpResponse('test')
  
# 無名分組就是將括號內正則表示式匹配到的內容當作位置引數傳遞給後面的檢視函式

有名分組

"""
可以給正則表示式起一個別名
"""
url(r'^testadd/(?P<year>\d+)',views.testadd)

def testadd(request,year):
    print(year)
    return HttpResponse('testadd')

# 有名分組就是將括號內正則表示式匹配到的內容當作關鍵字引數傳遞給後面的檢視函式

無名有名是否可以混合使用

"""
嘻嘻 不能混用
但是同一個分組可以使用N多次
"""

# 單個的分組可以使用多次
url(r'^index/(\d+)/(\d+)/(\d+)/',views.index),
url(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),

反向解析

# 通過一些方法得到一個結果 該結果可以直接訪問對應的url觸發檢視函式

# 先給路由與檢視函式起一個別名
	url(r'^func_kkk/',views.func,name='ooo')
# 反向解析
	# 後端反向解析
  	from django.shortcuts import render,HttpResponse,redirect,reverse
  	reverse('ooo')
  # 前端反向解析
  	<a href="{% url 'ooo' %}">111</a>

無名有名分組反向解析

# 無名分組反向解析
	url(r'^index/(\d+)/',views.index,name='xxx')

# 前端
	{% url 'xxx' 123 %}
# 後端
	reverse('xxx', args=(1,))

"""
這個數字寫程式碼的時候應該放什麼
	數字一般情況下放的是資料的主鍵值  資料的編輯和刪除
	url(r'^edit/(\d+)/',views.edit,name='xxx')
	
	def edit(request,edit_id):
		reverse('xxx',args=(edit_id,))
		
	{%for user_obj in user_queryset%}
		<a href="{% url 'xxx' user_obj.id %}">編輯</a>
	{%endfor%}

今天每個人都必須完成的作業(*******)
	利用無名有名 反向解析 完成資料的增刪改查
"""



# 有名分組反向解析
   url(r'^func/(?P<year>\d+)/',views.func,name='ooo')
# 前端
	<a href="{% url 'ooo' year=123 %}">111</a>  瞭解
	<a href="{% url 'ooo' 123 %}">222</a>  			記憶

# 後端	
	 # 有名分組反向解析 寫法1  瞭解
   print(reverse('ooo',kwargs={'year':123}))
   # 簡便的寫法  減少你的腦容量消耗 記跟無名一樣的操作即可
   print(reverse('ooo',args=(111,)))

路由分發

"""
django的每一個應用都可以有自己的templates資料夾 urls.py static資料夾
正是基於上述的特點 django能夠非常好的做到分組開發(每個人只寫自己的app)
作為組長 只需要將手下書寫的app全部拷貝到一個新的django專案中 然後在配置檔案裡面註冊所有的app再利用路由分發的特點將所有的app整合起來

當一個django專案中的url特別多的時候 總路由urls.py程式碼非常冗餘不好維護
這個時候也可以利用路由分發來減輕總路由的壓力

利用路由分發之後 總路由不再幹路由與檢視函式的直接對應關係
而是做一個分發處理
	識別當前url是屬於哪個應用下的 直接分發給對應的應用去處理
	
"""


# 總路由
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 1.路由分發
    url(r'^app01/',include(app01_urls)),  # 只要url字首是app01開頭 全部交給app01處理
    url(r'^app02/',include(app02_urls))   # 只要url字首是app02開頭 全部交給app02處理
  
    # 2.終極寫法  推薦使用
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls'))
    # 注意事項:總路由裡面的url千萬不能加$結尾
]

# 子路由
	# app01 urls.py
  from django.conf.urls import url
  from app01 import views

  urlpatterns = [
      url(r'^reg/',views.reg)
  ]
  # app02 urls.py
  from django.conf.urls import url
  from app02 import views

  urlpatterns = [
      url(r'^reg/',views.reg)
  ]

名稱空間(瞭解)

# 當多個應用出現了相同的別名 我們研究反向解析會不會自動識別應用字首
"""
正常情況下的反向解析是沒有辦法自動識別字首的
"""

# 名稱空間
	# 總路由
    url(r'^app01/',include('app01.urls',namespace='app01')),
    url(r'^app02/',include('app02.urls',namespace='app02'))
  # 解析的時候
  	# app01
  	urlpatterns = [
    url(r'^reg/',views.reg,name='reg')
		]
    # app02
    urlpatterns = [
    url(r'^reg/',views.reg,name='reg')
		]
    
  	reverse('app01:reg')
    reverse('app02:reg')
    
    {% url 'app01:reg' %}
    {% url 'app02:reg' %}
# 其實只要保證名字不衝突 就沒有必要使用名稱空間
"""
一般情況下 有多個app的時候我們在起別名的時候會加上app的字首
這樣的話就能夠確保多個app之間名字不衝突的問題
"""
urlpatterns = [
    url(r'^reg/',views.reg,name='app01_reg')
]
urlpatterns = [
    url(r'^reg/',views.reg,name='app02_reg')
]

偽靜態(瞭解)

"""
靜態網頁
	資料是寫死的 萬年不變
	
偽靜態
	將一個動態網頁偽裝成靜態網頁
	
	為什麼要偽裝呢?
		https://www.cnblogs.com/Dominic-Ji/p/9234099.html
		偽裝的目的在於增大本網站的seo查詢力度
		並且增加搜尋引擎收藏本網上的概率
	
	搜尋引擎本質上就是一個巨大的爬蟲程式
	
	總結:
		無論你怎麼優化 怎麼處理
		始終還是幹不過RMB玩家
"""
urlpatterns = [
    url(r'^reg.html',views.reg,name='app02_reg')
]

虛擬環境(瞭解)

"""
在正常開發中 我們會給每一個專案配備一個該專案獨有的直譯器環境
該環境內只有該專案用到的模組 用不到一概不裝

linux:缺什麼才裝什麼

虛擬環境
	你每建立一個虛擬環境就類似於重新下載了一個純淨的python直譯器
	但是虛擬環境不要建立太多,是需要消耗硬碟空間的

擴充套件:
	每一個專案都需要用到很多模組 並且每個模組版本可能還不一樣
	那我該如何安裝呢? 一個個看一個個裝???
	
	開發當中我們會給每一個專案配備一個requirements.txt檔案
	裡面書寫了該專案所有的模組即版本
	你只需要直接輸入一條命令即可一鍵安裝所有模組即版本
"""

django版本區別

"""
1.django1.X路由層使用的是url方法
	而在django2.Xhe3.X版本中路由層使用的是path方法
	url()第一個引數支援正則
	path()第一個引數是不支援正則的 寫什麼就匹配什麼
	
	
	如果你習慣使用path那麼也給你提供了另外一個方法
		from django.urls import path, re_path
		from django.conf.urls import url
		
		re_path(r'^index/',index),
    url(r'^login/',login)
  2.X和3.X裡面的re_path就等價於1.X裡面的url
 
 
2.雖然path不支援正則 但是它的內部支援五種轉換器
	path('index/<int:id>/',index)
	# 將第二個路由裡面的內容先轉成整型然後以關鍵字的形式傳遞給後面的檢視函式

	def index(request,id):
    print(id,type(id))
    return HttpResponse('index')
    
  
  
  str,匹配除了路徑分隔符(/)之外的非空字串,這是預設的形式
	int,匹配正整數,包含0。
	slug,匹配字母、數字以及橫槓、下劃線組成的字串。
	uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
	path,匹配任何非空字串,包含了路徑分隔符(/)(不能用?)
	
3.除了有預設的五個轉換器之外 還支援自定義轉換器(瞭解)
	class MonthConverter:
    regex='\d{2}' # 屬性名必須為regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return value # 匹配的regex是兩個數字,返回的結果也必須是兩個數字
	
	
	from django.urls import path,register_converter
	from app01.path_converts import MonthConverter

	# 先註冊轉換器
	register_converter(MonthConverter,'mon')

	from app01 import views


	urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/', 	views.article_detail, name='aaa'),

]


4.模型層裡面1.X外來鍵預設都是級聯更新刪除的
但是到了2.X和3.X中需要你自己手動配置引數
	models.ForeignKey(to='Publish')
	
	models.ForeignKey(to='Publish',on_delete=models.CASCADE...)
"""

三板斧

"""
HttpResponse
	返回字串型別
render
	返回html頁面 並且在返回給瀏覽器之前還可以給html檔案傳值
redirect
	重定向
"""
# 檢視函式必須要返回一個HttpResponse物件  正確   研究三者的原始碼即可得處結論
The view app01.views.index didn't return an HttpResponse object. It returned None instead.

# render簡單內部原理
		from django.template import Template,Context
    res = Template('<h1>{{ user }}</h1>')
    con = Context({'user':{'username':'jason','password':123}})
    ret = res.render(con)
    print(ret)
    return HttpResponse(ret)

JsonResponse物件

"""
json格式的資料有什麼用?
	前後端資料互動需要使用到json作為過渡 實現跨語言傳輸資料

前端序列化
	JSON.stringify()					json.dumps()
	JSON.parse()							json.loads()
"""
import json
from django.http import JsonResponse
def ab_json(request):
    user_dict = {'username':'jason好帥哦,我好喜歡!','password':'123','hobby':'girl'}

    l = [111,222,333,444,555]
    # 先轉成json格式字串
    # json_str = json.dumps(user_dict,ensure_ascii=False)
    # 將該字串返回
    # return HttpResponse(json_str)
    # 讀原始碼掌握用法
    # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
    # In order to allow non-dict objects to be serialized set the safe parameter to False.
    # return JsonResponse(l,safe=False)  
    # 預設只能序列化字典 序列化其他需要加safe引數	

form表單上傳檔案及後端如何操作

"""
form表單上傳檔案型別的資料
	1.method必須指定成post
	2.enctype必須換成formdata

"""
def ab_file(request):
    if request.method == 'POST':
        # print(request.POST)  # 只能獲取普通的簡直對資料 檔案不行
        print(request.FILES)  # 獲取檔案資料
        # <MultiValueDict: {'file': [<InMemoryUploadedFile: u=1288812541,1979816195&fm=26&gp=0.jpg (image/jpeg)>]}>
        file_obj = request.FILES.get('file')  # 檔案物件
        print(file_obj.name)
        with open(file_obj.name,'wb') as f:
            for line in file_obj.chunks():  # 推薦加上chunks方法 其實跟不加是一樣的都是一行行的讀取
                f.write(line)

    return render(request,'form.html')

request物件方法

def index(request):
    '''
    request:django封裝的物件,它的類是WSGIRequest,它裡面包含了所有http請求的東西
    '''
    print(request)
    print(type(request))
    # from django.core.handlers.wsgi import WSGIRequest
    #######################1 講過的
    print(request.method)
    print(request.GET)
    print(request.POST)

    ########################2 新講的path,get_full_path,META,FIELS,body
    # 自定製請求頭
    # 上傳檔案使用的編碼方式是form-data,預設編碼方式urlencoded
    print(request.is_ajax()) # 是不是ajax請求
    print(request.path)      # 請求路徑
    print(request.get_full_path()) # 請求全路徑,帶資料

    # print(request.body)      # 請求體,二進位制,如果傳檔案,這個報錯
    '''
    使用form表單,預設情況下資料被轉成name=lqz&password=123放到請求體中
    request.POST其實是從body中取出bytes格式的,轉成了字典
    requet.GET其實是把路徑中?後面的部分拆出來,轉成了字典
    '''
    print(request.encoding) # 客戶端向服務端傳遞時,使用的編碼方法

    print(request.META)    # 重點,字典,一堆東西,請求使用者的ip地址,請求頭中資料,使用者自定製請求頭的資料
    '''
    把請求頭的key值部分統一加HTTP_  並且全部轉成大寫
    '''
    print(request.META['REMOTE_ADDR'])  # 客戶端的ip地址
    print(request.FILES)  # 客戶端上傳的檔案



    ########################3 暫時不用關注(後面會講)
    print(request.COOKIES) # 空字典
    print(request.session) # session物件
    print(request.user)    # 匿名使用者
    return HttpResponse('ok')

FBV與CBV

# 檢視函式既可以是函式也可以是類
def index(request):
  return HttpResponse('index')

# CBV
    # CBV配置路由
url(r'^login/',views.MyLogin.as_view())


from django.views import View


	class MyLogin(View):
    	def get(self,request):
        	return render(request,'form.html')

    	def post(self,request):
       		return HttpResponse('post方法')
      
"""
FBV和CBV各有千秋
CBV特點
	能夠直接根據請求方式的不同直接匹配到對應的方法執行
	
	內部到底是怎麼實現的?
		CBV內部原始碼(******)
"""

Pycharm的自動提示

from django.core.handlers.wsgi import WSGIRequest
# pycharm的自動提示
request=request  # type: WSGIRequest