1. 程式人生 > >Django Rest Framework之Tutorial 4分析與實踐

Django Rest Framework之Tutorial 4分析與實踐

前言

在DRF所有的練習中,Tutorial 4算是比較難以理解的一部分,因此對這一節做一分析。在開始閱讀之前,強烈建議你完成Tutorial 1-3的所有內容,我們以下所有的內容都會用到前面三個練習的程式碼。

知識準備

  1. 主鍵:若某一個屬性組(注意是組)能唯一標識一條記錄,該屬性組就是一個主鍵。主鍵不能重複,且只能有一個,也不允許為空。定義主鍵主要是為了維護關係資料庫的完整性。
  2. 外來鍵:外來鍵用於與另一張表的關聯,是能確定另一張表記錄的欄位。外來鍵是另一個表的主鍵,可以重複,可以有多個,也可以是空值。定義外來鍵主要是為了保持資料的一致性。
    這裡對Tutorial 4中的User表說明一下:
    auth_user表

    在這個表中,參閱Django官方文件中提到,如果沒有特別宣告,將預設新增一個id欄位並以該欄位作為主鍵。雖然auth_user不是我們主動建立的,在auth_user中,id還是作為主鍵:
    這裡寫圖片描述

正式開始

這一節首先向models.py中的Snippetmodle中寫入了兩個新的欄位:

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

我們重點關注第一行程式碼。其中涉及了一個函式models.ForeignKey

,先看Django官方的描述:

A many-to-one relationship. Requires two positional arguments: the class to which the model is related and the on_delete option.
一種多對一的關係。需要兩個引數:需要建立關係的model 類和on_delete選項。

model類和引數on_delete很好理解:

  • 我們需要告知model Sneppet要和model User建立關係
  • on_delete=models.CASCADE的意思如下:
    刪除:刪除主表時自動刪除從表。刪除從表,主表不變
    更新:更新主表時自動更新從表。更新從表,主表不變
  • 最後,related_name引數,如果看字面意思應該是關係名,這個有什麼用呢?官方的解釋太拗口,可以參考這篇部落格,後面我會用程式碼解釋這個關係名的具體用法。暫時可以這麼理解,我們的Snappet model和User model是一對多的關係,我們可以通過外來鍵表明某段“snappet”屬於某個“user”,我們還需要從某個“user”入手查詢屬於他的“snappet”,這就是related_name的用處。

現在我們可以這樣說:
Snippets表中的owner欄位是Snippets表的外來鍵
這裡寫圖片描述
這一節剩下的部分很好理解,不再贅述。
我們來解決上面提到的related_name引數的問題。
首先完成所有需要的程式碼,我下面給出的程式碼均來源於Tutorial 4,你的snipetts/models.py應該是這樣:

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

LEXERS=[item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0],item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())

class Snippet(models.Model):
    create=models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
    owner=models.ForeignKey('auth.User',related_name='snippets',on_delete=models.CASCADE)
    highlighted=models.TextField()

    class Meta:
        ordering = ('create',)
    def save(self,*args,**kwargs):
        lexer=get_lexer_by_name(self.language)
        linenos='table' if self.linenos else False
        options={'title':self.title} if self.title else {}
        formatter = HtmlFormatter(style=self.style,linenos=linenos,full=True,**options)
        self.highlighted=highlight(self.code,lexer,formatter)
        super(Snippet,self).save(*args,**kwargs)

snippets/serializers.py應該是這樣

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
from django.contrib.auth.models import User
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

然後執行:

rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

然後使用

python manage.py createsuperuser --email [email protected].com --username
admin
python manage.py createsuperuser --email [email protected].com --username
root

建立兩個使用者,使用者名稱可隨意
然後進入shell:

python manage.py shell

首先import:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from snippets.serializers import UserSerializer
from django.contrib.auth.models import User

然後我們直接操作Snippet寫入一部分資料:

u=User.objects.get(pk=1) //這是為了獲取一個使用者
s=Snippet(owner=u,code="print hello") //為獲取到的使用者寫入一段snippet
s.save() //儲存

檢視資料庫:

成功寫入了一條資料
最後,我們來使用related_name

u.snippets.all() //這裡就是使用related_name進行反向查詢
<QuerySet [<Snippet: Snippet object (1)>]> //執行結果

也就是說我們通過user中的一條記錄,查詢到了該使用者具有的snippet

在這一節開始,我們要向serializers.py檔案中加入如下程式碼:

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

