中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用Django中裝飾器

發布時間:2020-08-25 10:14:12 來源:億速云 閱讀:155 作者:Leah 欄目:編程語言

這篇文章運用簡單易懂的例子給大家介紹怎么用Django中裝飾器,代碼非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

Python裝飾器是一個消除冗余的強大工具。隨著將功能模塊化為大小合適的方法,即使是最復雜的工作流,裝飾器也能使它變成簡潔的功能。

例如讓我們看看Django web框架,該框架處理請求的方法接收一個方法對象,返回一個響應對象:

def handle_request(request):
  return HttpResponse("Hello, World")

我最近遇到一個案例,需要編寫幾個滿足下述條件的api方法:

返回json響應

如果是GET請求,那么返回錯誤碼

做為一個注冊api端點例子,我將會像這樣編寫:

def register(request):
  result = None
  # check for post only
  if request.method != 'POST':
    result = {"error": "this method only accepts posts!"}
  else:
    try:
      user = User.objects.create_user(request.POST['username'],
                      request.POST['email'],
                      request.POST['password'])
      # optional fields
      for field in ['first_name', 'last_name']:
        if field in request.POST:
          setattr(user, field, request.POST[field])
      user.save()
      result = {"success": True}
    except KeyError as e:
      result = {"error": str(e) }
  response = HttpResponse(json.dumps(result))
  if "error" in result:
    response.status_code = 500
  return response

然而這樣我將會在每個api方法中編寫json響應和錯誤返回的代碼。這將會導致大量的邏輯重復。所以讓我們嘗試用裝飾器實現DRY原則吧。

裝飾器簡介

如果你不熟悉裝飾器,我可以簡單解釋一下,實際上裝飾器就是有效的函數包裝器,python解釋器加載函數的時候就會執行包裝器,包裝器可以修改函數的接收參數和返回值。舉例來說,如果我想要總是返回比實際返回值大一的整數結果,我可以這樣寫裝飾器:

# a decorator receives the method it's wrapping as a variable 'f'
def increment(f):
  # we use arbitrary args and keywords to
  # ensure we grab all the input arguments.
  def wrapped_f(*args, **kw):
    # note we call f against the variables passed into the wrapper,
    # and cast the result to an int and increment .
    return int(f(*args, **kw)) + 1
  return wrapped_f # the wrapped function gets returned.

現在我們就可以用@符號和這個裝飾器去裝飾另外一個函數了:

@increment
def plus(a, b):
  return a + b
  
result = plus(4, 6)
assert(result == 11, "We wrote our decorator wrong!")

裝飾器修改了存在的函數,將裝飾器返回的結果賦值給了變量。在這個例子中,'plus'的結果實際指向increment(plus)的結果。

對于非post請求返回錯誤

現在讓我們在一些更有用的場景下應用裝飾器。如果在django中接收的不是POST請求,我們用裝飾器返回一個錯誤響應。

def post_only(f):
  """ Ensures a method is post only """
  def wrapped_f(request):
    if request.method != "POST":
      response = HttpResponse(json.dumps(
        {"error": "this method only accepts posts!"}))
      response.status_code = 500
      return response
    return f(request)
  return wrapped_f

現在我們可以在上述注冊api中應用這個裝飾器:

@post_only
def register(request):
  result = None
  try:
    user = User.objects.create_user(request.POST['username'],
                    request.POST['email'],
                    request.POST['password'])
    # optional fields
    for field in ['first_name', 'last_name']:
      if field in request.POST:
        setattr(user, field, request.POST[field])
    user.save()
    result = {"success": True}
  except KeyError as e:
    result = {"error": str(e) }
  response = HttpResponse(json.dumps(result))
  if "error" in result:
    response.status_code = 500
  return response

現在我們就有了一個可以在每個api方法中重用的裝飾器。

發送json響應

為了發送json響應(同時處理500狀態碼),我們可以新建另外一個裝飾器:

def json_response(f):
  """ Return the response as json, and return a 500 error code if an error exists """
  def wrapped(*args, **kwargs):
    result = f(*args, **kwargs)
    response = HttpResponse(json.dumps(result))
    if type(result) == dict and 'error' in result:
      response.status_code = 500
return response

現在我們就可以在原方法中去除json相關的代碼,添加一個裝飾器做為代替:

post_only
@json_response
def register(request):
  try:
    user = User.objects.create_user(request.POST['username'],
                    request.POST['email'],
                    request.POST['password'])
    # optional fields
    for field in ['first_name', 'last_name']:
      if field in request.POST:
        setattr(user, field, request.POST[field])
    user.save()
    return {"success": True}
  except KeyError as e:
    return {"error": str(e) }

