{"id":13932934,"url":"https://github.com/twtrubiks/django-rest-framework-tutorial","last_synced_at":"2025-04-04T10:08:12.698Z","repository":{"id":47078839,"uuid":"87180320","full_name":"twtrubiks/django-rest-framework-tutorial","owner":"twtrubiks","description":"Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide 📝","archived":false,"fork":false,"pushed_at":"2021-10-12T07:15:12.000Z","size":75,"stargazers_count":861,"open_issues_count":5,"forks_count":181,"subscribers_count":32,"default_branch":"master","last_synced_at":"2025-03-28T09:07:39.929Z","etag":null,"topics":["django-rest-framework","pycharm","python","rest-api","restful-api","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twtrubiks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-04T11:49:19.000Z","updated_at":"2025-03-26T07:01:29.000Z","dependencies_parsed_at":"2022-08-23T05:40:34.770Z","dependency_job_id":null,"html_url":"https://github.com/twtrubiks/django-rest-framework-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-rest-framework-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-rest-framework-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-rest-framework-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-rest-framework-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twtrubiks","download_url":"https://codeload.github.com/twtrubiks/django-rest-framework-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157283,"owners_count":20893220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django-rest-framework","pycharm","python","rest-api","restful-api","tutorial"],"created_at":"2024-08-07T21:01:22.926Z","updated_at":"2025-04-04T10:08:12.679Z","avatar_url":"https://github.com/twtrubiks.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# django-rest-framework-tutorial\n\n`django \u003e 2.0` 可參考 [django2](https://github.com/twtrubiks/django-rest-framework-tutorial/tree/django2) 分支.\n\n`django \u003e 3.0` 可參考 [django3](https://github.com/twtrubiks/django-rest-framework-tutorial/tree/django3) 分支.\n\n Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide 📝\n\n* [Youtube Tutorial PART 1](https://youtu.be/lunVXqMVsrs)\n* [Youtube Tutorial PART 2](https://youtu.be/Qnir5iFpMyQ)\n* [Youtube Tutorial PART 3](https://youtu.be/3qoB3RVoOvA)\n* [Youtube Tutorial PART 4](https://youtu.be/yvH1-jx_-z4)\n* [Youtube Tutorial PART 5](https://youtu.be/YMtz7OSwIlE)\n* [Youtube Tutorial PART 6](https://youtu.be/jONV4Bfjq6g)\n\n透過 [Django REST framework](http://www.django-rest-framework.org/) ( DRF ) 建立 REST API 非常方便快速，\n\n REST API ? 這是什麼，可以吃嗎 ? 如果你想先對  REST API 有一些認識，可參考之前寫的 [認識 RESTful API](https://github.com/twtrubiks/django-rest-framework-tutorial/tree/master/RESTful-API-Tutorial)\n\n在這裡教大家建立自己的第一個 [Django-REST-framework](http://www.django-rest-framework.org/)  :smile:\n\n建議對 [Django](https://github.com/django/django) 還不熟的人，可以先閱讀我之前寫的 [Django 基本教學 - 從無到有 Django-Beginners-Guide](https://github.com/twtrubiks/django-tutorial)，\n\n先建立一些基本觀念，再來看 DRF 會比較清楚。\n\n## 教學\n\n請先確認電腦有安裝 [Python](https://www.python.org/)\n\n請在你的命令提示字元 (cmd ) 底下輸入\n\n安裝 [Django](https://github.com/django/django)\n\n\u003epip install django\n\n安裝 [Django-REST-framework](http://www.django-rest-framework.org/)\n\u003epip install djangorestframework\n\n基本上安裝應該沒什麼問題。\n\n### django-rest-framework 設定\n\n***請記得要將 [Django-REST-framework](http://www.django-rest-framework.org/) 加入設定檔***\n\n請在 settings.py 裡面的 **INSTALLED_APPS** 加入下方程式碼 (下圖)\n\n```python\nINSTALLED_APPS = (\n    ...\n    'rest_framework',\n    ...\n)\n```\n\n![alt tag](http://i.imgur.com/bm7cO0e.jpg)\n\n### 建立 Django App\n\n先建立一個觀念，在 [Django](https://github.com/django/django) 中，通常我們會依照 **功能** 去建立一個 App ， 例如範例的 musics ，代表他是 管理音樂 的部份。\n\n有了這個觀念之後，我們動手開始做吧～\n\n請在你的命令提示字元 (cmd ) 底下輸入\n\n\u003epython manage.py startapp musics\n\n***建立完請記得要將 App 加入設定檔***\n\n請在 settings.py 裡面的 **INSTALLED_APPS** 加入 musics (也就是你自己建立的 App 名稱)\n\n![alt tag](http://i.imgur.com/xP1MoFI.jpg)\n\n### Models\n\n定義出資料庫中的結構（schema），並且透過 Django 中的指令去建立資料庫。\n\n[Django](https://github.com/django/django) 預設是使用 [SQLite](https://www.sqlite.org/) ，如果想要修改為其他的資料庫，可以在 settings.py  裡面進行修改。\n\n首先，請先在 models.py 裡面增加下方程式碼 (下圖)\n\n```python\nfrom django.db import models\n\n\n# Create your models here.\nclass Music(models.Model):\n    song = models.TextField()\n    singer = models.TextField()\n    last_modify_date = models.DateTimeField(auto_now=True)\n    created = models.DateTimeField(auto_now_add=True)\n\n    class Meta:\n        db_table = \"music\"\n\n```\n\n![alt tag](http://i.imgur.com/gydF0x4.jpg)\n\n接著在命令提示字元 (cmd ) 底下輸入\n\n\u003epython manage.py makemigrations\n\n![alt tag](http://i.imgur.com/xH4Sm3s.jpg)\n\n\u003e python manage.py migrate\n\n![alt tag](http://i.imgur.com/CpcdT3X.jpg)\n\nmakemigrations ： 會幚你建立一個檔案，去記錄你更新了哪些東西。\n\nmigrate ： 根據 makemigrations 建立的檔案，去更新你的 DATABASE 。\n\n執行完上面的指令之後，\n\n你可以使用[SQLiteBrowser](http://sqlitebrowser.org/) 或  [PyCharm](https://www.jetbrains.com/pycharm/) 觀看 DATABASE，\n\n你會發現多出一個 **music** 的 table ( 如下圖 )\n\n![alt tag](http://i.imgur.com/xVbTtjq.jpg)\n\n有沒有注意到我們明明在 models.py 裡面就沒有輸入 id ，可是 database 裡面卻有 id 欄位，\n\n這是因為 Django 預設會幫你帶入，所以可以不用設定。\n\n### Serializers 序列化\n\nSerializers 序列化 是 DRF 很重要的一個地方 :star:\n\n主要功能是將 Python 結構序列化為其他格式，例如我們常用的 JSON。\n\n在 musics 裡面新增 serializers.py，並輸入下方程式碼\n\n```python\nfrom rest_framework import serializers\nfrom musics.models import Music\n\n\nclass MusicSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Music\n        # fields = '__all__'\n        fields = ('id', 'song', 'singer', 'last_modify_date', 'created')\n\n```\n\n![alt tag](http://i.imgur.com/KY5UwHW.jpg)\n\n如果你想要全部 fields ，可以使用第 8 行的寫法。\n\n2017/9/8 新增\n\n增加 `SerializerMethodField` 使用方法 ，可參考 [serializers.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/serializers.py)， days_since_created 為例\n\n ```python\nclass MusicSerializer(serializers.ModelSerializer):\n    days_since_created = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Music\n        # fields = '__all__'\n        fields = ('id', 'song', 'singer', 'last_modify_date', 'created', 'days_since_created')\n\n    def get_days_since_created(self, obj):\n        return (now() - obj.created).days\n ```\n\n更多說明請參考 [http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield](http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield)\n\n2018/2/11 新增\n\n有時候會需要自定義序列化，舉個例子，假如我希望將回傳的 singer 都轉成大寫這樣我要該怎麼辦 ?\n\n這邊不希望又多一個 property 回傳 ( singer1 之類的 )，所以這時候我們就必須自定義序列化，也就是\n\n透過 `.to_representation(self, value)` 這個方法，更多說明請參考 [Custom relational fields](http://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields)。\n\n範例寫法可參考 [serializers.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/serializers.py)\n\n```python\nfrom django.utils.timezone import now\nfrom rest_framework import serializers\nfrom musics.models import Music\n\n\nclass ToUpperCaseCharField(serializers.CharField):\n    def to_representation(self, value):\n        return value.upper()\n\n\nclass MusicSerializer(serializers.ModelSerializer):\n    days_since_created = serializers.SerializerMethodField()\n    singer = ToUpperCaseCharField()\n\n    class Meta:\n        model = Music\n        # fields = '__all__'\n        fields = ('id', 'song', 'singer', 'last_modify_date', 'created', 'days_since_created')\n\n    def get_days_since_created(self, obj):\n        return (now() - obj.created).days\n```\n\n這樣你就會發現回傳的 singer 都被轉成大寫了\n\n![alt tag](https://i.imgur.com/WsVG86d.png)\n\n### Views\n\n在  [Django 基本教學 - 從無到有 Django-Beginners-Guide](https://github.com/twtrubiks/django-tutorial) 中我們使用 views，\n\n而在 DRF 中提供我們可以使用另一種稱為 viewsets 。\n\n請在 views.py 裡輸入下方程式碼 (下圖)\n\n```python\n# Create your views here.\nfrom musics.models import Music\nfrom musics.serializers import MusicSerializer\n\nfrom rest_framework import viewsets\n\n\n# Create your views here.\nclass MusicViewSet(viewsets.ModelViewSet):\n    queryset = Music.objects.all()\n    serializer_class = MusicSerializer\n\n```\n\n![alt tag](http://i.imgur.com/GMSz7u7.jpg)\n\n只需要寫這樣，你就擁有 CRUD 的全部功能，是不是非常強大 :open_mouth:\n\n為什麼呢? 因為 DRF 的 **viewsets.ModelViewSet** 裡面幫你定義了這些功能，\n\n![alt tag](http://i.imgur.com/GHbUOT5.jpg)\n\n當然，如果你需要，也可以覆寫他。\n\n### Routers 路由\n\nDRF 提供 DefaultRouter 讓我們快速建立 Routers 路由。\n\n請先將 urls.py 裡面增加一些程式碼，如下圖\n\n```python\nfrom django.conf.urls import url, include\nfrom django.contrib import admin\nfrom rest_framework.routers import DefaultRouter\nfrom musics import views\n\nrouter = DefaultRouter()\nrouter.register(r'music', views.MusicViewSet)\n\nurlpatterns = [\n    url(r'^admin/', admin.site.urls),\n    url(r'^api/', include(router.urls))\n]\n\n```\n\n![alt tag](http://i.imgur.com/imdF1f8.jpg)\n\n最後執行 Django ， 然後瀏覽   [http://127.0.0.1:8000/api/](http://127.0.0.1:8000/api/)\n\n你應該會看到如下圖\n\n![alt tag](http://i.imgur.com/ZpmiVnG.jpg)\n\n恭喜你，成功了 :smile:\n\n接下來，讓我來測試 API 吧~\n\n### 測試 API\n\n在測試 API 之前，大家必須先了解一下什麼是 REST API\n\nREST API 全名為 RESTful API，它並不是一個新東西、新技術，它只是一個規範。\n\n簡單說明 :\n\nGET : 讀取資源\n\nPUT : 替換資源\n\nDELETE : 刪除資源\n\nPOST : 新增資源\n\nPATCH : 更新資源部份內容\n\n剩下更詳細的資料就麻煩大家 GOOGLE了，我在現在來 測試 API   :smiley:\n\n測試 API 的工具很多，在這裡我們使用 [Postman](https://www.getpostman.com/) ，大家可以用自己習慣的工具。\n\n#### POST\n\n我們先來新增幾筆資料，如下圖\n\n![alt tag](http://i.imgur.com/zalPhwM.jpg)\n\n在 步驟1 的地方輸入你的 API 的網址，範例為  [http://127.0.0.1:8000/api/music/](http://127.0.0.1:8000/api/music/)\n\n在 步驟2 body 的地方，填入 song 和 singer 的值，然後按下 Send，\n\n接著看 response ( 步驟3 )，也就是你新增進去 dabase 的資料。\n\n#### GET\n\n如果你想一次看裡面全部的資料，可以使用 [http://127.0.0.1:8000/api/music/](http://127.0.0.1:8000/api/music/)\n\n![alt tag](http://i.imgur.com/clilnZL.jpg)\n\n或是你只想看特定的某一筆，可以使用 [http://127.0.0.1:8000/api/music/2/](http://127.0.0.1:8000/api/music/2/)\n\n![alt tag](http://i.imgur.com/RHwAjpU.jpg)\n\n#### PUT\n\n如果你想修改特定資料，可以使用 [http://127.0.0.1:8000/api/music/2/](http://127.0.0.1:8000/api/music/2/)\n\n![alt tag](http://i.imgur.com/7v5U03P.jpg)\n\n當按下 send 之後，會看到 response ( 步驟3 )的地方回傳修改後的值。\n\n#### DELETE\n\n如果你想刪除特定資料，可以使用 [http://127.0.0.1:8000/api/music/3/](http://127.0.0.1:8000/api/music/3/)\n\n![alt tag](http://i.imgur.com/HjCCICb.jpg)\n\n執行後，你會發現 id=3 的資料被刪除了。\n\n![alt tag](http://i.imgur.com/tOQS5cq.jpg)\n\n### Performing raw SQL queries\n\n* [Youtube Tutorial PART 5](https://youtu.be/YMtz7OSwIlE)\n\n2018/2/11 新增\n\n雖然 Django ORM 使用起來很棒，又容易使用 ( 如不了解 Django ORM，請參考我之前的介紹文章 [Django ORM](https://github.com/twtrubiks/django-tutorial#django-orm) )，\n\n但有時候我們還是會希望使用 raw SQL ，像是邏輯比較複雜的，不適合使用 Django ORM 寫，畢竟 Django ORM 的底層\n\n還是 raw SQL，Django 提供兩種方法來完成他，分別是 **Performing raw queries** 以及 **Executing custom SQL directly**。\n\n這邊提醒一下，如果使用這種方法，請注意 [SQL injection protection](https://docs.djangoproject.com/en/1.11/topics/security/#sql-injection-protection)。\n\n更多詳細可參考 [https://docs.djangoproject.com/en/1.11/topics/db/sql/#performing-raw-queries](https://docs.djangoproject.com/en/1.11/topics/db/sql/#performing-raw-queries)\n\n#### Performing raw queries\n\n透過 `Manager.raw()`這個方法，可參考 [models.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/models.py)\n\n簡單說明一下這段 code，前端可以帶入 song 的名稱近來查詢，也可以不帶，不帶的話就是回傳全部\n\n```python\ndef fun_raw_sql_query(**kwargs):\n    song = kwargs.get('song')\n    if song:\n        result = Music.objects.raw('SELECT * FROM music WHERE song = %s', [song])\n    else:\n        result = Music.objects.raw('SELECT * FROM music')\n    return result\n```\n\n [views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/views.py) 中的片段 code\n\n```python\n# /api/music/raw_sql_query/\n@list_route(methods=['get'])\ndef raw_sql_query(self, request):\n    song = request.query_params.get('song', None)\n    music = fun_raw_sql_query(song=song)\n    serializer = MusicSerializer(music, many=True)\n    return Response(serializer.data, status=status.HTTP_200_OK)\n```\n\n這個方法有 map 到你的 models，所以一樣可以序列化\n\nrequest\n\n![alt tag](https://i.imgur.com/jz9aqi4.png)\n\nresponse\n\n![alt tag](https://i.imgur.com/0p3KN3e.png)\n\n更多詳細可參考 [https://docs.djangoproject.com/en/1.11/topics/db/sql/#performing-raw-queries](https://docs.djangoproject.com/en/1.11/topics/db/sql/#performing-raw-queries)\n\n#### Executing custom SQL directly\n\n有時候 `Manager.raw()` 是不夠的，像是你可能需要 queries 沒有完全 map 到 models 的資料，\n\n或是執行 UPDATE, INSERT, or DELETE。\n\n當我們使用這個方法時，是完全的繞過 model ，直接 access database。\n\n可參考 [models.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/models.py)\n\n簡單說明一下這段 code，前端可以帶入 id 和 song 來更新資料\n\n```python\ndef namedtuplefetchall(cursor):\n    # Return all rows from a cursor as a namedtuple\n    desc = cursor.description\n    nt_result = namedtuple('Result', [col[0] for col in desc])\n    return [nt_result(*row) for row in cursor.fetchall()]\n\n\ndef fun_sql_cursor_update(**kwargs):\n    song = kwargs.get('song')\n    pk = kwargs.get('pk')\n\n    '''\n    Note that if you want to include literal percent signs in the query,\n    you have to double them in the case you are passing parameters:\n    '''\n    with connection.cursor() as cursor:\n        cursor.execute(\"UPDATE music SET song = %s WHERE id = %s\", [song, pk])\n        cursor.execute(\"SELECT * FROM music WHERE id = %s\", [pk])\n        # result = cursor.fetchone()\n        result = namedtuplefetchall(cursor)\n    result = [\n        {\n            'id': r.id,\n            'song': r.song,\n            'singer': r.singer,\n            'last_modify_date': r.last_modify_date,\n            'created': r.created,\n        }\n        for r in result\n    ]\n\n    return result\n```\n\n補充一下上面英文註解的說明，假設今天我們使用 like 搜尋，也就是會包含 `%` 的符號，\n\n這時候我們必須重複 `%` 這個符號，也就是 `%%`，請看以下的例子，\n\n假如我想執行這個 sql\n\n```sql\nSELECT * FROM music WHERE song like 'song%'\n```\n\n在 `cursor.execute` 中，必須多加上一個 `%`\n\n```python\ncursor.execute(\"SELECT * FROM music WHERE song like 'song%%'\", [])\n```\n\n [views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/views.py) 中的片段 code\n\n 由於這個方法是沒有 map 到 model，所以我們沒辦法進行序列化，\n\n 這邊將直接回傳一個 dict 字典，\n\n```python\n# /api/music/{pk}/sql_cursor_update/\n@detail_route(methods=['put'])\ndef sql_cursor_update(self, request, pk=None):\n    song = request.data.get('song', None)\n    if song:\n        music = fun_sql_cursor_update(song=song, pk=pk)\n        return Response(music, status=status.HTTP_200_OK)\n```\n\nrequest\n\n![alt tag](https://i.imgur.com/0Qfyrra.png)\n\nresponse\n\n![alt tag](https://i.imgur.com/gVFgSPx.png)\n\n更多詳細可參考 [https://docs.djangoproject.com/en/1.11/topics/db/sql/#executing-custom-sql-directly](https://docs.djangoproject.com/en/1.11/topics/db/sql/#executing-custom-sql-directly)\n\n### 授權 (Authentications )\n\n在 REST API 中，授權很重要，如果沒有授權，別人一直任意不受限制的操作你的 API ，很危險，\n\n所以 DRF 有提供 Authentications，讓我們來試試看吧~\n\n首先，請在 views.py 裡面新增  permission_classes\n\n```python\n# Create your views here.\nfrom musics.models import Music\nfrom musics.serializers import MusicSerializer\n\nfrom rest_framework import viewsets\nfrom rest_framework.permissions import IsAuthenticated\n\n\n# Create your views here.\nclass MusicViewSet(viewsets.ModelViewSet):\n    queryset = Music.objects.all()\n    serializer_class = MusicSerializer\n    permission_classes = (IsAuthenticated,)\n```\n\n![alt tag](http://i.imgur.com/RbQrZLt.jpg)\n\n接著在 urls.py 裡面增加 api-auth\n\n```python\nfrom django.conf.urls import url, include\nfrom django.contrib import admin\nfrom rest_framework.routers import DefaultRouter\nfrom musics import views\n\nrouter = DefaultRouter()\nrouter.register(r'music', views.MusicViewSet)\n\nurlpatterns = [\n    url(r'^admin/', admin.site.urls),\n    url(r'^api/', include(router.urls)),\n    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n```\n\n![alt tag](http://i.imgur.com/YISdOvo.jpg)\n\n最後執行 Django ， 然後瀏覽   [http://127.0.0.1:8000/api/](http://127.0.0.1:8000/api/) ，你會發現右上角多了 Log in 的按鈕\n\n![alt tag](http://i.imgur.com/DxgSK9q.jpg)\n\n我們先使用我們在 [Django 基本教學 - 從無到有 Django-Beginners-Guide](https://github.com/twtrubiks/django-tutorial) 裡面學到的 建立超級使用者\n\n\u003epython manage.py createsuperuser\n\n![alt tag](http://i.imgur.com/wqacaCR.jpg)\n\n讓我們再次使用 POSTMAN，我們用 GET 當作範例\n\n#### GET 授權\n\n![alt tag](http://i.imgur.com/MoMLRB3.jpg)\n\n有注意到嗎? response 說我沒有 授權，\n\n所以這時候我們就必須再加上授權才能操作 API (如下圖)，我們可以操作 API 了\n\n我的 帳號/密碼 設定為 twtrubiks/password123\n\n![alt tag](http://i.imgur.com/8leY8ZH.jpg)\n\n2017/12/3 新增\n\n* [Youtube Tutorial PART 3](https://youtu.be/3qoB3RVoOvA)\n\n上面的方法是針對整個 `class` 設定權限，那我們可不可以依照 method 呢？\n\n幾個例子，我希望 GET 時不用權限，但是 POST 時就需要權限，這樣該怎麼做呢？\n\n可以參考 shares/[views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/shares/views.py)\n\n```python\nclass ShareViewSet(viewsets.ModelViewSet):\n    queryset = Share.objects.all()\n    serializer_class = ShareSerializer\n    parser_classes = (JSONParser,)\n\n    def get_permissions(self):\n        if self.action in ('create',):\n            self.permission_classes = [IsAuthenticated]\n        return [permission() for permission in self.permission_classes]\n\n    # [GET] api/shares/\n    def list(self, request, **kwargs):\n        users = Share.objects.all()\n        serializer = ShareSerializer(users, many=True)\n\n        return Response(serializer.data, status=status.HTTP_200_OK)\n\n    # [POST] api/shares/\n    def create(self, request, **kwargs):\n        name = request.data.get('name')\n        users = Share.objects.create(name=name)\n        serializer = ShareSerializer(users)\n\n        return Response(serializer.data, status=status.HTTP_201_CREATED)\n```\n\n透過`get_permissions`來決定是否需要權限（在這裡設定 `create`， 也就是 POST）。\n\n這個例子就是 **GET** 時**不用權限**，但是 **POST** 時就**需要權限**。\n\n更多詳細介紹可參考官網 [authentication](http://www.django-rest-framework.org/api-guide/authentication/)\n\n### Parsers\n\n在 REST framework 中有一個 [Parser classes](http://www.django-rest-framework.org/api-guide/parsers/#parsers) ，這個  Parser\nclasses 主要是能控制接收的 Content-Type ，\n\n例如說我規定 Content-Type 只接受 application/json ，這樣你就不能傳其他的 Content-Type ( 舉例 : text/plain ) 。\n\n通常如果沒有特別去設定 ，一般預設是使用 application / x-www-form-urlencode ，不過預設的可能不是你想要的或是\n\n說你想要設計只允許規範一種 Content-Type 。\n\n設定 Parsers 也很簡單，如果你希望全域的設定，可以加在 [settings.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/django_rest_framework_tutorial/settings.py)，\n\n這樣就代表我只允許 Content-Type  是 application/json 。\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': (\n        'rest_framework.parsers.JSONParser',\n    )\n}\n```\n\n也可以針對特定 view 或 viewsets 加以設定 ，直接在 [views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/views.py) 加上 parser_classes 即可\n\n```python\nclass MusicViewSet(viewsets.ModelViewSet):\n    queryset = Music.objects.all()\n    serializer_class = MusicSerializer\n    permission_classes = (IsAuthenticated,)\n    parser_classes = (JSONParser,)\n```\n\n當然，parser_classes 不只有 [JSONParser](http://www.django-rest-framework.org/api-guide/parsers/#jsonparser)，還有 [FormParser](http://www.django-rest-framework.org/api-guide/parsers/#formparser) ， [MultiPartParser](http://www.django-rest-framework.org/api-guide/parsers/#multipartparser) 等等\n\n更多資訊可參考\n[http://www.django-rest-framework.org/api-guide/parsers/#parsersr](http://www.django-rest-framework.org/api-guide/parsers/#parsersr)\n\n### Extra link and actions\n\n* [Youtube Tutorial PART 4](https://youtu.be/yvH1-jx_-z4)\n\n我們使用 REST framework 時，難免會有想要制定額外的 route ，這時候我們可以利用\n`@detail_route` 或 `@list_route`。\n\n範例程式碼可參考 [views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/views.py)\n\n***detail_route***\n\n使用方法很簡單，直接加上裝飾器 `@detail_route`  即可\n\n```python\n@detail_route(methods=['get'])\ndef detail(self, request, pk=None):\n    music = get_object_or_404(Music, pk=pk)\n    result = {\n        'singer': music.singer,\n        'song': music.song\n    }\n\n    return Response(result, status=status.HTTP_200_OK)\n```\n\n以上面這個例子來說， URL pattern:  `/api/music/{pk}/detail/`，\n\n如果你沒有額外指定，通常你的 url_path 就是你 function 命名的名稱，\n\n當然，我們也可以自己額外定義 url_path，只需要加上  url_path 參數，\n\n範例如下\n\n```python\n@detail_route(methods=['get'], url_path='detail_self')\ndef detail(self, request, pk=None):\n    music = get_object_or_404(Music, pk=pk)\n    result = {\n        'singer': music.singer,\n        'song': music.song\n    }\n\n    return Response(result, status=status.HTTP_200_OK)\n```\n\n以上面這個例子來說， URL pattern:  `/api/music/{pk}/detail_self/`，\n\n這樣就不會使用你的 function 做為 url_path 了。\n\n***list_route***\n\n使用方法很簡單，直接加上裝飾器 `@list_route`  即可\n\n```python\n@list_route(methods=['get'])\ndef all_singer(self, request):\n    music = Music.objects.values_list('singer', flat=True).distinct()\n    return Response(music, status=status.HTTP_200_OK)\n```\n\n以上面這個例子來說，URL pattern: `/api/music/all_singer/`\n\n他也有 url_path 的特性，如果要自定義，只需要加上 url_path 參數。\n\n看完了以上的例子，相信大家可以分辨 `@detail_route` 以及 `@list_route`的不同。\n\n更多資訊可參考 [http://www.django-rest-framework.org/api-guide/routers/#extra-link-and-actions](http://www.django-rest-framework.org/api-guide/routers/#extra-link-and-actions)\n\n### Testing\n\n先簡單介紹一下大家常聽到的 ***TDD*** 以及 ***BDD***\n\nTDD : Test-Driven Development。\n\nBDD : Behavior-driven development 。\n\n詳細地請大家再自行 GOOGLE，這邊要講 DRF 的 Testing，\n\n你也可以參考官網的教學　[http://www.django-rest-framework.org/api-guide/testing/](http://www.django-rest-framework.org/api-guide/testing/)\n\n或是你也可以參考我寫的範例\n[tests.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/tests.py)\n\n#### Test Case Scenarios\n\n* Create a music with API.\n* Retrieve a music with API.\n* Partial Update a music with API.\n* Update a music with API.\n* Delete a music with API.\n* Retrieve a music detail with API.\n* Get All singer with API.\n\n#### API Endpoints\n\nMusic\n\n* ***/api/music/ (Music create and list endpoint)***\n* ***/api/music/{music-id}/ (Music retrieve, update and partial update and destroy endpoint)***\n\n* ***/api/music/{music-id}/detail/ (Music retrieve detail endpoint)***\n\n* ***/api/music/all_singer/ (Music list singer endpoint)***\n\nUsage\n\n```python\npython manage.py test\n```\n\n![img](http://i.imgur.com/OTZ1IRD.png)\n\n因為本範例剛好只有建立一個 APP ，如果你有很多個 APP ，你也可以指定\n\n你要測試的 APP，範例如下\n\n```python\npython manage.py test [app 名稱]\n```\n\n```python\npython manage.py test musics\n```\n\n### Versioning\n\n* [Youtube Tutorial PART 6](https://youtu.be/jONV4Bfjq6g)\n\n有時候我們可能需要版本來控制 API ，當然沒版本的 API 也是可以被接受的，\n\n可參考 [Non-versioned systems can also be appropriate](https://www.infoq.com/articles/roy-fielding-on-versioning)。\n\n要設定 versioning，請先到 [settings.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/django_rest_framework_tutorial/settings.py) 加入下方設定，\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning'\n}\n```\n\n有很多方法可以實現，分別為\n\n`AcceptHeaderVersioning` `URLPathVersioning` `NamespaceVersioning` `HostNameVersioning` `QueryParameterVersioning`，\n\n由於 `AcceptHeaderVersioning` 這個方法通常被認為是最佳的設計，所以這邊就用它來介紹。\n\n使用序列化的不同來介紹 Versioning，[views.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/musics/views.py) 如下，\n\n```python\n# /api/music/version_api/\n@list_route(methods=['get'])\ndef version_api(self, request):\n    music = Music.objects.all()\n    if self.request.version == '1.0':\n        serializer = MusicSerializerV1(music, many=True)\n    else:\n        serializer = MusicSerializer(music, many=True)\n    return Response(serializer.data, status=status.HTTP_200_OK)\n```\n\n其實也很簡單，就是判斷 `self.request.version` 是否有值，\n\n如果 header 沒有帶入版本號，就會使用 `MusicSerializer` 進行序列化，\n\n![alt tag](https://i.imgur.com/kOuzqgG.png)\n\n如果 header 有帶入版本號，就會使用 `MusicSerializerV1` 進行序列化。\n\n![alt tag](https://i.imgur.com/kGRJmt2.png)\n\n其他的使用方法，請參考官網 [Versioning](http://www.django-rest-framework.org/api-guide/versioning/)。\n\n### Model Meta options\n\n`app_label`\n\n還記得文章前面提到的 `INSTALLED_APPS` 嗎 ? 如果你沒有將 model 寫在 `INSTALLED_APPS` 中，\n\n這時候你就必須在 Model Meta 中宣告 ( 否則會報錯 )，像下面這樣\n\n```python\nclass Music(models.Model):\n    song = models.TextField()\n    singer = models.TextField()\n    last_modify_date = models.DateTimeField(auto_now=True)\n    created = models.DateTimeField(auto_now_add=True)\n\n    class Meta:\n        db_table = \"music\"\n        app_label = \"music\"\n```\n\n可參考 [https://docs.djangoproject.com/en/1.11/ref/models/options/#app-label](https://docs.djangoproject.com/en/1.11/ref/models/options/#app-label)\n\n這邊的東西很多，我有用到就會慢慢補 :kissing_closed_eyes:\n\n更多詳細可參考 [https://docs.djangoproject.com/en/1.11/ref/models/options/#model-meta-options](https://docs.djangoproject.com/en/1.11/ref/models/options/#model-meta-options)\n\n### Multiple databases\n\n這邊的部分也蠻多的，有空我會補 :kissing_closed_eyes:\n\n更多詳細可參考 [https://docs.djangoproject.com/en/2.0/topics/db/multi-db/](https://docs.djangoproject.com/en/2.0/topics/db/multi-db/)\n\n#### Automatic database routing\n\n這部分我先簡單寫個範例，以後有情境我在將細節補上來:smiley:\n\n更多詳細可參考 [https://docs.djangoproject.com/en/2.0/topics/db/multi-db/#automatic-database-routing)](https://docs.djangoproject.com/en/2.0/topics/db/multi-db/#automatic-database-routing)\n\napi/[routers.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/api/routers.py)\n\n```python\nclass AuthRouter:\n    \"\"\"\n    A router to control all database operations on models in the\n    auth application.\n    \"\"\"\n    def db_for_read(self, model, **hints):\n        \"\"\"\n        Attempts to read auth models go to auth_db.\n        \"\"\"\n        if model._meta.app_label == 'musics':\n            return 'default'\n        return None\n\n    def db_for_write(self, model, **hints):\n        \"\"\"\n        Attempts to write auth models go to auth_db.\n        \"\"\"\n        if model._meta.app_label == 'auth':\n            return 'auth_db'\n        return None\n```\n\n在 [settings.py](https://github.com/twtrubiks/django-rest-framework-tutorial/blob/master/django_rest_framework_tutorial/settings.py) 中加上這段\n\n```python\nDATABASE_ROUTERS = ['api.routers.AuthRouter']\n```\n\n## 後記\n\n恭喜你，基本上到這裡，已經是一個非常簡單的  [Django-REST-framework](http://www.django-rest-framework.org/) ，趕快動手下去玩玩吧 :stuck_out_tongue:\n\n如果意猶未盡，延伸閱讀 :satisfied:\n\n* [Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide](https://github.com/twtrubiks/django-rest-framework-tutorial)\n\n* [DRF-dataTable-Example-server-side](https://github.com/twtrubiks/DRF-dataTable-Example-server-side) - DataTables Example (server-side) - Python Django REST framework\n\n* [Deploying_Django_To_Heroku_Tutorial](https://github.com/twtrubiks/Deploying_Django_To_Heroku_Tutorial) - Deploying a Django App To Heroku Tutorial\n\n* [結合 Django + jQuery 實現無限捲軸 Infinite Scroll 📝](https://github.com/twtrubiks/ptt_beauty_infinite_scroll)\n\n## 執行環境\n\n* Python 3.4.3\n\n## Reference\n\n* [Django](https://www.djangoproject.com/)\n* [Django-REST-framework](http://www.django-rest-framework.org/)\n\n## Donation\n\n文章都是我自己研究內化後原創，如果有幫助到您，也想鼓勵我的話，歡迎請我喝一杯咖啡:laughing:\n\n![alt tag](https://i.imgur.com/LRct9xa.png)\n\n[贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8)\n\n## License\n\nMIT license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwtrubiks%2Fdjango-rest-framework-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwtrubiks%2Fdjango-rest-framework-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwtrubiks%2Fdjango-rest-framework-tutorial/lists"}