必備知識點 檢視

Formerly0^0發表於2024-03-28

4.檢視

1.檢視引數

# urls.py
urlpatterns = [
    path('login/', account.login, name="login"),
    path('auth/', order.auth, name='auth'),
]
# views.py
from django.shortcuts import HttpResponse


def login(request):
    return HttpResponse("login")

Request 是什麼?

requests是一個物件,存放了瀏覽器給咱們發過來的所有內容,所以含有:
- 請求相關所有的資料: 當前訪問的url、請求方式、...
- django額外新增的資料

1.1 request 方法

1.1.1 request.path_info
  • 只能獲取到路由地址,無法獲取到引數
  • 它保留了URL路徑中的任何編碼、特殊字元或斜槓等資訊。
http://localhost:8000/api/login/

def login(request):
    print(request.path_info) # /api/login/
    return HttpResponse("Hello, world. You're at the")
1.1.2 request.path
  • 只能獲取到路由地址,無法獲取到引數
  • 它包含在域名之後,在任何查詢引數之前
http://localhost:8000/api/login/

def login(request):
    print(request.path) # /api/login/
    return HttpResponse("Hello, world. You're at the")
1.1.3 request.GET
  • 該屬性包含了請求中透過GET方法傳送的所有引數。
  • 這些引數通常會附加在URL之後,以問號分隔。
  • 您可以使用引數的名字作為鍵來訪問單個引數,例如request.GET['page']也可以直接.get()獲取
http://localhost:8000/api/login/?xxx=serein&age=999

def login(request):
    print(request.GET) # <QueryDict: {'xxx': ['serein'], 'age': ['999']}>
    print(request.GET.get('xxx')) # serein
    print(request.GET['age']) # 999
    return HttpResponse("Hello, world. You're at the")
1.1.4 request.POST
  • request.POST:該屬性是一個類似字典的物件,包含了請求中透過POST方法傳送的所有引數。
  • 這些引數通常是透過HTML表單傳送的。
  • 您可以使用引數的名字作為鍵來訪問單個引數,例如request.POST['username'],也可以直接.get()獲取
1.1.5 request.method
  • 返回客戶端用於發起請求的HTTP方法,比如'GET','POST'等
http://localhost:8000/api/login/?xxx=serein&age=999

def login(request):
    print(request.method) # GET
    return HttpResponse("Hello, world. You're at the")
1.1.6 request.body
  • 獲取POST請求的請求體
  • 獲取到的資料是原始格式,資料格式什麼樣根據content-type決定
def login(request):
    print(request.body) # GET
    return HttpResponse("Hello, world. You're at the")
# b'{"code":"083Sjmll2yla694F3bll2DguCM2SjmlG","unionId":"oP6QCsyT_9bk1dfSaVf0GEV5Y-yE"}'  b'v1=123&v2=456'


# 請求體+請求頭       資料格式 b'v1=123&v2=456'  content-type格式:content-type:application/x-www-form-urlencoded
		# 可以這麼取值
		print(request.POST)
    print(request.POST.get("v1"))
    print(request.POST.get("v2"))
1.1.7 request.FILES
  • request.FILES:該屬性是一個類似字典的物件,包含了請求中透過檔案上傳元件傳送的所有檔案。
  • 當表單中包含檔案上傳欄位時,透過request.FILES可以訪問上傳的檔案。
  • 您可以使用檔案的名字作為鍵來訪問單個檔案,例如request.FILES['file'],也可以.get()獲取
  • content-type格式, multipart/form-data
file_obj = request.FILES.get("avatar")
        print(file_obj, type(file_obj))
        # 165917-16953731571c11.jpg <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>

        # 獲取檔名
        file_name = file_obj.name
        print(file_name, type(file_name))
1.1.8 request.get_full_path()
  • 即能獲取到路由地址又能獲取到完整的路由地址後面的引數
  • request.get_full_path():該方法返回請求URL的完整路徑,包括路徑部分和任何查詢引數。
  • 當您需要將完整URL作為字串使用時,這個方法非常有用。
def login(request):
    print(request.get_full_path())  # 獲取到GET請求的完整路由地址和引數
    # /app01/login/?username=111&password=111
    return render(request, "login.html")
1.1.9 request.header
  • 獲取請求的請求頭
1.1.10 request.COOKIES
  • 請求頭有個特殊的cookie,
  • 可以直接request.COOKIES獲取
1.1.11 request.resolver_match
  • 獲取requests中其他值

2. 返回json資料

  • 服務端返回json資料
  • JsonResponse模組
  • from django.http import JsonResponse
# 第一種方式:json模組序列化
def index(request):
    data = {"username": "serein", 'password': '1234'}
    # 先用json序列化
    data = json.dumps(data)
    # 三板斧物件
    response = HttpResponse(data)
    return response

# 第二種辦法
from django.http import JsonResponse
def index(request):
        data = {"username": "serein", 'password': '1234'}
    response = JsonResponse(data)
    return response

3. FBV和CBV

3.1 FBV和CBV

  • funtion 函式和路由之間的對映關係
  • class 類和路由之間的對映關係

