Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api_yamdb/api/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import django_filters as filters


class TitleFilter(filters.FilterSet):
...
10 changes: 10 additions & 0 deletions api_yamdb/api/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from rest_framework import mixins, viewsets


class ListCreateDestroyViewSet(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet
):
pass
3 changes: 0 additions & 3 deletions api_yamdb/api/models.py

This file was deleted.

26 changes: 26 additions & 0 deletions api_yamdb/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from rest_framework import permissions


class IsAdmin(permissions.BasePermission):

def has_permission(self, request, view):
return request.user.is_authenticated and request.user.is_admin


class IsOwnerAdminModeratorOrReadOnly(permissions.IsAuthenticatedOrReadOnly):
message = 'Изменить контент может только автор, админ или модератор.'

def has_object_permission(self, request, view, obj):
return (request.method in permissions.SAFE_METHODS
or request.user.is_admin
or request.user.is_moderator
or request.user == obj.author)


class IsAdminOrReadOnly(permissions.BasePermission):
message = 'Изменить контент может только админ.'

def has_permission(self, request, view):
return (request.method in permissions.SAFE_METHODS
or (request.user.is_authenticated
and request.user.is_admin))
23 changes: 23 additions & 0 deletions api_yamdb/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers

User = get_user_model()


class UsersSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name',
'bio', 'role')


class CreateUserSerializer(serializers.ModelSerializer):

class Meta:
fields = ('username', 'email')
model = User


class UserJWTTokenCreateSerializer(serializers.Serializer):
confirmation_code = serializers.CharField(required=True)
username = serializers.CharField(required=True)
12 changes: 8 additions & 4 deletions api_yamdb/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from rest_framework import routers

from .views import ...

v1_router = DefaultRouter()
from api.views import (UsersViewSet, user_create_view,
user_jwt_token_create_view)

v1_router = routers.DefaultRouter()
v1_router.register('users', UsersViewSet, basename='users')

v1_router.register(...)

urlpatterns = [
path('v1/', include(v1_router.urls)),
path('v1/auth/signup/', user_create_view),
path('v1/auth/token/', user_jwt_token_create_view)
]
82 changes: 80 additions & 2 deletions api_yamdb/api/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
from django.shortcuts import render
from http import HTTPStatus

# Create your views here.
from django.contrib.auth import get_user_model
from django.contrib.auth.tokens import default_token_generator
from django.core.mail import send_mail
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, permissions, filters
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import AccessToken

from .permissions import (IsAdmin, IsAdminOrReadOnly,
IsOwnerAdminModeratorOrReadOnly)
from .serializers import (UsersSerializer, CreateUserSerializer,
UserJWTTokenCreateSerializer)
from users.models import User



class UsersViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
permission_classes = [IsAdmin]
serializer_class = UsersSerializer
lookup_field = 'username'
lookup_value_regex = '[^/]+'
pagination_class = LimitOffsetPagination

@action(methods=['patch', 'get'], detail=False,
permission_classes=[permissions.IsAuthenticated],
url_path='me', url_name='me')
def me(self, request, *args, **kwargs):
instance = self.request.user
serializer = self.get_serializer(instance)
if self.request.method == 'PATCH':
serializer = self.get_serializer(
instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save(role=self.request.user.role)
return Response(serializer.data)


@api_view(['POST'])
@permission_classes([permissions.AllowAny])
def user_create_view(request):
serializer = CreateUserSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.validated_data.get('email')
username = serializer.validated_data.get('username')
serializer.save()
confirmation_code = default_token_generator.make_token(
User.objects.get(email=email, username=username)
)
MESSAGE = (f'Здравствуйте, {username}! '
f'Ваш код подтверждения: {confirmation_code}')
send_mail(message=MESSAGE,
subject='Confirmation code',
recipient_list=[email],
from_email=None)
return Response(serializer.data, status=HTTPStatus.OK)


@api_view(['POST'])
@permission_classes([permissions.AllowAny])
def user_jwt_token_create_view(request):
serializer = UserJWTTokenCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
confirmation_code = serializer.validated_data.get('confirmation_code')
username = serializer.validated_data.get('username')
user = get_object_or_404(User, username=username)
if default_token_generator.check_token(user, confirmation_code):
token = AccessToken.for_user(user)
return Response(
data={'token': str(token)},
status=HTTPStatus.OK
)
return Response(
'Неверный код подтверждения или имя пользователя!',
status=HTTPStatus.BAD_REQUEST
)
4 changes: 2 additions & 2 deletions api_yamdb/api_yamdb/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'Секретный ключ'

# SECURITY WARNING: don't run with debug turned on in production!
# SECURITY WARNING: don't run with debug tFurned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']
Expand All @@ -23,11 +23,11 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'django_filters',
'rest_framework',
'reviews.apps.ReviewsConfig',
'api.apps.ApiConfig',
'users',
]

MIDDLEWARE = [
Expand Down
13 changes: 12 additions & 1 deletion api_yamdb/reviews/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
from django.db import models

# Create your models here.
from .validators import validate_actual_year


class Title(models.Model):
name = models.CharField(
max_length=256,
verbose_name='Название'
)
year = models.PositiveSmallIntegerField(
verbose_name='Год выпуска',
validators=[validate_actual_year]
)
10 changes: 10 additions & 0 deletions api_yamdb/reviews/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.core.exceptions import ValidationError
from django.utils import timezone


def validate_actual_year(value):
if value > timezone.now().year:
raise ValidationError(
('Год %(value)s ещё не наступил'),
params={'value': value},
)