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
File renamed without changes.
Binary file added learn_docs/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/admin.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/apps.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/mixins.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/models.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/serializers.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/urls.cpython-38.pyc
Binary file not shown.
Binary file added learn_docs/__pycache__/views.cpython-38.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions learn_docs/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions learn_docs/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class LearnDocsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'learn_docs'
Empty file.
Binary file not shown.
15 changes: 15 additions & 0 deletions learn_docs/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from rest_framework.generics import get_object_or_404


class MultipleFieldLookupMixin:

def get_object(self):
queryset = self.get_queryset() # соглашаемся с queryset
queryset = self.filter_queryset() # также соглашаемся с фильтрами
filter = {}
for field in self.lookup_fields:
if self.kwargs.get(field): ## так достаем из lookup
filter[field] = self.kwargs.get(field)
obj = get_object_or_404(queryset, **filter) # передаем фильтр для поиска
self.check_object_permissions(self.request, obj)
return obj
3 changes: 3 additions & 0 deletions learn_docs/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
62 changes: 62 additions & 0 deletions learn_docs/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from datetime import datetime

from rest_framework import serializers


class Comment:

def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()


class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=20)
created = serializers.DateTimeField()

# если мы хотим сразу возвращать готовые обькты по валидированным данным данным
def create(self, validated_data):
print('created')
# Comment.objects.create(**validated_data) ## если хотим сразу в бд
return Comment(**validated_data)

def update(self, instance, validated_data):
print('update')
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.email)
instance.created = validated_data.get('created', instance.email)
# instance.save()
return instance

# # если нам не нужно создавать новый обьект, и например сделать какую нибудь логику(например, отправка сообщения на почту, можно переопределить save напрямую)
# def save(self, **kwargs):
# email = self.validated_data.get('email')
# message = self.validated_data.get('message')
# # send_email(from=email, message=message)

# для валидации поля мы создаем метод с названием validate_<field>
# при неудачной валидации кидаем validation error
# только для полей required true
def validate_content(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError('This comment is not about Django')
return value

# валидации на уровне обьекто
def validate(self, attrs):
# if attrs['start'] > attrs['finish']:
# raise serializers.ValidationError('finish ust occur after start')
return attrs

# можно написать валидатор, который будет использоваться для нескольких полей

def multiple_of_ten(self, value):
if value % 10 != 0:
raise serializers.ValidationError('Not a muliply of ten')

class GameRecord(serializers.Serializer):
score = serializers.IntegerField( )


3 changes: 3 additions & 0 deletions learn_docs/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
22 changes: 22 additions & 0 deletions learn_docs/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.urls import path
from rest_framework.routers import DefaultRouter

from .views import TestRequestAPIView, TestList, UserViewSet, TestSerializers

## 1 вариант
urlpatterns = [
path('request/', TestRequestAPIView.as_view()),
path('list/', TestList.as_view()),
path('user/', UserViewSet.as_view({'get': 'list'})),
path('user/<uuid:pk>/', UserViewSet.as_view({'get': 'retrieve'})),
path('serializer/', TestSerializers.as_view())




]
## тк юрл самому писать тяжело и запарно, необходим роутер
## 2 вариант для вьюсетов
router = DefaultRouter()
router.register(r'user-set', UserViewSet, basename='user')
urlpatterns += router.urls
189 changes: 189 additions & 0 deletions learn_docs/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import datetime

from django.contrib.auth import get_user_model
from django.shortcuts import render
from rest_framework import generics, permissions, status, viewsets

# Create your views here.
from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication

from learn_docs.mixins import MultipleFieldLookupMixin
from learn_docs.serializers import Comment, CommentSerializer
from users.serializers import UserProfileSerializer, UserSerializer

User = get_user_model()

# добавляем миксин и все ок в целом

class TestSerializers(generics.RetrieveUpdateDestroyAPIView):
serializer_class = CommentSerializer
authentication_classes = (JWTAuthentication, )

def get(self, request, *args, **kwargs):
comment = Comment(email='leila@example.com', content='foo bar')
# serializing
serializer = self.get_serializer(comment)
print(serializer.data) # здесь мы транслируем лишь в нативный питоновский словарь. Нужны рендеры
# json = JSONRenderer().render(serializer.data)
# print(json)
return Response(serializer.data) ## рендер вызывается автоматически

def patch(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) ## нужно указывать дата
comment = None
if serializer.is_valid(raise_exception=True):
comment = serializer.save() # create
# comment = serializer.save(owner=request.user) # можно добавлять информацию при save, которая не находится в request.data
## вызов create или update в методе save зависит от того, как мы обьявили сериализатор
# print(comment)
# serializer = self.get_serializer(data=request.data, instance=comment)
# if serializer.is_valid():
# comment = serializer.save() # update
# print(comment)
return Response(serializer.validated_data)

# def get_permissions(self):
# if self.request.method == 'GET':
# self.permission_classes = (AllowAny,)
# else:
# self.permission_classes = (IsAuthenticated, )
# return super().get_permissions()

# def get_serializer(self, *args, **kwargs):
# if self.request.method == 'GET':
# self.serializer_class = CommentSerializer
# else:
# self.serializer_class = UserSerializer
# return super().get_serializer(*args, **kwargs)


class TestList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer

def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True) ## флаг того, что много инстансов
return Response(serializer.data)

