使用Python创建RESTAPI #简介

我們這次要做的事情

之前我发布了一篇关于JWT认证的文章,但对于后端的解释几乎没有。因此,我决定将其拆成多次发布(也有一些空闲时间导致)。我考虑使用Go和Python两种编程语言,但由于Gin(Go语言的API框架)的日文文献几乎没有,所以我选择了更受欢迎的Python的Django。从今天开始,我将创建一个简单的系统,可以用来发布文章作为示例。

API的设计

API指的是应用程序编程接口,特别是在本文章中,它指的是根据客户端(如Web浏览器)的HTTP请求与数据库等进行交互,并返回特定数据格式(如JSON等)的响应的系统。这次的API端点设计如下。

pathmethodparamsdetail/api/usersGET, POSTnullfalse/api/user/:idGET, PATCH, PUT, DELETEid: inttrue/api/postsGET, POSTnullfalse/api/posts/:slugGET, PATCH, PUT, DELETEslug: stringtrue/api/tokenPOSTnulltrue/api/token/refreshPOSTnulltrue/api/token/logoutPOSTnulltrue

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

最终

下一次我们会做各种不同的事情。
谢谢你的辛苦。

广告
将在 10 秒后关闭
bannerAds