如果你對serializer理解不太深,可暫且認為serializer是將model和json互相轉換的過程。
顯然,User model中沒有snippets這個欄位,當然,我們也大可不必再User的serializer中加入snippets這個欄位,User model仍然會很好的執行。這裡加入的原因是我們想在從model中獲取全部或某個使用者的時候同時獲得他擁有的snippets。
我們對serializers.PrimaryKeyRelatedField的作用描述如下:使用 PrimaryKeyRelatedField 將返回一個對應關係 model 的主鍵,參考知乎
對應到我們的練習中,snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())這句程式碼將返回當前序列化USER的在model Snippets中的記錄的所有主鍵。
我們用實驗來說明。不必修改程式碼,繼續上面的shell。
首先我們還是再加入一段snippet,不過這次使用另一個使用者:

>>> u=User.objects.get(pk=2)
>>> s=Snippet(owner=u,code="whoami")
>>> s.save()
>>> s=Snippet(owner=u,code="ifconfig")
>>> s.save()
>>> us=UserSerializer(u)
>>> us.data

如上,我們先使用id=2的使用者建立了兩條sneppts記錄,然後使用UserSerializer序列化了該使用者,最後我們輸出序列化結果:

{'id': 2, 'username': 'root', 'snippets': [2, 3]}

顯然,截止目前,我們能夠做的事情有:
- 能夠在Snippet model中插入和獲取資料,且能夠實現owner作為外來鍵
- 能夠使用User的Serializer對User model進行序列化和反序列化,且能夠反查snippet
這一節其他的程式碼很簡單,不再贅述。

這一節只做了一件事:

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

將上面的程式碼寫入到了snippets的view.py中的SnippetList類中。為什麼要這麼寫?參考Tutorial 3
首先(WHAT),我們知道SnippetList繼承於ListCreateAPIView類,這個類中替我們實現了列出序列化所有資料和反序列化建立一條記錄,也就是資料庫的select allinsert。函式perform_create在這裡被重寫了一次,這裡的重寫是OOP裡面的含義。可以在執行繼承來的ListCreateAPIView中的create()函式時新增一個額外的owner欄位。因此,serializer (注意上面的這段程式碼中serializer只是一個引數名而已,不是類名)也將首先反序列化owner欄位並儲存。當然,對應到我們實際的程式碼中,serializer實際上是SnippetSerializer,因為我們在SnippetList類中顯式聲明瞭serializer_class = SnippetSerializer。這裡可能有點繞,關於serializer.save這個用法,可以參考Tutorial 1中的程式碼,如下所示:
這裡寫圖片描述
其次(WHY),我們要問,既然ListCreateAPIView替我們完成了select allinsert功能,那麼為什麼我們要單獨把owner提出來?單獨為它寫一段程式碼?Good Question。事實上,我們可以不寫這一段程式碼,前提是,owner作為一個POST 資料包的引數而不是HTTP HEADER傳入
最後(WHERE),請記住,我們寫這段程式碼的位置是view.py,也就是說,我們應當從瀏覽器或者HTTP協議的角度去考慮這個函式,而不是從命令列。我們後面會有一個實驗解釋這個問題,現在,請把它寫入到你的view.py。

按照這節的要求,我們的serializer.py應該是下面這個樣子:

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
from django.contrib.auth.models import User
class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'owner')
class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

我們來研究下owner = serializers.ReadOnlyField(source='owner.username')這行程式碼。
首先從整體來說,這行程式碼和我們在Tutorial 1 這一節中做的類似,我們在序列化器中聲明瞭一個owner欄位。但是同樣的問題,為什麼SnippetSerializer在繼承了serializers.ModelSerializer類後還要再次宣告呢?我們用實驗來解釋。
第一步:先註釋掉這行程式碼,然後進入python manage.py shell
第二步:執行下面的程式碼:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
s=Snippet.objects.get(pk=1)
ser=SnippetSerializer(s)
ser.data

我們看到返回的資料為:

{'linenos': False, 'title': '', 'language': 'python', 'owner': 1, 'style': 'friendly', 'code': 'print hello', 'id': 1}

注意owner這個值,顯然不需要這一行程式碼,我們的序列化器依舊可以正常工作,這裡owner值為1,我們知道它代表User model中id=1的記錄。
第三步我們取消被註釋的程式碼,重複上面的步驟,返回的結果是:

{'title': '', 'code': 'print hello', 'owner': 'admin', 'linenos': False, 'style': 'friendly', 'language': 'python', 'id': 1}

