├── .gitignore ├── README.md ├── db.sqlite3 ├── django_rest_framework_swagger_tutorial ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── musics ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── serializers.py ├── tests.py └── views.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | migrations/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-rest-framework-swagger-tutorial 2 | 3 | Django-REST-Swagger 基本教學 4 | 5 | * [Youtube Tutorial](https://youtu.be/ayTF26EIMFU) 6 | 7 | 相信大家在網路上一定都看過 **API 文件**, 8 | 9 | 那我們該如何撰寫 **API 文件** 給別人看呢 ? 10 | 11 | 今天我要教大家使用 [drf-yasg](https://github.com/axnsan12/drf-yasg) 來完成他 !! 12 | 13 | ***溫馨小提醒*** 14 | 15 | 建議大家先對 [Django](https://github.com/django/django) 以及 [Django REST framework](http://www.django-rest-framework.org/) ( DRF ) 有基礎的知識。 16 | 17 | 如果還不熟的人,可以先閱讀我之前寫的 18 | 19 | [Django 基本教學 - 從無到有 Django-Beginners-Guide](https://github.com/twtrubiks/django-tutorial) 20 | 21 | 以及 22 | 23 | [Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide](https://github.com/twtrubiks/django-rest-framework-tutorial) 24 | 25 | 先建立一些基本觀念,再來看這篇比較清楚。 26 | 27 | ## 教學 28 | 29 | 我們依照 [Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide](https://github.com/twtrubiks/django-rest-framework-tutorial) 這篇繼續延伸下去。 30 | 31 | 請在你的命令提示字元 (cmd ) 底下輸入 32 | 33 | 安裝 [drf-yasg](https://github.com/axnsan12/drf-yasg) 34 | >pip install drf-yasg 35 | 36 | ### drf-yasg 設定 37 | 38 | ***請記得要將 [drf-yasg](https://github.com/axnsan12/drf-yasg) 加入設定檔*** 39 | 40 | 請在 [settings.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/django_rest_framework_swagger_tutorial/settings.py) 裡面的 **INSTALLED_APPS** 加入下方程式碼 41 | 42 | ```python 43 | INSTALLED_APPS = ( 44 | ... 45 | 'django.contrib.staticfiles', 46 | 'drf_yasg', 47 | 'rest_framework', 48 | ) 49 | ``` 50 | 51 | 接著我們設定 Routers 路由 ,請將 [urls.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/django_rest_framework_swagger_tutorial/urls.py) 增加一些程式碼 52 | 53 | ```python 54 | ...... 55 | 56 | schema_view = get_schema_view( 57 | openapi.Info( 58 | title="Snippets API", 59 | default_version='v1', 60 | description="Test description", 61 | terms_of_service="https://www.google.com/policies/terms/", 62 | contact=openapi.Contact(email="contact@snippets.local"), 63 | license=openapi.License(name="BSD License"), 64 | ), 65 | # public=True, 66 | public=False, # need login 67 | permission_classes=[permissions.AllowAny], 68 | ) 69 | 70 | urlpatterns = [ 71 | path('admin/', admin.site.urls), 72 | 73 | # for rest_framework 74 | path('api/', include(router.urls)), 75 | # for rest_framework auth 76 | path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), 77 | 78 | re_path('swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), 79 | re_path('swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), 80 | re_path('redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), 81 | ] 82 | ``` 83 | 84 | 最後執行 Django ,然後瀏覽 [http://127.0.0.1:8000/swagger/](http://127.0.0.1:8000/swagger/) 85 | 86 | 你應該會看到如下圖 (如果你沒看到任何東西,可以點一下 **Show/Hide** ) 87 | 88 | ![alt tag](http://i.imgur.com/qY9pz8N.png) 89 | 90 | 也可以瀏覽 [http://127.0.0.1:8000/redoc/](http://127.0.0.1:8000/redoc/) 91 | 92 | ![alt tag](https://i.imgur.com/WsHAMmk.png) 93 | 94 | ### 執行畫面 95 | 96 | 畫面非常漂亮,功能也非常完善, 97 | 98 | 我們可以在上面執行 **GET** , **POST** , **PUT** , **PATCH** , **DELETE** , 甚至可以直接看到執行結果, 99 | 100 | 我介紹幾個給大家當範例,看完大家就會比較了解 101 | 102 | ***POST*** 103 | 104 | 點選編號 1 的地方,他會幫你把範例貼到左手邊,修改完值之後,直接按 **Try it out!** 105 | 106 | ![](http://i.imgur.com/RtDc29v.png) 107 | 108 | 接著你會發現下面有 Response , 以這個範例來講,201 就是新增成功 109 | 110 | ![](http://i.imgur.com/y0tSltJ.png) 111 | 112 | ***GET*** 113 | 114 | 接著我們再去 **GET** ,我們剛剛新增的那筆的確有在裡面 115 | 116 | ![](http://i.imgur.com/rKf0KdN.png) 117 | 118 | 有沒有發現非常強大 :open_mouth: 119 | 120 | 接下來你可能會擔心,這樣我的資料不就會被任何人任意操作? 121 | 122 | 不用擔心,和之前介紹的 [授權 (Authentication)](https://github.com/twtrubiks/django-rest-framework-tutorial#授權-authentications-) 是一樣的。 123 | 124 | ## 授權( Authentication ) 125 | 126 | 在 [urls.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/django_rest_framework_swagger_tutorial/urls.py) 底下程式碼, 127 | 128 | ```python 129 | urlpatterns = [ 130 | ...... 131 | # for rest_framework auth 132 | path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), 133 | ...... 134 | ] 135 | ``` 136 | 137 | 在 [views.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/musics/views.py) 底下加入 **permission** 138 | 139 | ```python 140 | # Create your views here. 141 | class MusicViewSet(viewsets.ModelViewSet): 142 | queryset = Music.objects.all() 143 | serializer_class = MusicSerializer 144 | permission_classes = (IsAuthenticated,) 145 | ``` 146 | 147 | 在 [settings.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/django_rest_framework_swagger_tutorial/settings.py) 底下加入下方程式碼 148 | 149 | 設定 SWAGGER_SETTINGS 為使用 rest_framework login, logout 150 | 151 | ```python 152 | SWAGGER_SETTINGS = { 153 | 'LOGIN_URL': 'rest_framework:login', 154 | 'LOGOUT_URL': 'rest_framework:logout' 155 | } 156 | ``` 157 | 158 | 也要把 [urls.py](https://github.com/twtrubiks/django_rest_framework_swagger_tutorial/blob/master/django_rest_framework_swagger_tutorial/urls.py) 中的 public 設為 `False`. 159 | 160 | 可參考 [https://drf-yasg.readthedocs.io/en/stable/settings.html](https://drf-yasg.readthedocs.io/en/stable/settings.html) 161 | 162 | 執行 Django, 然後瀏覽 [http://127.0.0.1:8000/swagger/](http://127.0.0.1:8000/swagger/) 163 | 164 | 你會發現,當你沒有登入的時候,你是看不到這些 API 的內容, 165 | 166 | ![alt tag](http://i.imgur.com/b3rbEZw.png) 167 | 168 | 登入之後你才有權限可以看到這些資料 169 | 170 | 我的 帳號/密碼 設定為 twtrubiks/password123 , 171 | 172 | Swagger 的基本介紹我們就介紹到這邊,更多的說明可以參考 [drf-yasg](https://github.com/axnsan12/drf-yasg) 173 | 174 | ## 執行環境 175 | 176 | * Python 3.8 177 | 178 | ## Reference 179 | 180 | * [Django](https://www.djangoproject.com/) 181 | * [Django-REST-framework](https://www.django-rest-framework.org/) 182 | * [drf-yasg](https://github.com/axnsan12/drf-yasg) 183 | 184 | ## License 185 | 186 | MIT license 187 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twtrubiks/django_rest_framework_swagger_tutorial/ee1e03ad0e37020ce377b46c2c58d6e47cc1fc73/db.sqlite3 -------------------------------------------------------------------------------- /django_rest_framework_swagger_tutorial/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twtrubiks/django_rest_framework_swagger_tutorial/ee1e03ad0e37020ce377b46c2c58d6e47cc1fc73/django_rest_framework_swagger_tutorial/__init__.py -------------------------------------------------------------------------------- /django_rest_framework_swagger_tutorial/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for django_rest_framework_swagger_tutorial project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_rest_framework_swagger_tutorial.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /django_rest_framework_swagger_tutorial/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_rest_framework_swagger_tutorial project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.0.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-$i6y64%vy0+8rzm3)m3+ht1o-rm73)*nx(cwsl*n+5p9((fd-)' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | REST_FRAMEWORK = { 32 | 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning' 33 | } 34 | 35 | # Application definition 36 | 37 | INSTALLED_APPS = [ 38 | 'musics.apps.MusicsConfig', 39 | 'django.contrib.admin', 40 | 'django.contrib.auth', 41 | 'django.contrib.contenttypes', 42 | 'django.contrib.sessions', 43 | 'django.contrib.messages', 44 | 'django.contrib.staticfiles', 45 | 'drf_yasg', 46 | 'rest_framework', 47 | ] 48 | 49 | 50 | SWAGGER_SETTINGS = { 51 | 'LOGIN_URL': 'rest_framework:login', 52 | 'LOGOUT_URL': 'rest_framework:logout' 53 | } 54 | 55 | MIDDLEWARE = [ 56 | 'django.middleware.security.SecurityMiddleware', 57 | 'django.contrib.sessions.middleware.SessionMiddleware', 58 | 'django.middleware.common.CommonMiddleware', 59 | 'django.middleware.csrf.CsrfViewMiddleware', 60 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 61 | 'django.contrib.messages.middleware.MessageMiddleware', 62 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 63 | ] 64 | 65 | ROOT_URLCONF = 'django_rest_framework_swagger_tutorial.urls' 66 | 67 | TEMPLATES = [ 68 | { 69 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 70 | 'DIRS': [], 71 | 'APP_DIRS': True, 72 | 'OPTIONS': { 73 | 'context_processors': [ 74 | 'django.template.context_processors.debug', 75 | 'django.template.context_processors.request', 76 | 'django.contrib.auth.context_processors.auth', 77 | 'django.contrib.messages.context_processors.messages', 78 | ], 79 | }, 80 | }, 81 | ] 82 | 83 | WSGI_APPLICATION = 'django_rest_framework_swagger_tutorial.wsgi.application' 84 | 85 | 86 | # Database 87 | # https://docs.djangoproject.com/en/4.0/ref/settings/#databases 88 | 89 | DATABASES = { 90 | 'default': { 91 | 'ENGINE': 'django.db.backends.sqlite3', 92 | 'NAME': BASE_DIR / 'db.sqlite3', 93 | } 94 | } 95 | 96 | 97 | # Password validation 98 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 99 | 100 | AUTH_PASSWORD_VALIDATORS = [ 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 109 | }, 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 112 | }, 113 | ] 114 | 115 | 116 | # Internationalization 117 | # https://docs.djangoproject.com/en/4.0/topics/i18n/ 118 | 119 | LANGUAGE_CODE = 'en-us' 120 | 121 | TIME_ZONE = 'UTC' 122 | 123 | USE_I18N = True 124 | 125 | USE_TZ = True 126 | 127 | 128 | # Static files (CSS, JavaScript, Images) 129 | # https://docs.djangoproject.com/en/4.0/howto/static-files/ 130 | 131 | STATIC_URL = 'static/' 132 | 133 | # Default primary key field type 134 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field 135 | 136 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 137 | -------------------------------------------------------------------------------- /django_rest_framework_swagger_tutorial/urls.py: -------------------------------------------------------------------------------- 1 | """django_rest_framework_swagger_tutorial URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include, re_path 18 | from rest_framework.routers import DefaultRouter 19 | from musics.views import MusicViewSet 20 | from drf_yasg.views import get_schema_view 21 | from drf_yasg import openapi 22 | 23 | from rest_framework import permissions 24 | 25 | router = DefaultRouter() 26 | router.register('musics', MusicViewSet) 27 | 28 | schema_view = get_schema_view( 29 | openapi.Info( 30 | title="Snippets API", 31 | default_version='v1', 32 | description="Test description", 33 | terms_of_service="https://www.google.com/policies/terms/", 34 | contact=openapi.Contact(email="contact@snippets.local"), 35 | license=openapi.License(name="BSD License"), 36 | ), 37 | # public=True, 38 | public=False, # need login 39 | permission_classes=[permissions.AllowAny], 40 | ) 41 | 42 | urlpatterns = [ 43 | path('admin/', admin.site.urls), 44 | 45 | # for rest_framework 46 | path('api/', include(router.urls)), 47 | # for rest_framework auth 48 | path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), 49 | 50 | re_path('swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), 51 | re_path('swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), 52 | re_path('redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), 53 | ] 54 | -------------------------------------------------------------------------------- /django_rest_framework_swagger_tutorial/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_rest_framework_swagger_tutorial project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_rest_framework_swagger_tutorial.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_rest_framework_swagger_tutorial.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /musics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twtrubiks/django_rest_framework_swagger_tutorial/ee1e03ad0e37020ce377b46c2c58d6e47cc1fc73/musics/__init__.py -------------------------------------------------------------------------------- /musics/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /musics/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MusicsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'musics' 7 | -------------------------------------------------------------------------------- /musics/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class Musics(models.Model): 5 | song = models.TextField() 6 | singer = models.TextField() 7 | last_modify_date = models.DateTimeField(auto_now=True) 8 | created = models.DateTimeField(auto_now_add=True) 9 | 10 | class Meta: 11 | db_table = "musics" 12 | -------------------------------------------------------------------------------- /musics/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from musics.models import Musics 3 | 4 | 5 | class MusicSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Musics 8 | # fields = '__all__' 9 | fields = ('id', 'song', 'singer', 'last_modify_date', 'created') 10 | -------------------------------------------------------------------------------- /musics/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /musics/views.py: -------------------------------------------------------------------------------- 1 | from musics.models import Musics 2 | from musics.serializers import MusicSerializer 3 | 4 | from rest_framework import viewsets 5 | from rest_framework.permissions import IsAuthenticated 6 | 7 | 8 | # Create your views here. 9 | class MusicViewSet(viewsets.ModelViewSet): 10 | queryset = Musics.objects.all() 11 | serializer_class = MusicSerializer 12 | permission_classes = (IsAuthenticated,) 13 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==4.0.5 2 | djangorestframework==3.13.1 3 | drf-yasg==1.20.0 --------------------------------------------------------------------------------