用Docker+Django+Next+TypeScript+ECS创建应用的故事(第3部分)〜从创建Django模式到获取数据〜
首先
这是上一次的续集。
上一篇文章:利用Docker+Django+Next+TypeScript+ECS创建应用的经验分享(第2部分)- 从Django的初始配置到创建模型
下一篇文章:利用Docker+Django+Next+TypeScript+ECS创建应用的经验分享(第4部分)- Django测试篇
这次我在Django项目中创建了一个架构,并写到了在浏览器中获取数据的地方。
1. 创建架构
使用以下方式创建新文件夹和文件:
myProject/
app/
app/
+ schema.py
...
api/
+ schema.py
+ utils/
+ validator.py
- validator.py
创建用于用户创建和编辑个人资料的Mutation的验证方法。
import re
from graphql import GraphQLError
def validate_blank(value):
if value == "":
raise GraphQLError("Value is required")
return value
def validate_too_long(value, num):
if len(value) > num:
raise GraphQLError("Value is too long")
return value
def validate_nickname(value):
validate_blank(value)
validate_too_long(value, 20)
return value
def validate_email(value):
match = re.match(r'[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$', value)
if not match:
raise GraphQLError("Invalid Email Address")
return value
def validate_password(value):
validate_blank(value)
if len(value) < 6:
raise GraphQLError("Password is too short")
- api/schema.py
创建用户,获取令牌,删除用户,编辑个人资料的Mutation,以及获取自己的个人资料和所有个人资料的查询将被创建。
作为扩展功能,使用relay。
import graphene
import graphql_jwt
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from graphene import relay
from graphql_jwt.decorators import login_required
from api.models import CustomUser, Profile
from api.utils.validator import validate_nickname, validate_email, validate_password
class UserNode(DjangoObjectType):
class Meta:
model = CustomUser
filter_fields = {
'email': ['exact'],
}
interfaces = (relay.Node,)
class ProfileNode(DjangoObjectType):
class Meta:
model = Profile
filter_fields = {
'nickname': ['exact', 'icontains'],
}
interfaces = (relay.Node,)
class CreateUserMutation(relay.ClientIDMutation):
class Input:
nickname = graphene.String(required=True)
email = graphene.String(required=True)
password = graphene.String(required=True)
user = graphene.Field(UserNode)
def mutate_and_get_payload(root, info, **input):
nickname = validate_nickname(input.get('nickname'))
email = validate_email(input.get('email'))
password = validate_password(input.get('password'))
user = CustomUser(
email=email,
)
user.set_password(password)
user.save()
profile = Profile(
nickname=nickname,
user=user
)
profile.save()
return CreateUserMutation(user=user)
class DeleteUserMutation(relay.ClientIDMutation):
class Input:
confirm = graphene.Boolean(required=True)
user = graphene.Field(UserNode)
@login_required
def mutate_and_get_payload(root, info, **input):
user = info.context.user
user.delete()
return DeleteUserMutation(user=None)
class UpdateProfileMutation(relay.ClientIDMutation):
class Input:
nickname = graphene.String(required=True)
profile = graphene.Field(ProfileNode)
@login_required
def mutate_and_get_payload(root, info, **input):
profile = info.context.user.profile
profile.nickname = validate_nickname(input.get('nickname'))
profile.save()
return UpdateProfileMutation(profile=profile)
class Mutation(graphene.AbstractType):
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
create_user = CreateUserMutation.Field()
delete_user = DeleteUserMutation.Field()
update_profile = UpdateProfileMutation.Field()
class Query(graphene.ObjectType):
my_profile = graphene.Field(ProfileNode)
all_profile = DjangoFilterConnectionField(ProfileNode)
@login_required
def resolve_my_profile(self, info, **kwargs):
return Profile.objects.get(user=info.context.user.id)
@login_required
def resolve_all_profile(self, info, **kwargs):
return Profile.objects.all()
- app/schema.py
将 api/schema.py 文件中的模式加载的方式进行描述。
import graphene
import api.graphql.schema
class Query(api.graphql.schema.Query, graphene.ObjectType):
pass
class Mutation(api.graphql.schema.Mutation, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query, mutation=Mutation)
- urls.py
添加用于操作数据的端点。
from django.contrib import admin
from django.urls import path
+ from graphene_django.views import GraphQLView
+ from app.schema import schema
+ from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
path('admin/', admin.site.urls),
+ path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
]
启动容器后,通过访问localhost:8000/graphql,即可显示GraphQL页面。
如果在GraphQL页面的左侧进行以下描述,就可以进行一系列的数据操作。
mutation CREATE_USER{
createUser(input: { nickname: "user", email: "user@example.com", password: "password" }) {
user {
id
}
}
}
mutation TOKEN_AUTH{
tokenAuth(email: "user@example.com", password: "password") {
token
}
}
mutation UPDATE_MYPROFILE{
updateProfile(input: {nickname: "user update"}) {
profile {
nickname
}
}
}
mutation DELETE_USER {
deleteUser(input: {confirm: true}) {
user {
id
}
}
}
query MY_PROFILE{
myProfile {
nickname
}
}
query ALL_PROFILE{
allProfile {
edges {
node {
nickname
}
}
}
}
如果用户没有进行令牌认证,则无法执行用户删除、个人资料编辑和个人资料获取操作。(假设在浏览器中使用Google Chrome)可以通过在Google Chrome中添加名为ModHeader的扩展来在请求标头中添加从graphql页面获取的令牌,从而使操作变得可行。
总结起来
我已经创建了与用户验证相关的用户模型和个人资料模型的模式,并写了操作数据的部分。
下次我想写测试Django项目模式的部分。
用Docker+Django+Next+TypeScript+ECS创建应用程序的故事(第4部分)——Django测试篇