這裡有一些有意思的變化,owner的值變為了admin。為什麼會有這樣的變化?原因在source='owner.username'這裡。
先看DRF官方關於source關鍵字的解釋:

The name of the attribute that will be used to populate the field. May be a method that only takes a self argument, such as URLField(source=’get_absolute_url’), or may use dotted notation to traverse attributes, such as EmailField(source=’user.email’). When serializing fields with dotted notation, it may be necessary to provide a default value if any object is not present or is empty during attribute traversal.
The value source=’*’ has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation.
Defaults to the name of the field.

為了方便我翻譯下這段話:

(source引數)是指用來填充欄位的屬性的名稱。可以是一個採用self關鍵字的方法,例如URLField(source='get_absolute_url'),或者可以使用點符號來遍歷屬性,例如EmailField(source =’user.email’),在使用點符號序列化欄位時,如果在屬性遍歷期間物件不存在或為空,則可能需要提供預設值。
source ='*'具有特殊含義,表示整個物件應該傳遞到該欄位。……
預設為該欄位的名稱

回到我們的程式碼owner = serializers.ReadOnlyField(source='owner.username')這行程式碼中。也就是說,如果我們將這行程式碼寫為上面的形式的時候,序列化器將從owner指向的User model的某條記錄中獲取其username欄位的值,如果我們寫為owner = serializers.ReadOnlyField(source='*'),那麼最終序列化出的owner的值就是當前Snippet物件,是的你沒有看錯,可以自行實驗一下。如果我們寫為owner = serializers.ReadOnlyField(),也就是預設值情況下,那麼最終序列化出的owner的值就是當前User物件,是的你也沒有看錯,可以自行實驗一下。DRF的文件說的也不是那麼明白。
OK,現在我的的序列化器會將owner序列化為User.username,並且是一個ReadOnly欄位。我們來看一下serializers.ReadOnlyField這段的意思。先通俗的說明一下,ReadOnlyField表示具有該屬性的欄位在從model序列化時可以正常返回其值,但是在從json反序列化為model時,不接受json中對該值的修改。結合我們的例子看,owner也應該是隻讀的。我們用實驗來解釋:
第一步修改serializer.py中的程式碼:

owner = serializers.ReadOnlyField(source='owner.username')

也就是沿用Tutorial 4中的程式碼。
第二步進入python manage.py shell,執行:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
s=Snippet.objects.get(pk=1)
ser=SnippetSerializer(s)
ser.data

這裡沒有什麼變化,輸出的內容和之前一樣:

{'title': '', 'code': 'print hello', 'style': 'friendly', 'id': 1, 'owner': 'admin', 'language': 'python', 'linenos': False}

還是注意owner的值,表明這行程式碼屬於admin使用者,現在,我們更新Snippet model中的這條記錄,嘗試將它的owner修改為root,並且為了對比,我們也修改code值。
第三步,繼續執行:

data=ser.data
data['owner']='root'
data['code']='env'
ser=SnippetSerializer(s,data=data)
ser.is_valid()
ser.save()
ser.data

這裡的輸出為:

{'id': 1, 'title': '', 'language': 'python', 'owner': 'admin', 'linenos': False, 'code': 'env', 'style': 'friendly'}

我們可以重新序列化一下s:

s=Snippet.objects.get(pk=1)
ser=SnippetSerializer(s)
ser.data

輸出為:

{'id': 1, 'title': '', 'language': 'python', 'owner': 'admin', 'linenos': False, 'code': 'env', 'style': 'friendly'}

顯然,我們的確修改code的值,但是owner的值並沒有變為root。當然,如果我們修改程式碼為:

owner = serializers.CharField(source='owner.username')

取消ReadOnly屬性,然後重複上面的步驟,就會在save的時候報錯,錯誤資訊為”update()函式無法對採用了source='owner.username'這類形式的變數進行save,需要重寫update函式或者加入readonly屬性。”
現在,我們解決最後一個疑問,為什麼要寫這行程式碼?

owner = serializers.ReadOnlyField(source='owner.username')

檢視我們的model.py:

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

LEXERS=[item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0],item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())

class Snippet(models.Model):
    create=models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
    owner=models.ForeignKey('auth.User',related_name='snippets',on_delete=models.CASCADE)
    highlighted=models.TextField()

    class Meta:
        ordering = ('create',)
    def save(self,*args,**kwargs):
        lexer=get_lexer_by_name(self.language)
        linenos='table' if self.linenos else False
        options={'title':self.title} if self.title else {}
        formatter = HtmlFormatter(style=self.style,linenos=linenos,full=True,**options)
        self.highlighted=highlight(self.code,lexer,formatter)
        super(Snippet,self).save(*args,**kwargs)