現在,如果我需要編寫新的方法,那么我就可以使用裝飾器做冗余的工作。如果我要寫登錄方法,我只需要寫真正相關的代碼:

@post_only
@json_response
def login(request):
  if request.user is not None:
    return {"error": "User is already authenticated!"}
  user = auth.authenticate(request.POST['username'], request.POST['password'])
  if user is not None:
    if not user.is_active:
      return {"error": "User is inactive"}
    auth.login(request, user)
    return {"success": True, "id": user.pk}
  else:
    return {"error": "User does not exist with those credentials"}

參數化你的請求方法

我曾經使用過Tubogears框架,其中請求參數直接解釋轉遞給方法這一點我很喜歡。所以要怎樣在Django中模仿這一特性呢?嗯,裝飾器就是一種解決方案!

例如:

def parameterize_request(types=("POST",)):
  """
  Parameterize the request instead of parsing the request directly.
  Only the types specified will be added to the query parameters.
  
  e.g. convert a=test&b=cv in request.POST to
  f(a=test, b=cv)
  """
  def wrapper(f):
    def wrapped(request):
      kw = {}
      if "GET" in types:
        for k, v in request.GET.items():
          kw[k] = v
      if "POST" in types:
        for k, v in request.POST.items():
          kw[k] = v
      return f(request, **kw)
    return wrapped
  return wrapper

注意這是一個參數化裝飾器的例子。在這個例子中,函數的結果是實際的裝飾器。

現在我就可以用參數化裝飾器編寫方法了!我甚至可以選擇是否允許GET和POST,或者僅僅一種請求參數類型。

@post_only
@json_response
@parameterize_request(["POST"])
def register(request, username, email, password,
       first_name=None, last_name=None):
  user = User.objects.create_user(username, email, password)
  user.first_name=first_name
  user.last_name=last_name
  user.save()
  return {"success": True}

現在我們有了一個簡潔的、易于理解的api。

使用functools.wraps保存docstrings和函數名

很不幸,使用裝飾器的一個副作用是沒有保存方法名(__name__)和docstring(__doc__)值:

def increment(f):
  """ Increment a function result """
  wrapped_f(a, b):
    return f(a, b) + 1
  return wrapped_f
  
@increment
def plus(a, b)
  """ Add two things together """
  return a + b
  
plus.__name__ # this is now 'wrapped_f' instead of 'plus'
plus.__doc__  # this now returns 'Increment a function result' instead of 'Add two things together'

這將對使用反射的應用造成麻煩,比如Sphinx,一個 自動生成文檔的應用。

為了解決這個問題,我們可以使用'wraps'裝飾器附加上名字和docstring:

from functools import wraps
  
def increment(f):
  """ Increment a function result """
  @wraps(f)
  wrapped_f(a, b):
    return f(a, b) + 1
  return wrapped_f
  
@increment
def plus(a, b)
  """ Add two things together """
  return a + b
  
plus.__name__ # this returns 'plus'
plus.__doc__  # this returns 'Add two things together'

使用'decorator'裝飾器

如果仔細看看上述使用裝飾器的方式,在包裝器聲明和返回的地方也有不少重復。

你可以安裝python egg 'decorator',其中包含一個提供裝飾器模板的'decorator'裝飾器!

使用easy_install:

$ sudo easy_install decorator

或者Pip:

$ pip install decorator

然后你可以簡單的編寫:

from decorator import decorator
  
@decorator
def post_only(f, request):
  """ Ensures a method is post only """
  if request.method != "POST":
    response = HttpResponse(json.dumps(
      {"error": "this method only accepts posts!"}))
    response.status_code = 500
    return response
  return f(request)

這個裝飾器更牛逼的一點是保存了__name__和__doc__的返回值,也就是它封裝了 functools.wraps的功能!

關于怎么用Django中裝飾器就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

连平县| 九寨沟县| 金昌市| 微山县| 惠东县| 万年县| 通化县| 米泉市| 樟树市| 赤水市| 邢台县| 仪征市| 江口县| 全南县| 旅游| 黄石市| 高台县| 新巴尔虎右旗| 新密市| 龙里县| 广灵县| 扶沟县| 梁山县| 湖北省| 茌平县| 遂昌县| 张家港市| 垣曲县| 顺平县| 东乌珠穆沁旗| 织金县| 延川县| 扶风县| 凤翔县| 钟山县| 绩溪县| 中西区| 石景山区| 镇原县| 武隆县| 五家渠市|