class TestRequestAPIView(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
permission_classes = (permissions.AllowAny, )
authentication_classes = (JWTAuthentication, )
lookup_fields = ('id', 'username', )

def get(self, request, *args, **kwargs):
print(request.query_params)
return Response('1', status=status.HTTP_200_OK)

def post(self, request, *args, **kwargs):
queryset = self.get_queryset() ## очень важно вызывать данный метод, чем обращаться к проперте напрямую, тк при обращении напрямую, он кэшируется и получается грустно невкусно
print(request.data)
print(request.accepted_renderer)
print(request.user)
print(request.auth) # тут крч содержится токен(или может что то другое), что является параметром для аутентификации
response = Response()
request.data['1'] = '2'
print(response.set_cookie('COKA', '12345', max_age=5))
return response

def handle_exception(self, exc): # вот так можно хэндлить ошибки любые(даже ерроры)(мб и не все)
print(type(exc))
return Response(data={'detail': 'pizda', })

#def filter_queryset(self, queryset): фильтрует queryset (там нужны фильтры бэкэнды)

# если нужно определить гибко сериализоатор, то как вариант. Если переопределяем, обращаемся только через метод(да и вообще всегда)

def get_serializer_class(self):
if self.request.user.is_staff:
return UserProfileSerializer
return UserSerializer

# хуки
# нужны для того, чтобы можно было переопределить сохранение или удаление внутри миксина(CreateModelMixin и тд вызывают их)

# в данном случае очень удобно добавлять при сохранении информацию, которая передается вместе с request, но не с request.data
def perform_create(self, serializer):
serializer.save(user=self.request.user)

# в данном случае можно вставлять определенную логику до или после сохранение
def perform_update(self, serializer):
#logger.log('update instance')
instance = serializer.save() ## сохранение изменненных данных
##send_email_confirmation(user=self.request.user, modified=instance)

## также можно внедрять валидацию
# def perform_create(self, serializer):
# queryset = SignupRequest.objects.filter(user=self.request.user)
# if queryset.exists():
# raise ValidationError('You have already signed up')
# serializer.save(user=self.request.user)


# если придется часто использовать данный миксин, можно создать базовый класс
class BaseRetrieveView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
pass

class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPIView):
pass

### тесты классов
### В целом если мы переопределяем поведения методов, то настраивать некоторые параметры необязательно(если они нам в коде не нужны)
## но в целом тенденция такая, что для retrieve нам нужен полюбому queryset и model
### для create,update достаточно модели (и для delete тоже), однако в целом, иногда очень полезно вызывать queryset в коде через класс представления, а не через модель напрямую(например ели queryset отфильтрован)



## view set
## обычный класс - не содежит действий
## GenericViewSet добавляет стандартную логику для вьюшек(действий также не содержит)
## ReadOnlyViewSet - list() и retrieve()
## ModelViewSet - содержит все методы(использовать можно, но для себя в крайне редких случаях)
## для всех них (кроме ModelViewSet) удобно использовать роутер
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = (JWTAuthentication, )
## так мы можем устанавливать permissions для конкретного юрл

def get_permissions(self):
if self.action == 'list':
permission_classes = (IsAuthenticated,)
else:
permission_classes = (AllowAny, )
return (permission() for permission in permission_classes)

# благодаря данному декоратору можем создавать свои action. Можно настроить хоть пермиссион хоть что
@action(detail=True, methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.validated_data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)

# def list(self, request, *args, **kwargs):
# queryset = User.objects.all()
# serializer = UserSerializer(queryset, many=True)
# return Response(serializer.data)
#
# def retrieve(self, request, pk=None):
# queryset = User.objects.all()
# user = get_object_or_404(queryset, pk=pk)
# serializer = UserSerializer(user)
# return Response(serializer.data)

# у вью сета нету методов хендлеров вроде get post и тд



40 changes: 0 additions & 40 deletions questions/migrations/0001_initial.py

This file was deleted.

Binary file not shown.
Binary file not shown.
11 changes: 11 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
asgiref==3.4.1
Django==3.2.6
django-ckeditor==6.1.0
django-js-asset==1.2.2
djangorestframework==3.12.4
djangorestframework-simplejwt==4.7.2
Pillow==8.3.1
PyJWT==2.1.0
pytz==2021.1
sqlparse==0.4.1
typing==3.7.4.3
Binary file not shown.
Binary file modified stackoverflow_api/__pycache__/settings.cpython-38.pyc
Binary file not shown.
Binary file modified stackoverflow_api/__pycache__/urls.cpython-38.pyc
Binary file not shown.
Loading