我們在宣告owner時程式碼如下:

owner=models.ForeignKey('auth.User',related_name='snippets',on_delete=models.CASCADE)

我們沒有指明owner為readonly,也沒有指明owner在序列化時要以User.username進行填充,這導致了兩個問題:

  • 我們可以像上一節一樣修改owner的值
  • 在序列化一個snippet物件後,owner的值是User.id

最重要的是,在model中,我們無法顯式宣告一個欄位為’read-only’,因為這是序列化器的事。所以,我們無法在model中設定owner為只讀。

小結

截止目前,我們實現了:

  • 向snippet model中加入了一個owner外來鍵,關聯到User model的主鍵
  • 我們在snippet model中將這種關係命名為snippets,從而我們可以由User反查Snippet
  • 我們在Snippet的序列化器中,聲明瞭owner欄位在序列化和反序列化時的行為如readonly,填充User.username
  • 我們在User的序列化器中加入了snippet欄位,使得序列化和反序列化一個user物件時將snippets加入到其中

思考一下,我們完成了嗎?我們上面所有的操作,都沒有到達VIEW層面,也就是HTTP層面,現在不論是誰,都可以查詢任意使用者的snippets,也可以更新任意使用者的snippets,除了你不能將其他使用者的snippet佔為己有。我們需要在VIEW層加入訪問許可權

我們跳過了Adding required permissions to viewsAdding login to the Browsable API兩節,這兩節內容很簡單,但你還是應該完成其中要求的程式碼。完成之後,我們遇到一個問題,我們加入的許可權控制為permissions.IsAuthenticatedOrReadOnly,也就是說,現在如果你通過瀏覽器登入成功之後,對任意使用者的snippets均具有讀寫許可權,如果登入失敗,則僅具有讀許可權。這不是我們想要的,至少只是其中一部分。我們想要實現的是登入之後的使用者,對自己的snippets具有完全的控制權,但是對其他使用者的snippets,則僅具有可讀權。我們需要實現自定義的permissions類:

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

我們研究一下這段程式碼,首先我們建立了一個類IsOwnerOrReadOnly,它繼承於permissions.BasePermission,然後我們重寫了方法has_object_permission。這個方法有三個引數,request引數很好理解,代表我們傳送的http請求物件,view引數,可以將其理解為我們在snippets/urls.py定義的各個url,snippets/urls.py如下:

from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns=[
    url(r'^users/$', views.UserList.as_view()),
    url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
    url(r'^snippets/$',views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$',views.SnippetDetail.as_view()),
]

urlpatterns=format_suffix_patterns(urlpatterns)

view引數代表上面的每一個url指向的view類,最終我們會在每個view類中宣告permission_classes = (permissions.IsAuthenticatedOrReadOnly,),這樣每次呼叫has_object_permission函式時引數view會對應到各個view類上。最後一個引數obj代表我們每個訪問請求的物件,對應到我們的程式碼中就是某個SnippetSerializer物件。可以將其理解為資源。
我們繼續研究has_object_permission函式:

def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

對於if request.method in permissions.SAFE_METHODS:,我們首先要知道permissions.SAFE_METHODS,代表什麼,啟動命令列,我們看一下:
這裡寫圖片描述
顯然是一個元祖,其中定義了三種請求方式:GETHEADOPTIONS,如果你對HTTP的方法的定義比較理解,你會提出一個問題,GET方法依舊可以攜帶引數,很多時候POST的內容也可以以GET的方式作為引數提交,如果僅僅以請求方式來判斷許可權,是否能夠保證安全?答案是可以,因為我們在urls.py中僅接受pk引數以GET的方式提交。
現在,不管你是否登入成功,是否是你要訪問的SnippetSerializer物件的owner,你都可以檢視SnippetSerializer物件序列化的內容(return True)。
最後我們對於request.methodPUTPOSTDELETE方式的請求,做了一個判斷,如果是某個SnippetSerializer物件的擁有者則返回True,否則返回False。
OK,完善後面的程式碼,我們來測試一下是否成功了!

測試

你可以使用DRF文件中介紹的方式來測試,我這裡採用burpsuite測試。

GET測試(List)

這裡寫圖片描述
成功獲取到了6組記錄

POST測試(Create)

這裡需要在HTTP頭中加入引數Authorization: Basic YWRtaW46cXdlcjEyMzQ=YWRtaW46cXdlcjEyMzQ=這段是【使用者名稱:密碼】的Base64編碼,在我測試時,即為admin:qwer1234的Base64編碼,這是BasicAuthentication的基本要求,具體請百度。同時也要加入Content-Type: application/json為HTTP頭。然後,我們嘗試建立一個屬於admin使用者的snippets:
這裡寫圖片描述
建立成功,你也可以測試一下將owner修改為root,看最後建立的Snippets物件的owner是否會隨著你的修改而變化。

PUT測試(Update)

這裡寫圖片描述
修改成功,我們嘗試修改一下以admin使用者的身份修改一下root使用者的snippet:
這裡寫圖片描述
很不錯,許可權控制程式碼成功發揮了作用。
對於其他的測試如DELETE可以自行測試。

完了嗎?

不要忘記,我們還有一個問題沒有解答。
view.py中的SnippetList類中為什麼要有下面這行程式碼?

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

不多說,我們先去把這個程式碼註釋掉,然後執行我們的程式,進行測試:
我們嘗試create一條snippets記錄:
這裡寫圖片描述
這裡寫圖片描述
報錯了,什麼意思呢?告訴我們owner_id這個欄位不能為空,至於為什麼是owner_id而不是owner欄位,參考Django官方文件,在這裡不再贅述。也就是說,程式碼沒有從我們我們的資料包中獲取到owner欄位的值,但是我們POST的資料中明明包含了owner的值為admin。
我們先通過一個實驗來解釋:
首先進入命令列,然後匯入我們需要的包:

python manage.py shell
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from snippets.serializers import UserSerializer
from django.contrib.auth.models import User

然後,我們嘗試直接寫入一條記錄到 model Snippet中:

 s=Snippet(code="print helloworld",owner="admin")

看起來很美好,我們寫入了一個屬於admin使用者的snippet,結果卻報錯了:
這裡寫圖片描述
原因是owner應該是一個User記錄而不是一個字串。
也就是說,如果我們修改view.py中內容如下:

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    def perform_create(self,serializer):
        serializer.save(owner=self.request.data['owner'])

我們從POST過去的json中獲取owner的話,仍然會報錯,應為它是一個字串而不是一個User記錄。
這裡我們需要理解request.user的含義,它從我們POST的資料包的HTTP頭中獲取使用者名稱,並且將它轉換為一個User記錄物件。如果我們將view.py修改為:

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    def perform_create(self,serializer):
        print(type(self.request.user))
        serializer.save(owner=self.request.user)

然後POST一條資料的話,可以看到request.user是一個User物件。
這裡寫圖片描述
這樣就能夠序列化並儲存了。

結語

寫這篇文章的目的,是因為我自己也是一個DRF的初學者,在做練習的時候發現練習4還是比較難理解,尤其對我這種只用過FLASK的人來說,因此,將我學習過程中的一些分析和理解分享出來,希望對初學者有用。

相關推薦

Django Rest FrameworkTutorial 4分析實踐

前言 在DRF所有的練習中,Tutorial 4算是比較難以理解的一部分,因此對這一節做一分析。在開始閱讀之前,強烈建議你完成Tutorial 1-3的所有內容,我們以下所有的內容都會用到前面三個練習的程式碼。 知識準備 主鍵:若某一個屬性組(注意是組)

DRF Django REST framework 頻率,響應器分頁器元件(六)

頻率元件 頻率元件類似於許可權元件,它判斷是否給予請求通過。頻率指示臨時狀態,並用於控制客戶端可以向API發出的請求的速率。 與許可權一樣,可以使用多個調節器。API可能會對未經身份驗證的請求進行限制,而對於經過身份驗證的請求則進行限制較少。 例如,可以將使用者限制為每分鐘最多60個請求,每天最多1000個請

DRF Django REST framework 路由器版本控制組件(七)

路由器 一些Web框架提供了用於自動確定應如何將應用程式的URL對映到處理傳入請求的邏輯的功能。 而DRF的路由器元件也提供了一種簡單,快速且一致的方式將檢視邏輯對映到一組URL上。 路由器元件的使用配合include 第一步:匯入模組 from rest_framework import routers

django rest framework 版本

一、前言 1、版本的重要性 在RESTful 規範中,有關版本的問題,用restful規範做開放介面的時候,使用者請求API,系統返回資料。但是難免在系統發展的過程中,不可避免的需要新增新的資源,或者修改現有資源。因此,改動升級必不可少,但是,作為平臺開發者,應該知道:一旦你的API開放出去,有人開始用了

django rest framework 解析器

quest code amp cati bmi name parse 內部 表單提交 一、前言 在前端向後臺發送form表單或者ajax數據的時候,django的content_type會拿到請求頭中的Content-Type屬性然後根據值進行解析。 將request.da

Django rest framework 許可權操作(原始碼分析二)

知識回顧  這一篇是基於上一篇寫的,上一篇謝了認證的具體流程,看懂了上一篇這一篇才能看懂, 當用戶訪問是 首先執行dispatch函式,當執行當第二部時: #2.處理版本資訊 處理認證資訊 處理許可權資訊 對使用者的訪問頻率進行限制 self

Django rest framework 版本控制(原始碼分析四)

基於上述分析 #2.處理版本資訊 處理認證資訊 處理許可權資訊 對使用者的訪問頻率進行限制 self.initial(request, *args, **kwargs) #2.1處理版本資訊 #version代表版本

django rest framework 檢視

在之前的django rest framework其他元件中,在檢視函式中繼承類都是rest_framework.view.APIView,這個APIView是繼承的django中的View並且做了封裝和方法重寫的。 那麼在django rest framework中,還有有沒有提供其他的類能夠繼承? 一、

django rest framework節流的原始碼流程剖析

檢視類: 1 class UserViewset(BaseView): 2 ''' 3 create: 4 建立使用者 5 retrieve: 6 7 ''' 8 queryset = User.objects.

django rest framework解析器的原始碼流程剖析

在初始化Request的時候: 1 def initialize_request(self, request, *args, **kwargs): 2 """ 3 Returns the initial request object. 4

django rest framework版本的原始碼流程剖析

和之前的認證一樣在initial函式中: 1 def initial(self, request, *args, **kwargs): 2 """ 3 Runs anything that needs to occur prior to calling t

django rest framework序列化的原始碼流程剖析

當要對資料物件進行序化列例項化的時候 1 def __new__(cls, *args, **kwargs): 2 # We override this method in order to automagically create 3 # `ListSeria

Django Rest framework 分頁

RESTful 規範 一、例項 分頁有三種方式 普通分頁,看第n頁,每頁顯示m條資料; 切割分頁,在n個位置,向後檢視m條資料; 加密分頁,這與普通分頁方式相似,不過對url中的請求頁碼進行加密。 1、路由 <1>、主路由 from django.urls

Django REST frameworkModelViewSet繼承關係

class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin,

Django REST framework API認證

密鑰 請求參數 odin 字符 signal 介紹 發布 項目 www. RESTful API 認證   和 Web 應用不同,RESTful APIs 通常是無狀態的, 也就意味著不應使用 sessions 或 cookies, 因此每個請求應附帶某種授權憑證,因為用

Django REST FrameWork中文教程4:驗證和許可權

目前,我們的API對誰可以編輯或刪除程式碼段沒有任何限制。我們想要一些更高階的行為,以確保: 程式碼段始終與建立者相關聯。 只有身份驗證的使用者可以建立片段。 只有片段的建立者可以更新或刪除它。 未經身份驗證的請求應具有完全只讀訪問許可權。 將資訊新增到

Django Rest Framework認證

結構 params 這就是 header 情況 繼續 拋出異常 列表生成式 相對 代碼基本結構   url.py: from django.conf.urls import url, include from web.views.s1_api import Tes

Django Rest Framework權限

port -a urn elf 原生 面向對象編程 對象權限 display 繼承 基本代碼結構   url.py: from django.conf.urls import url, include from app import views urlp

Django REST Framework頻率限制

開放平臺的API介面呼叫需要限制其頻率,以節約伺服器資源和避免惡意的頻繁呼叫 使用 自定義頻率限制元件:utils/thottle.py class MyThrottle(BaseThrottle): def __init__(self): self.history

Django REST Framework版本控制

何謂版本控制? 為什麼需要版本控制? 一個專案在升級迭代的時候,不會立馬拋棄舊的版本,甚至會出現多個版本共存同時維護的情況,因此需要版本控制。 版本控制做了什麼? 版本控制做的事情很簡單,在前後端分離的情況下,只是對請求做判斷,判斷這是哪個版本的請求,然後將版本資訊封裝入request物件中。 自定