使用Python创建RESTAPI #简介
我們這次要做的事情
之前我发布了一篇关于JWT认证的文章,但对于后端的解释几乎没有。因此,我决定将其拆成多次发布(也有一些空闲时间导致)。我考虑使用Go和Python两种编程语言,但由于Gin(Go语言的API框架)的日文文献几乎没有,所以我选择了更受欢迎的Python的Django。从今天开始,我将创建一个简单的系统,可以用来发布文章作为示例。
API的设计
API指的是应用程序编程接口,特别是在本文章中,它指的是根据客户端(如Web浏览器)的HTTP请求与数据库等进行交互,并返回特定数据格式(如JSON等)的响应的系统。这次的API端点设计如下。
slug在URL中经常与其他内容附加在一起(例如“ hello-world”)。从第五个开始,关于JWT的讨论将省略在这里。
这次需要的东西
首先,我会从本次必需的内容开始解释。
* Python 3
* Django
* Django Rest Framework
* pip
* virtualenv
virtualenv是一个用于创建和管理Python虚拟环境的软件包。借助它,我们可以轻松管理版本问题,不再为此困扰。在进行工作时,请务必进入虚拟环境。
pip是Python的软件包管理工具。如果您已经安装了Anaconda,它应该已经预装了。
包括了不必要的部分,整理起来大致是这样的。
appdirs==1.4.4
asgiref==3.2.10
black==21.6b0
certifi==2020.12.5
cffi==1.14.5
chardet==4.0.0
click==8.0.1
colorama==0.4.4
coreapi==2.3.3
coreschema==0.0.4
cryptography==3.4.7
defusedxml==0.7.1
Django==3.1
django-cors-headers==3.7.0
django-filter==2.4.0
django-templated-mail==1.1.1
django-versatileimagefield==2.0
djangorestframework==3.12.4
djangorestframework-jwt==1.11.0
djangorestframework-simplejwt==4.6.0
idna==2.10
itypes==1.2.0
Jinja2==3.0.0
jwt==1.2.0
MarkupSafe==2.0.0
mypy-extensions==0.4.3
oauthlib==3.1.0
pathspec==0.8.1
Pillow==8.2.0
pycparser==2.20
PyJWT==2.1.0
python-magic-bin==0.4.14
python-slugify==5.0.2
python3-openid==3.2.0
pytz==2021.1
requests==2.25.1
requests-oauthlib==1.3.0
six==1.16.0
social-auth-app-django==4.0.0
social-auth-core==4.1.0
sqlparse==0.4.1
text-unidecode==1.3
toml==0.10.2
uritemplate==3.0.1
urllib3==1.26.4
安装时,可以将所有内容写入一个文件,并同时进行安装。命令稍后提供。
安装和环境设置
首先创建虚拟环境。可以使用以下命令安装virtualenv。
pip install virtualenv
// pathが通っていない方はこちら
python -m pip install virtualenv
接下来创建虚拟环境。
virtualenv myenv
通过这个命令创建了一个名为myenv的虚拟环境(可以指定Python的版本)。
要启动虚拟环境,请进入myenv目录并输入以下命令。
D:/myenv> ../Scripts/activate.bat
(myenv) D:/myenv>
这样的话,会出现像(name)一样的东西,这就是标记。当它出现时,请在同一位置输入“deactivate”来取消激活。
然后,我们将在这里安装刚才介绍的东西。在相同的目录下,创建并安装刚才的requirements.txt文件。
(myenv) D:/myenv> pip install -r requirements.txt
偶尔安装程序需要耗费难以置信的时间,但让我们以佛心来等待吧。
因为已经安装了所有的东西,所以现在可以开始启动项目了。
(myenv) D:/myenv> django-admin startproject backend
// backendのところはプロジェクト名
移动文件夹。然后注册应用程序。
(myenv) D:/myenv> cd backend
(myenv) D:/myenv/backend> python manage.py startapp accounts
(myenv) D:/myenv/backend> python manage.py startapp posts
在backend目录下将创建名为”posts”和”accounts”的文件夹,结构如下所示。
backend
├─.pytest_cache
│ └─v
│ └─cache
├─.vscode
├─accounts
│ ├─migrations
│ │ └─__pycache__
│ └─__pycache__
├─backend
│ └─__pycache__
├─posts
│ ├─migrations
│ │ └─__pycache__
│ └─__pycache__
└─utils
└─__pycache__
现在我要说的是针对使用VScode的人。在.vscode文件夹(就是上面写的那个文件夹)中创建一个名为launch.json的文件,然后输入以下内容会很方便。
{
"version": "0.2.0",
"configurations": [
{
"name": "run server",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}\\manage.py",
"args": ["runserver"],
"console": "externalTerminal",
"stopOnEntry": false,
},
{
"name": "make migration files",
"request": "launch",
"cwd": "${workspaceFolder}",
"type": "python",
"program": "${workspaceFolder}\\manage.py",
"args": ["makemigrations"],
"console": "externalTerminal"
},
{
"name": "migrate",
"type": "python",
"request": "launch",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}\\manage.py",
"args": ["migrate"],
"console": "externalTerminal"
}
]
}
只需按下F5,就可以不用每次都输入命令执行了。
项目设置
在后端文件夹中有一个名为settings.py的文件,在那里注册之前添加的应用程序。
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # 追加
'corsheaders', #追加
'posts', # 追加
'accounts', #追加
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
今后注册新应用时,请务必执行相同操作。
我也要进行语言设置。
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
从一开始就是一个巨大的挑战,但我们将创建一个定制用户模型。因为以前曾试图在后期进行更改而陷入了泥潭,所以我们决定提前创建。并不需要从头到尾大幅度改变默认设置,但提前创建会相当方便。身份验证将使用电子邮件和密码。请将以下内容输入到accounts文件夹的models.py文件中。
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None, **extra_kwargs):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have an username')
user = self.model(
email = self.normalize_email(email),
username = username,
**extra_kwargs
)
user.set_password(password)
user.save(using = self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email = self.normalize_email(email),
username = username,
password = password
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(
verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.username
def get_absolute_url(self):
return '/users/{}'.format(self.id)
# 絶対書くこと(必須)
def has_perm(self, perm, obj=None):
return self.is_admin
# これも絶対書くこと(必須)
def has_module_perms(self, app_label):
return True
class Meta:
ordering = ['-date_joined']
我理解你想知道正在发生什么,但这件事情在幕后变得非常复杂,所以如果可以的话,请复制粘贴。
我们将在下一次讨论比这更个性化的方法。
把这个模型加入项目设置中。
from pathlib import Path
import os
from corsheaders.defaults import default_headers
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
CORS_ALLOWED_ORIGINS = [
#frontend側の設定
]
CORS_ALLOW_HEADERS = default_headers + ('contenttype', 'Authorization')
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'posts',
'accounts',
'rest_framework_simplejwt.token_blacklist', #JWT認証を使わないなら要らない
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'backend.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME':
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'accounts.User' #この部分を新たに追加
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny', ),
'DEFAULT_AUTHENTICATION_CLASSES':
('rest_framework_simplejwt.authentication.JWTAuthentication', ),
}
然后将该模型添加到管理站点中。进入位于同一目录下的 admin.py 文件,输入以下内容。
from django.contrib import admin
from .models import User
admin.site.register(User)
请最后进行迁移并启动服务器。
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
管理网站应该会打开。
只需要一种选项的话,以下是对”おまけ”的中文本地化释义:
“小赠品”
你觉得utils文件夹不存在吗?看来你很仔细呢。这是我自己做的一个东西,里面装着用来管理用户权限和限制的脚本。如果方便的话,可以试试看。
from rest_framework import permissions
class IsAdminOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_staff
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.posts.author == request.user
class IsSelfOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj == request.user
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken
class JWTAuthenticationSafe(JWTAuthentication):
def authenticate(self, request):
try:
return super().authenticate(request=request)
except InvalidToken:
return None
最终
下一次我们会做各种不同的事情。
谢谢你的辛苦。