-
Notifications
You must be signed in to change notification settings - Fork 729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement py-api-views #674
base: master
Are you sure you want to change the base?
Changes from 6 commits
23e210d
2b20c3d
639545e
6e3f944
bf7b26b
0afbe7a
744f464
8531bca
f35a783
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,4 @@ venv/ | |
.pytest_cache/ | ||
**__pycache__/ | ||
*.pyc | ||
db.sqlite3 | ||
#db.sqlite3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,27 @@ | ||
from django.db import models | ||
|
||
|
||
class Actor(models.Model): | ||
first_name = models.CharField(max_length=255) | ||
last_name = models.CharField(max_length=255) | ||
|
||
|
||
class Genre(models.Model): | ||
name = models.CharField(max_length=255, unique=True) | ||
|
||
|
||
class CinemaHall(models.Model): | ||
name = models.CharField(max_length=255) | ||
rows = models.IntegerField() | ||
seats_in_row = models.IntegerField() | ||
|
||
|
||
class Movie(models.Model): | ||
title = models.CharField(max_length=255) | ||
description = models.TextField() | ||
duration = models.IntegerField() | ||
actors = models.ManyToManyField(Actor, related_name="movies") | ||
genres = models.ManyToManyField(Genre, related_name="movies") | ||
|
||
def __str__(self): | ||
return self.title |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,39 @@ | ||
from rest_framework import serializers | ||
|
||
from cinema.models import Movie | ||
from cinema.models import Movie, Genre, CinemaHall, Actor | ||
|
||
|
||
class MovieSerializer(serializers.Serializer): | ||
class MovieSerializer(serializers.ModelSerializer): | ||
actors = serializers.PrimaryKeyRelatedField( | ||
queryset=Actor.objects.all(), | ||
many=True, | ||
) | ||
genres = serializers.PrimaryKeyRelatedField( | ||
queryset=Genre.objects.all(), | ||
many=True | ||
) | ||
|
||
class Meta: | ||
model = Movie | ||
fields = "__all__" | ||
|
||
|
||
class GenreSerializer(serializers.ModelSerializer): | ||
id = serializers.IntegerField(read_only=True) | ||
title = serializers.CharField(max_length=255) | ||
description = serializers.CharField() | ||
duration = serializers.IntegerField() | ||
name = serializers.CharField(max_length=255) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you need to define these fields if you use ModelSerializer? |
||
|
||
class Meta: | ||
model = Genre | ||
fields = "__all__" | ||
|
||
def create(self, validated_data): | ||
return Movie.objects.create(**validated_data) | ||
|
||
def update(self, instance, validated_data): | ||
instance.title = validated_data.get("title", instance.title) | ||
instance.description = validated_data.get( | ||
"description", instance.description | ||
) | ||
instance.duration = validated_data.get("duration", instance.duration) | ||
class ActorSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Actor | ||
fields = "__all__" | ||
|
||
instance.save() | ||
|
||
return instance | ||
class CinemaHallSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = CinemaHall | ||
fields = "__all__" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,43 @@ | ||
from django.urls import path | ||
from django.urls import path, include | ||
from rest_framework import routers | ||
|
||
from cinema.views import movie_list, movie_detail | ||
from cinema.views import ( | ||
ActorList, | ||
ActorDetail, | ||
GenreDetail, | ||
GenreList, | ||
CinemaHallViewSet | ||
) | ||
from .views import MovieViewSet | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this to the import above |
||
router = routers.DefaultRouter() | ||
router.register("movies", MovieViewSet) | ||
urlpatterns = [ | ||
path("movies/", movie_list, name="movie-list"), | ||
path("movies/<int:pk>/", movie_detail, name="movie-detail"), | ||
path("", include(router.urls)), | ||
path("actors/", ActorList.as_view(), name="actor-list"), | ||
path("actors/<int:pk>/", ActorDetail.as_view(), name="actor-detail"), | ||
path("genres/", GenreList.as_view(), name="actor-list"), | ||
path("genres/<int:pk>/", GenreDetail.as_view(), name="actor-detail"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the name here should be "genre-list". See also below. |
||
path( | ||
"cinema_halls/", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can register CinemaHallViewSet in router with the correct prefix. It will look better |
||
CinemaHallViewSet.as_view( | ||
{ | ||
"get": "list", "post": "create" | ||
} | ||
), | ||
name="cinema_hall-list" | ||
), | ||
path( | ||
"cinema_halls/<int:pk>/", | ||
CinemaHallViewSet.as_view( | ||
{ | ||
"get": "retrieve", | ||
"put": "update", | ||
"patch": "partial_update", | ||
"delete": "destroy" | ||
} | ||
), name="cinema_hall-detail" | ||
), | ||
] | ||
|
||
app_name = "cinema" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,109 @@ | ||
from rest_framework.decorators import api_view | ||
from rest_framework import status, generics, mixins, viewsets | ||
from rest_framework.generics import get_object_or_404 | ||
from rest_framework.views import APIView | ||
from rest_framework.response import Response | ||
from rest_framework import status | ||
from django.http import HttpRequest | ||
|
||
from django.shortcuts import get_object_or_404 | ||
from cinema.models import Movie, Actor, Genre, CinemaHall | ||
from cinema.serializers import ( | ||
MovieSerializer, | ||
ActorSerializer, | ||
GenreSerializer, | ||
CinemaHallSerializer | ||
) | ||
|
||
from cinema.models import Movie | ||
from cinema.serializers import MovieSerializer | ||
|
||
class MovieViewSet(viewsets.ModelViewSet): | ||
queryset = Movie.objects.all() | ||
serializer_class = MovieSerializer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using PrimaryKeyRelatedField in your serializer you will have N+1 query problem for genres and actors. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
|
||
@api_view(["GET", "POST"]) | ||
def movie_list(request): | ||
if request.method == "GET": | ||
movies = Movie.objects.all() | ||
serializer = MovieSerializer(movies, many=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
if request.method == "POST": | ||
serializer = MovieSerializer(data=request.data) | ||
if serializer.is_valid(): | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
class ActorList( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
generics.GenericAPIView | ||
): | ||
queryset = Actor.objects.all() | ||
serializer_class = ActorSerializer | ||
|
||
def get(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.list(request, *args, **kwargs) | ||
|
||
def post(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.create(request, *args, **kwargs) | ||
|
||
|
||
class ActorDetail( | ||
mixins.RetrieveModelMixin, | ||
mixins.UpdateModelMixin, | ||
mixins.DestroyModelMixin, | ||
generics.GenericAPIView | ||
): | ||
queryset = Actor.objects.all() | ||
serializer_class = ActorSerializer | ||
|
||
def get(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.retrieve(request, *args, **kwargs) | ||
|
||
def put(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.update(request, *args, **kwargs) | ||
|
||
def patch(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.partial_update(request, *args, **kwargs) | ||
|
||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | ||
def delete(self, request: HttpRequest, *args, **kwargs) -> Response: | ||
return self.destroy(request, *args, **kwargs) | ||
|
||
|
||
@api_view(["GET", "PUT", "DELETE"]) | ||
def movie_detail(request, pk): | ||
movie = get_object_or_404(Movie, pk=pk) | ||
class CinemaHallViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
mixins.RetrieveModelMixin, | ||
mixins.UpdateModelMixin, | ||
mixins.DestroyModelMixin, | ||
viewsets.GenericViewSet | ||
): | ||
queryset = CinemaHall.objects.all() | ||
serializer_class = CinemaHallSerializer | ||
|
||
if request.method == "GET": | ||
serializer = MovieSerializer(movie) | ||
|
||
class GenreList(APIView): | ||
def get(self, request: HttpRequest) -> Response: | ||
genre = Genre.objects.all() | ||
serializer = GenreSerializer(genre, many=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. name it in plural - genres |
||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
def post(self, request: HttpRequest) -> Response: | ||
serializer = GenreSerializer(data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
|
||
|
||
class GenreDetail(APIView): | ||
def get_object(self, pk: int) -> Genre: | ||
return get_object_or_404(Genre, pk=pk) | ||
|
||
def get(self, request: HttpRequest, pk: int) -> Response: | ||
genre = self.get_object(pk) | ||
serializer = GenreSerializer(genre) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
if request.method == "PUT": | ||
serializer = MovieSerializer(movie, data=request.data) | ||
if serializer.is_valid(): | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
def put(self, request: HttpRequest, pk: int) -> Response: | ||
genre = self.get_object(pk) | ||
serializer = GenreSerializer(genre, data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | ||
def patch(self, request : HttpRequest, pk: int) -> Response: | ||
genre = self.get_object(pk) | ||
serializer = GenreSerializer(genre, data=request.data, partial=True) | ||
serializer.is_valid(raise_exception=True) | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
if request.method == "DELETE": | ||
movie.delete() | ||
def delete(self, request: HttpRequest, pk: int) -> Response: | ||
genre = self.get_object(pk) | ||
genre.delete() | ||
return Response(status=status.HTTP_204_NO_CONTENT) |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to push this file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You stille have this file here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using PrimaryKeyRelatedField you will have N+1 query problem.