3.2 CBV格式

  • 檢視層
# views.py
from django.shortcuts import render, HttpResponse
# 匯入view
from django.views import View

# 重寫類,繼承view
class LoginView(View):
  	# get請求
    def get(self, request):
        pass
        return HttpResponse("111")
		# post請求
    def post(self, request):
        return HttpResponse("111")
  • 路由層
from django.urls import path
from apps.api.views import LoginView

urlpatterns = [
    path('login/', LoginView.as_view())

3.3 CBV原始碼剖析

class View:
	# http_method_names : 存放了我們常用的請求方式
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
	
    # 繫結給類的函式,繫結方法
    @classonlymethod
    def as_view(cls, **initkwargs):
        # **initkwargs : 可變長關鍵字引數
        for key in initkwargs:
            # print(f"key :>>>> {key}") # key :>>>> pattern_name
            # 可變長關鍵字引數中的每一個鍵值對
            # 我們自己寫檢視類中沒有定義過 http_method_names ,只能從父類 View 裡面找
            if key in cls.http_method_names:
                # 
                raise TypeError(
                    'The method name %s is not accepted as a keyword argument '
                    'to %s().' % (key, cls.__name__)
                )
            # hasattr : 獲取到當前物件中的屬性
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))
		
        # 上面都沒有報錯,走到了 view 函式里面
        def view(request, *args, **kwargs):
            # 獲取到當前類的物件 這個 cls ---> Register
            self = cls(**initkwargs)
            # 獲取到了一個 當前的get方法
            self.setup(request, *args, **kwargs)
            # 必須接受一個位置引數叫request
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            # 觸發了 dispatch 方法 ---> 獲取到了get函式的返回值 
            # render
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    def setup(self, request, *args, **kwargs):
        # self : 當前Register例項化出來的物件
        # 有GET方法 並且 沒有 head屬性
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            # self.head 變成了我自己寫的 GET 方法
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
	
    # 走到了dispatch方法
    def dispatch(self, request, *args, **kwargs):
		# request.method.lower() :當前請求方法轉全小寫 
        if request.method.lower() in self.http_method_names:
            # 獲取到了當前物件中的get方法的函式記憶體地址
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        # get方法的函式記憶體地址呼叫
        return handler(request, *args, **kwargs)

    def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return HttpResponseNotAllowed(self._allowed_methods())

    def options(self, request, *args, **kwargs):
        """Handle responding to requests for the OPTIONS HTTP verb."""
        response = HttpResponse()
        response.headers['Allow'] = ', '.join(self._allowed_methods())
        response.headers['Content-Length'] = '0'
        return response

    def _allowed_methods(self):
        return [m.upper() for m in self.http_method_names if hasattr(self, m)]
  • 當我們啟動Django專案時
  • 會自動觸發路由中的方法,呼叫 as_view 方法並自執行
  • 在執行後我們檢視 as_view 方法的原始碼 發現
    • 在依次給我們的物件賦值後,最終返回了一個自執行的 dispatch 方法
  • 於是我們又去檢視了 dispatch 方法
    • 在 dispatch 內部 ,先是將請求方式轉換並進行校驗
    • 然後開始校驗需要呼叫的方法的呼叫位置,校驗成功並拿到需要執行的方法執行
  • 在自己寫的類中如果有相關的方法,會首先呼叫我們重寫的類方法,並返回執行結果
    • 如果自己的類裡面沒有該方法 會去自己的父類中呼叫 父類的方法
      • 如果父類 以及 基類 都找不到則報錯,丟擲異常

3.3 給CBV和FBV加裝飾器

3.3.1 給FBV加裝飾器
  • 只需要將自己定義的裝飾器放在指定的檢視函式上面用語法糖
def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        print(f"總耗時 :>>>>> {time.time() - start_time} s ")
        return res

    return inner


@timer
def register(request):
    time.sleep(2)
    return HttpResponse("ok")
3.3.2 給CBV加裝飾器
from django.utils.decorators import method_decorator


class Login(View):
    # 第一種方式
    # @timer
    # def get(self, request, *args, **kwargs):
    #     time.sleep(2)
    #     return HttpResponse("login")

    # 第二種方式 : 藉助Django內建的公共函式 method_decorator
    # @method_decorator(timer)
    # def get(self, request, *args, **kwargs):
    #     time.sleep(2)
    #     return HttpResponse("login")

    # 第三種方式:重寫 dispatch 方法做攔截
    def get(self, request, *args, **kwargs):
        time.sleep(2)
        return HttpResponse("login")

    def dispatch(self, request, *args, **kwargs):
        start_time = time.time()
        # 可以什麼都不寫
        # obj = super().dispatch(request, *args, **kwargs)
        # 可以放自己的類名和自己的物件 self
        obj = super(Login, self).dispatch(request, *args, **kwargs)
        print(f"總耗時 :>>>> {time.time() - start_time} s ")
        return obj

        # if request.method.lower() in self.http_method_names:
        #     handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        # else:
        #     handler = self.http_method_not_allowed
        # return handler(request, *args, **kwargs)

相關文章