diff --git a/api_yamdb/api/admin.py b/api_yamdb/api/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/api_yamdb/api/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/api_yamdb/api/permissions.py b/api_yamdb/api/permissions.py new file mode 100644 index 0000000..99c6db9 --- /dev/null +++ b/api_yamdb/api/permissions.py @@ -0,0 +1,8 @@ +from rest_framework import permissions + + +class IsOwnerOrReadOnly(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return request.method in permissions.SAFE_METHODS or ( + obj.author == request.user + ) diff --git a/api_yamdb/api/serializers.py b/api_yamdb/api/serializers.py new file mode 100644 index 0000000..88b91b9 --- /dev/null +++ b/api_yamdb/api/serializers.py @@ -0,0 +1,43 @@ +from rest_framework import serializers +from rest_framework.validators import UniqueValidator +from reviews.models import Comment, Review + + +class CommentSerializer(serializers.ModelSerializer): + author = serializers.SlugRelatedField( + read_only=True, + slug_field='username' + ) + + class Meta: + model = Comment + fields = '__all__' + read_only_fields = ('reviews', ) + + +class ReviewSerializer(serializers.ModelSerializer): + author = serializers.PrimaryKeyRelatedField( + source='author.username', read_only=True + ) + comments = CommentSerializer(many=True, read_only=True) + text = serializers.CharField(allow_null=True) + id = serializers.ReadOnlyField() + author_username = serializers.ReadOnlyField(source='author.username') + + class Meta: + model = Review + fields = '__all__' + validators = [ + UniqueValidator( + queryset=Review.objects.all(), + message='Вы уже написали отзыв к данному произведению' + ) + ] + + def get_rating(self, obj): + reviews = Review.objects.filter(title=obj.title) + if reviews: + total_score = sum([review.score for review in reviews]) + rating = total_score / len(reviews) + return round(rating, 2) + return None diff --git a/api_yamdb/api/tests.py b/api_yamdb/api/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/api_yamdb/api/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/api_yamdb/api/urls.py b/api_yamdb/api/urls.py index 1ffa438..f234dfa 100644 --- a/api_yamdb/api/urls.py +++ b/api_yamdb/api/urls.py @@ -4,8 +4,7 @@ # from .views import CategoryViewSet # from .views import GenreViewSet # from .views import TitleViewSet -# from .views import ReviewViewSet -# from .views import CommentViewSet +from .views import ReviewViewSet, CommentViewSet # from .views import register # from .views import get_jwt_token @@ -16,14 +15,14 @@ # router.register(r'categories', CategoryViewSet, basename='categories') # router.register(r'genres', GenreViewSet, basename='genres') # router.register(r'titles', TitleViewSet, basename='titles') -# router.register(r'titles/(?P\d+)/reviews', ReviewViewSet, basename='reviews') -# router.register(r'titles/(?P\d+)/reviews/(?P\d+)/comments', CommentViewSet, basename='comments') +router.register(r'titles/(?P\d+)/reviews', ReviewViewSet, basename='reviews') +router.register(r'titles/(?P\d+)/reviews/(?P\d+)/comments', CommentViewSet, basename='comments') urlpatterns = [ -# path('v1/', include('djoser.urls')), -# path('v1/', include('djoser.urls.jwt')), + path('v1/', include('djoser.urls')), + path('v1/', include('djoser.urls.jwt')), path('v1/', include(router.urls)), -# path('v1/auth/signup/', register, name='register'), -# path('v1/auth/token/', get_jwt_token, name='token') + # path('v1/auth/signup/', register, name='register'), + # path('v1/auth/token/', get_jwt_token, name='token') ] \ No newline at end of file diff --git a/api_yamdb/api/views.py b/api_yamdb/api/views.py index 91ea44a..0d8b0a1 100644 --- a/api_yamdb/api/views.py +++ b/api_yamdb/api/views.py @@ -1,3 +1,38 @@ -from django.shortcuts import render +from django.shortcuts import get_object_or_404 +from django.db.models import Avg +from reviews.models import Review, Title +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework_simplejwt.authentication import JWTAuthentication -# Create your views here. +from .permissions import IsOwnerOrReadOnly +from .serializers import ReviewSerializer, CommentSerializer + + +class ReviewViewSet(viewsets.ModelViewSet): + queryset = Review.objects.all() + serializer_class = ReviewSerializer + permission_classes = (IsOwnerOrReadOnly, IsAuthenticatedOrReadOnly,) + authentication_classes = [JWTAuthentication] + + def perform_create(self, serializer): + serializer.save(author=self.request.user) + title_id = self.request.data.get('title') + reviews = Review.objects.filter(title_id=title_id) + rating = reviews.aggregate(Avg('score'))['score__avg'] + Title.objects.filter(id=title_id).update(rating=rating) + + +class CommentViewSet(viewsets.ModelViewSet): + serializer_class = CommentSerializer + permission_classes = (IsOwnerOrReadOnly, IsAuthenticatedOrReadOnly,) + authentication_classes = [JWTAuthentication] + + def get_queryset(self): + review = get_object_or_404(Review, pk=self.kwargs.get('review_id')) + return review.comments + + def perform_create(self, serializer): + review_id = self.kwargs.get('rewiew_id') + review = get_object_or_404(Review, pk=review_id) + serializer.save(author=self.request.user, review=review) diff --git a/api_yamdb/api_yamdb/settings.py b/api_yamdb/api_yamdb/settings.py index 7605e52..2f688f9 100644 --- a/api_yamdb/api_yamdb/settings.py +++ b/api_yamdb/api_yamdb/settings.py @@ -117,6 +117,8 @@ 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 3, } SIMPLE_JWT = { diff --git a/api_yamdb/reviews/models.py b/api_yamdb/reviews/models.py index 8a1c6a6..dc1d8af 100644 --- a/api_yamdb/reviews/models.py +++ b/api_yamdb/reviews/models.py @@ -13,7 +13,7 @@ class Category(models.Model): def __str__(self): return self.name - + class Meta: ordering = ['name'] @@ -42,7 +42,7 @@ class Title(models.Model): def __str__(self): return self.name - + class Meta: ordering = ['name'] @@ -52,7 +52,7 @@ class GenreTitle(models.Model): Title, on_delete=models.CASCADE) genre = models.ForeignKey( Genre, on_delete=models.CASCADE) - + def __str__(self): return f'{self.title}, genre: {self.genre}' @@ -63,14 +63,14 @@ class Review(models.Model): related_name='reviews') text = models.TextField() author = models.ForeignKey( - User, on_delete=models.CASCADE, + User, on_delete=models.CASCADE, related_name='reviews') score = models.IntegerField( validators=[ MinValueValidator(1), MaxValueValidator(10) ] - ) + ) pub_date = models.DateTimeField( 'Publication date', auto_now=True) @@ -79,19 +79,19 @@ class Meta: ordering = ['pub_date'] constraints = [ models.UniqueConstraint( - fields=('title', 'author',), - name='unique_review' + fields=('title', 'author',), + name='unique_review' ) ] class Comment(models.Model): review = models.ForeignKey( - Review, on_delete=models.CASCADE, + Review, on_delete=models.CASCADE, related_name='comments') text = models.TextField() author = models.ForeignKey( - User, on_delete=models.CASCADE, + User, on_delete=models.CASCADE, related_name='comments') pub_date = models.DateTimeField('Publication date', auto_now_add=True) diff --git a/api_yamdb/reviews/tests.py b/api_yamdb/reviews/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/api_yamdb/reviews/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/api_yamdb/users/models.py b/api_yamdb/users/models.py index e9aa07d..6a92ac4 100644 --- a/api_yamdb/users/models.py +++ b/api_yamdb/users/models.py @@ -2,7 +2,6 @@ from django.contrib.auth.models import AbstractUser from reviews.validators import validate_username -# Create your models here. class User(AbstractUser): ADMIN = 'admin' @@ -32,12 +31,12 @@ class User(AbstractUser): @property def is_moderator(self): - return self.role == self.MODERATOR + return self.role == self.MODERATOR @property def is_admin(self): return self.role == self.ADMIN - + USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] @@ -49,4 +48,4 @@ class Meta: check=~models.Q(username__iexact="me"), name="username_is_not_me" ) - ] \ No newline at end of file + ]