mirror of
https://github.com/evgen-app/chess_rpg_backend.git
synced 2024-11-25 11:04:03 +03:00
added deck model and creation endpoint
This commit is contained in:
parent
acc37f8e82
commit
ba04ce8347
0
game/api/__init__.py
Normal file
0
game/api/__init__.py
Normal file
0
game/api/v1/__init__.py
Normal file
0
game/api/v1/__init__.py
Normal file
94
game/api/v1/serializers.py
Normal file
94
game/api/v1/serializers.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
from abc import ABC
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from game.models import Hero, Player, HeroInDeck, Deck
|
||||
|
||||
|
||||
class CreateHeroSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Hero
|
||||
fields = (
|
||||
"type",
|
||||
"idle_img",
|
||||
"attack_img",
|
||||
"die_img",
|
||||
"health",
|
||||
"attack",
|
||||
"speed",
|
||||
)
|
||||
|
||||
|
||||
class GetHeroSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Hero
|
||||
fields = (
|
||||
"added",
|
||||
"type",
|
||||
"idle_img",
|
||||
"attack_img",
|
||||
"die_img",
|
||||
"health",
|
||||
"attack",
|
||||
"speed",
|
||||
)
|
||||
|
||||
|
||||
class ListHeroSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Hero
|
||||
fields = (
|
||||
"uuid",
|
||||
"type",
|
||||
"idle_img",
|
||||
"attack_img",
|
||||
"die_img",
|
||||
"health",
|
||||
"attack",
|
||||
"speed",
|
||||
)
|
||||
|
||||
|
||||
class CreatePlayerSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Player
|
||||
fields = ("ton_wallet", "name")
|
||||
|
||||
|
||||
class DeckCreateSerializer(serializers.ModelSerializer):
|
||||
hero_ids = serializers.ListSerializer(
|
||||
child=serializers.UUIDField(), min_length=16, max_length=16
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Deck
|
||||
fields = ("hero_ids",)
|
||||
|
||||
def validate_hero_ids(self, value):
|
||||
for x in value:
|
||||
if not (hero := Hero.objects.filter(uuid=x)):
|
||||
raise ValidationError(f"Hero with uuid {x} doesn't exist")
|
||||
|
||||
if deck := HeroInDeck.objects.filter(hero=hero.first()):
|
||||
raise ValidationError(
|
||||
f"Hero with uuid {x} is already in deck with id {deck.first().deck.id}"
|
||||
)
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
deck = Deck.objects.create(player=self.context["request"].user)
|
||||
for x in validated_data["hero_ids"]:
|
||||
HeroInDeck.objects.create(hero_id=x, deck=deck)
|
||||
return deck
|
||||
|
||||
|
||||
class GetDeckSerializer(serializers.ModelSerializer):
|
||||
heroes = ListHeroSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Deck
|
||||
fields = ("player_uuid", "heroes")
|
||||
|
||||
def get_heroes(self, val):
|
||||
print(val)
|
|
@ -1,16 +1,29 @@
|
|||
from rest_framework import status
|
||||
|
||||
from rest_framework.generics import GenericAPIView, UpdateAPIView
|
||||
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin
|
||||
from rest_framework.mixins import (
|
||||
CreateModelMixin,
|
||||
RetrieveModelMixin,
|
||||
ListModelMixin,
|
||||
DestroyModelMixin,
|
||||
UpdateModelMixin,
|
||||
)
|
||||
from rest_framework.response import Response
|
||||
|
||||
from game.authentication import PlayerAuthentication
|
||||
from game.models import Hero
|
||||
from game.serializers import CreateHeroSerializer, GetHeroSerializer, CreatePlayerView
|
||||
from game.api.v1.serializers import (
|
||||
CreateHeroSerializer,
|
||||
GetHeroSerializer,
|
||||
CreatePlayerSerializer,
|
||||
ListHeroSerializer,
|
||||
DeckCreateSerializer,
|
||||
GetDeckSerializer,
|
||||
)
|
||||
from game.services.jwt import sign_jwt
|
||||
|
||||
|
||||
class CreateHeroView(GenericAPIView, CreateModelMixin, ListModelMixin):
|
||||
class ListCreateHeroView(GenericAPIView, CreateModelMixin, ListModelMixin):
|
||||
authentication_classes = (PlayerAuthentication,)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
|
@ -27,7 +40,7 @@ class CreateHeroView(GenericAPIView, CreateModelMixin, ListModelMixin):
|
|||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == "GET":
|
||||
return GetHeroSerializer
|
||||
return ListHeroSerializer
|
||||
else:
|
||||
return CreateHeroSerializer
|
||||
|
||||
|
@ -52,7 +65,7 @@ class RetrieveHeroView(RetrieveModelMixin, UpdateAPIView, GenericAPIView):
|
|||
|
||||
|
||||
class PlayerCreateView(GenericAPIView, CreateModelMixin):
|
||||
serializer_class = CreatePlayerView
|
||||
serializer_class = CreatePlayerSerializer
|
||||
|
||||
def perform_create(self, serializer):
|
||||
return serializer.save()
|
||||
|
@ -65,4 +78,35 @@ class PlayerCreateView(GenericAPIView, CreateModelMixin):
|
|||
# TODO: add JTI to refresh token
|
||||
access_jwt = sign_jwt({"id": instance.id, "type": "access"}, t_life=3600)
|
||||
refresh_jwt = sign_jwt({"id": instance.id, "type": "refresh"})
|
||||
return Response({"access_token": access_jwt, "refresh_token": refresh_jwt}, status=status.HTTP_201_CREATED)
|
||||
return Response(
|
||||
{"access_token": access_jwt, "refresh_token": refresh_jwt},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class DeckCreateView(GenericAPIView, CreateModelMixin):
|
||||
serializer_class = DeckCreateSerializer
|
||||
authentication_classes = (PlayerAuthentication,)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
return serializer.save(player=self.request.user)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = self.perform_create(serializer)
|
||||
heroes_list = ListHeroSerializer(instance.get_heroes(), many=True)
|
||||
return Response(heroes_list.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class RetireUpdateDeleteDeckView(
|
||||
RetrieveHeroView, DestroyModelMixin, UpdateModelMixin, GenericAPIView
|
||||
):
|
||||
lookup_field = "pk"
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == "GET":
|
||||
return GetDeckSerializer
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.retrieve(request, *args, **kwargs)
|
|
@ -7,7 +7,9 @@ from .services.jwt import read_jwt
|
|||
class PlayerAuthentication(authentication.BaseAuthentication):
|
||||
def authenticate(self, request):
|
||||
|
||||
if "Authorization" not in request.headers or not (token := request.headers["Authorization"]):
|
||||
if "Authorization" not in request.headers or not (
|
||||
token := request.headers["Authorization"]
|
||||
):
|
||||
raise exceptions.AuthenticationFailed("No credentials provided.")
|
||||
|
||||
t = read_jwt(token)
|
||||
|
|
57
game/migrations/0001_initial.py
Normal file
57
game/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Generated by Django 4.0.5 on 2022-06-04 14:16
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Hero',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||
('added', models.DateTimeField(auto_now_add=True)),
|
||||
('type', models.CharField(choices=[('WIZARD', 'wizard'), ('ARCHER', 'archer'), ('WARRIOR', 'warrior')], max_length=7)),
|
||||
('idle_img', models.ImageField(upload_to='uploads/idle')),
|
||||
('attack_img', models.ImageField(upload_to='uploads/attack')),
|
||||
('die_img', models.ImageField(upload_to='uploads/die')),
|
||||
('health', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])),
|
||||
('speed', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'hero',
|
||||
'verbose_name_plural': 'heroes',
|
||||
'ordering': ['-added'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Player',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ton_wallet', models.CharField(max_length=50, verbose_name='TON wallet')),
|
||||
('name', models.CharField(blank=True, max_length=100)),
|
||||
('added', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'player',
|
||||
'verbose_name_plural': 'players',
|
||||
'ordering': ['-added'],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='player',
|
||||
index=models.Index(fields=['ton_wallet'], name='game_player_ton_wal_47dd93_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='hero',
|
||||
index=models.Index(fields=['uuid'], name='game_hero_uuid_ada5d9_idx'),
|
||||
),
|
||||
]
|
|
@ -41,7 +41,10 @@ class Player(models.Model):
|
|||
hero.player = self
|
||||
hero.type = t
|
||||
|
||||
with open("/home/sanspie/Projects/chess_rpg_backend/media/dummy.jpg", "rb+") as file:
|
||||
# TODO: create image pool to generate heroes (awaiting for designer)
|
||||
with open(
|
||||
"/home/sanspie/Projects/chess_rpg_backend/media/dummy.jpg", "rb+"
|
||||
) as file:
|
||||
hero.idle_img = File(file, name="dummy.jpg")
|
||||
hero.attack_img = File(file, name="dummy.jpg")
|
||||
hero.die_img = File(file, name="dummy.jpg")
|
||||
|
@ -58,6 +61,8 @@ class Player(models.Model):
|
|||
class Meta:
|
||||
indexes = [models.Index(fields=["ton_wallet"])]
|
||||
ordering = ["-created"]
|
||||
|
||||
db_table = "player"
|
||||
verbose_name = "player"
|
||||
verbose_name_plural = "players"
|
||||
|
||||
|
@ -65,7 +70,9 @@ class Player(models.Model):
|
|||
class Hero(models.Model):
|
||||
"""Model to store heroes and their stats, connected to player"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
uuid = models.UUIDField(
|
||||
default=uuid.uuid4, editable=False, unique=True, primary_key=True
|
||||
)
|
||||
player = models.ForeignKey(
|
||||
Player,
|
||||
on_delete=models.CASCADE,
|
||||
|
@ -94,5 +101,47 @@ class Hero(models.Model):
|
|||
class Meta:
|
||||
indexes = [models.Index(fields=["uuid"])]
|
||||
ordering = ["-added"]
|
||||
|
||||
db_table = "hero"
|
||||
verbose_name = "hero"
|
||||
verbose_name_plural = "heroes"
|
||||
|
||||
|
||||
class Deck(models.Model):
|
||||
player = models.ForeignKey(
|
||||
Player,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="decks",
|
||||
related_query_name="deck",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.player.name} deck"
|
||||
|
||||
def get_heroes(self):
|
||||
return [x.hero for x in HeroInDeck.objects.filter(deck=self)]
|
||||
|
||||
class Meta:
|
||||
db_table = "deck"
|
||||
verbose_name = "deck"
|
||||
verbose_name_plural = "decks"
|
||||
|
||||
|
||||
class HeroInDeck(models.Model):
|
||||
deck = models.ForeignKey(
|
||||
Deck,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="hero_in_deck",
|
||||
related_query_name="heroes",
|
||||
)
|
||||
hero = models.OneToOneField(
|
||||
Hero,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="hero_in_deck",
|
||||
related_query_name="decks",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = "hero_in_deck"
|
||||
verbose_name = "Hero in deck"
|
||||
verbose_name_plural = "Heroes in decks"
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from game.models import Hero, Player
|
||||
|
||||
|
||||
class CreateHeroSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Hero
|
||||
fields = (
|
||||
"type",
|
||||
"idle_img",
|
||||
"attack_img",
|
||||
"die_img",
|
||||
"health",
|
||||
"attack",
|
||||
"speed",
|
||||
)
|
||||
|
||||
|
||||
class GetHeroSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Hero
|
||||
fields = (
|
||||
"added",
|
||||
"type",
|
||||
"idle_img",
|
||||
"attack_img",
|
||||
"die_img",
|
||||
"health",
|
||||
"attack",
|
||||
"speed",
|
||||
)
|
||||
|
||||
|
||||
class CreatePlayerView(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Player
|
||||
fields = ("ton_wallet", "name")
|
18
game/urls.py
18
game/urls.py
|
@ -1,9 +1,19 @@
|
|||
from django.urls import path
|
||||
|
||||
from game.views import CreateHeroView, RetrieveHeroView, PlayerCreateView
|
||||
from game.api.v1.views import (
|
||||
ListCreateHeroView,
|
||||
RetrieveHeroView,
|
||||
PlayerCreateView,
|
||||
DeckCreateView,
|
||||
RetireUpdateDeleteDeckView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("hero/", CreateHeroView.as_view(), name="hero_api_create"),
|
||||
path("hero/<uuid:uuid>", RetrieveHeroView.as_view(), name="hero_api_retrieve"),
|
||||
path("player/", PlayerCreateView.as_view(), name="player_create_api"),
|
||||
path("v1/hero/", ListCreateHeroView.as_view(), name="hero_api_create"),
|
||||
path("v1/hero/<uuid:uuid>", RetrieveHeroView.as_view(), name="hero_api_retrieve"),
|
||||
path("v1/player/", PlayerCreateView.as_view(), name="player_create_api"),
|
||||
path("v1/deck/", DeckCreateView.as_view(), name="deck_create_api"),
|
||||
path(
|
||||
"v1/deck/<int:pk>", RetireUpdateDeleteDeckView.as_view(), name="deck_retire_api"
|
||||
),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue
Block a user