環境
MacOS Ventura
Python 3.10.7
Poetry 1.3.2
はじめに
この記事はこちらの続きです
https://qiita.com/ps0317ix/items/344471dd89a10ab520cd
早速始めよう
まずは、user用のディレクトリを作っていきます
python manage.py startapp user
models.pyを以下の内容に変更します。
今回はDjango-ninjaがメインなので、Djangoのモデルなどに関する詳しい説明は割愛
from django.db import models
from django.contrib.auth.models import PermissionsMixin, Group, Permission
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.utils.translation import gettext_lazy as _
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, email, password, **extra_fields):
if not email:
raise ValueError('Emailを入力して下さい')
email = self.normalize_email(email)
username = self.model.normalize_username(username)
user = self.model(username=username, email=email, **extra_fields)
user.set_password(password)
user.save(using=self.db)
return user
def create_user(self, username, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('is_staff=Trueである必要があります。')
if extra_fields.get('is_superuser') is not True:
raise ValueError('is_superuser=Trueである必要があります。')
return self._create_user(username, email, password, **extra_fields)
def update_or_create_user(self, username=None, email=None, password=None, username_new=None, **extra_fields):
try:
user = self.get(email=email)
updated = False
if username_new:
user.username = username_new
updated = True
for key, value in extra_fields.items():
if getattr(user, key) != value:
setattr(user, key, value)
updated = True
if updated:
user.save(using=self.db)
except self.model.DoesNotExist:
user = self.create_user(username, email, password, **extra_fields)
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_("username"), max_length=50, blank=True)
email = models.EmailField(_("email"), unique=True)
avatar = models.ImageField(_("avatar"), upload_to='avatars', blank=True)
avatar_url = models.URLField(_("avatar_url"), max_length=1500, blank=True)
router = models.JSONField(_("router"), blank=True, null=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
groups = models.ManyToManyField(Group, verbose_name=_("groups"), blank=True, related_name="users")
user_permissions = models.ManyToManyField(Permission, verbose_name=_("user permissions"), blank=True, related_name="users")
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True)
objects = UserManager()
USERNAME_FIELD = "email"
EMAIL_FIELD = "email"
REQUIRED_FIELDS = ['username']
class Meta:
db_table = 'user_user'
verbose_name = "user"
verbose_name_plural = "users"
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
続いてスキーマを定義します。
from pydantic.types import SecretStr
from ninja import Schema
from ninja import ModelSchema
from django.contrib.auth import get_user_model
class UserSchema(ModelSchema):
class Config:
model = get_user_model()
model_fields = ['id', 'username', 'email', 'is_staff', 'is_active', 'is_superuser']
class UseLogin(Schema):
username: str
email: str
password: SecretStr
class UserIn(Schema):
id: str
username: str
email: str
openai_api_key: str
class UserOut(Schema):
id: int
username: str
email: str
roles: list[str] = ['client']
上記ができたらapiを作成していきます
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from ninja_extra import (
http_get, http_post, http_generic, http_put, http_delete,
api_controller, status, ControllerBase, pagination
)
from ninja_extra.controllers.response import Detail
from ninja_jwt.authentication import JWTAuth
from .schema import UserSchema, UseLogin, UserIn, UserOut,
@api_controller('user', tags=['User'], auth=[JWTAuth()])
class UserController(ControllerBase):
user_model = get_user_model()
@http_get("/me", response=UserOut)
def get_user(self, request):
roles = []
if request.user.is_superuser:
roles.append('admin')
elif request.user.is_staff:
roles.append('staff')
else:
roles.append('client')
if len(request.user.avatar_url) > 0:
avatar = request.user.avatar_url
else:
avatar = request.user.avatar
return {
'id': request.user.id,
'username': request.user.username,
'email': request.user.email,
'avatar': avatar,
'roles': roles
}
@http_post('/create', response=UserSchema)
def create_user(self, payload: UseLogin):
return User.objects.create_user(**payload.dict())
@http_put('/update', response=UserSchema)
def update_user(self, request, payload: UserIn):
user = self.user_model.objects.update_or_create_user(
username=request.user.username,
email=request.user.email
)
return user
@http_delete('/{int:user_id}', response=Detail(status_code=status.HTTP_204_NO_CONTENT))
def delete_user(self, user_id: int):
user = self.get_object_or_exception(self.user_model, id=user_id)
user.delete()
return self.create_response('', status_code=status.HTTP_204_NO_CONTENT)
DJANGO_NINJA_TEMPLETEディレクトリに戻り、上記のController, appsを登録
from ninja_jwt.controller import NinjaJWTDefaultController
from ninja_extra import NinjaExtraAPI
from user.api import UserController # 追加
api = NinjaExtraAPI()
api.register_controllers(NinjaJWTDefaultController)
api.register_controllers(UserController) # 追加
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user', # 追加
]
migrationファイルを生成
python manage.py makemigrations
# ImageFieldを入れる場合、pip install Pillowしてください
そして、migrateを生成
python manage.py migrate
http://127.0.0.1:8000/api/docs
に遷移し、鍵マーク付きのエンドポイントが生成されていたら成功
確認
以下のコマンドでsuperuserを作成
python manage.py createsuperuser
/api/token/pairのエンドポイントで、登録したユーザー情報を入力し、tokenが生成されたら成功!!
最後に
圧倒的に少ないコード量で実装できて、速度もDjangoより早くて、docsなども自動生成されて、革命的に感じるのは私だけですかね。
こういったサービス運営やってるので、気になる方はぜひ
https://data-lab.project-g.co.jp?utm_source=Qiita&utm_medium=qiita&utm_campaign=qiita_20230313
参考
※2023/05/17追記
デプロイ編を公開しました
https://qiita.com/ps0317ix/items/07af7a863ff63ad3dc81