I need to move validation from views.py to serializators.py.
How can I do it?
Validation must check:
user can't follow himself
user can't follow author that his already follow
I have a ViewSet with #action:
my view.py:
#action(
detail=True,
methods=['post', 'delete'],
permission_classes=(permissions.IsAuthenticated,)
)
def subscribe(self, request, id=None):
"""Following"""
author_id = id
if request.method == 'POST':
author = get_object_or_404(CustomUser, id=author_id)
if author == request.user:
raise serializers.ValidationError(
{'errors': 'You can't follow yourself.'}
)
if self.request.user.follower.filter(author=author).exists():
raise serializers.ValidationError(
{'errors': 'You already follow this author.'}
)
author = get_object_or_404(CustomUser, id=author_id)
Follow.objects.create(
user=request.user, author=author
)
serializer = FollowSerializer(author, context={'request': request})
return Response(serializer.data, status=status.HTTP_201_CREATED)
if request.method == 'DELETE':
user_id = request.user.id
subscribe = get_object_or_404(
Follow, user__id=user_id, author__id=author_id
)
subscribe.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
and I have a FollowSerializer in serializers.py
my serializers.py
class FollowSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('email', 'id', 'username', 'first_name',
'last_name', 'is_subscribed', 'recipes', 'recipes_count')
I try to add a validate method like def validate(self, data): but it doesn't work.
Related
The concept is that I try to post or update a Case. The fields "title", "customer", "assigned_to"
and "created_by" is required. Also the "assigned_to" and "created_by", are foreign keys of model Employees with email field as unique.I get the validation error for "title" if it's null but for the others I get the "Duplicate entry" error. The rest of the requests for Employees and Customers are working great.Thanks everybody in advance.
Below you'll find the necessary code:
-----------MODELS------------------------------
class Employees(models.Model):
#auto increment id
email = models.EmailField(unique=True)
class Customers(models.Model):
#auto increment id
email = models.EmailField(unique=True, null=True, blank=True)
class Cases(models.Model):
#auto increment id
title = models.CharField(max_length=100)
assigned_to = models.ForeignKey(
Employees, on_delete=models.PROTECT, related_name='assignee')
created_by = models.ForeignKey(
Employees, on_delete=models.PROTECT, related_name='creator')
customer = models.ForeignKey(
Customers, on_delete=models.PROTECT, related_name='cases',null=True, blank=True)
-----------Serializers------------------------------
class SimpleEmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employees
fields = ['id', 'full_name', ]
read_only_fields=('full_name',)
full_name = serializers.SerializerMethodField(method_name='get_full_name')
def get_full_name(self, obj):
return '{} {}'.format(obj.first_name, obj.last_name)
class SimpleCustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customers
fields = ['id', 'full_name',]
read_only_fields=('full_name',)
full_name = serializers.SerializerMethodField( method_name='get_full_name')
def get_full_name(self, obj):
return '{} {}'.format(obj.first_name, obj.last_name)
class ViewUpdateCaseSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
class Meta:
model = Cases
fields = ['id', 'category', 'title', 'description', 'customer', 'assigned_to',
'created_by', 'priority', 'status', 'creation_date', 'start_date', 'resolved_date',
'due_date', 'comments', 'cost', 'earning_fixed','earning_percent', 'clear_earning',
'price_without_vat','price_with_vat']
customer = SimpleCustomerSerializer()
assigned_to = SimpleEmployeeSerializer()
created_by = SimpleEmployeeSerializer()
-----------Views------------------------------
#api_view(['GET', 'POST'])
def cases_list(request):
if request.method == 'GET':
queryset = Cases.objects.select_related('customer', 'assigned_to', 'created_by').all()
serializer = ViewUpdateCaseSerializer(queryset, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ViewUpdateCaseSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
#api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
def case_detail(request, id):
case = get_object_or_404(Cases, pk=id)
if request.method == 'GET':
serializer = ViewUpdateCaseSerializer(case)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ViewUpdateCaseSerializer(case, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
elif request.method == 'PATCH':
serializer = ViewUpdateCaseSerializer(case, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
print("patch serializer: ", serializer.data)
return Response(serializer.data)
elif request.method == 'DELETE':
case.delete()
return Response( status=status.HTTP_204_NO_CONTENT)
Finally except for the "Duplicate entry", I got 1 empty row to the Employee table on Mysql db and 1 empty row to the Customer table. How do I solve this kind of excemption?
I am getting this error "AssertionError: Class RegisterSerializer missing "Meta" attribute" when using Django Rest API. I did make all migrations but can't figure out how to fix this error. The /register part of this application is where I am getting the error.
serializers.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from rest_framework_jwt.serializers import User
from .models import Movie
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = ('pk', 'name', 'description', 'year', 'rating')
class RegisterSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'},
validators=[validate_password])
password2 = serializers.CharField(write_only=True, style={'input_type': 'password'}, required=True)
class Meta:
model = User
fields = ('username', 'password', 'password2', 'email', 'first_name', 'last_name')
extra_kwargs = {
'first_name': {'required': True},
'last_name': {'required': True}
}
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password": "Password fields didn't match."})
return attrs
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'])
user.set_password(validated_data['password'])
user.save()
return user
urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token
from api import views
from api.views import RegisterView
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/', obtain_jwt_token),
path('', views.movie_list),
url(r'^api/movies/$', views.movie_list),
url(r'^api/movies/(?P<pk>[0-9]+)$', views.getMovie),
path('register/', RegisterView.as_view(), name='auth_register'),
]
models.py
from django.db import models
from django.utils import timezone
# Create your models here.
class Movie(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(max_length=360)
year = models.IntegerField(blank=False, null=False)
rating = models.IntegerField(blank=False, null=False)
created_date = models.DateTimeField(
default=timezone.now)
updated_date = models.DateTimeField(auto_now_add=True)
def created(self):
self.created_date = timezone.now()
self.save()
def updated(self):
self.updated_date = timezone.now()
self.save()
views.py
from rest_framework import status, generics
from rest_framework.decorators import api_view
from django.views.decorators.csrf import csrf_exempt
from .serializers import *
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny
#csrf_exempt
#api_view(['GET', 'POST'])
def movie_list(request):
permission_classes = IsAuthenticatedOrReadOnly
if request.method == 'GET':
movies = Movie.objects.all()
serializer = MovieSerializer(movies, context={'request': request}, many=True)
return Response({'data': serializer.data})
elif request.method == 'POST':
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def getMovie(request, pk):
"""
Retrieve, update or delete a movie instance.
"""
try:
movie = Movie.objects.get(pk=pk)
except Movie.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = MovieSerializer(movie, context={'request': request})
return Response(serializer.data)
elif request.method == 'PUT':
serializer = MovieSerializer(movie, data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
movie.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
I have tried some suggestions from other posts on here but all of those did not apply to my current issue. Thanks for any help.
After user-registration, user wants to post data into Client model ( OnetoOne relationship with User model ). So, I want to access the requested user object inside the serializer-class to create a new row in Client model associated with the requested user.
models.py
class Client(models.Model):
user= models.OneToOneField(User, on_delete=models.CASCADE, null=False, blank=False)
sex = models.CharField(max_length = 6, blank=False, null=False)
location = models.CharField(max_length = 30, null = False, blank = False)
views.py
class ClientRegister(GenericAPIView):
def post(self, request):
user = request.user
serializer = ClientSerializer(data= request.data)
if serializer.is_valid():
serializer.save()
return Response(status= status.HTTP_201_CREATED)
else:
return Response(data= 'Invalid Form', status= status.HTTP_400_BAD_REQUEST)
serializers.py
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['sex', 'location']
def create(self, validated_data):
sex = validated_data.get('sex')
location = validated_data.get('location')
user = #------ Want requested user object here ------#
if user.is_client:
client = Client(user=user, sex=sex, location=location)
client.save()
return client
I have manually added user oject into the data in serializer = CientSerializer(data=request.data).
But, It didn't work. Please, tell me how to pass it from views.py or how to access it in serializers.py.
Pass user when you do serializer.save in post method of views like
def post(self, request):
u = request.user
serializer = ClientSerializer(data= request.data)
if serializer.is_valid():
serializer.save(user=u)
return Response(status= status.HTTP_201_CREATED)
else:
return Response(data= 'Invalid Form', status= status.HTTP_400_BAD_REQUEST)
Or you can read about passing context from views to serializers and in this context you can pass your required data like -
serializer = ClientSerializer(context = {'request':request}, data=request.data)
then you can have your request object in your serializers and you can get request.user or directly pass it in the context.
Try to pass the user with sex and location in views
class ClientRegister(GenericAPIView):
def post(self, request):
data = request.data
user = request.user
#add user in key
data['user']=user
serializer = ClientSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(status= status.HTTP_201_CREATED)
else:
return Response(data= 'Invalid Form', status= status.HTTP_400_BAD_REQUEST)
serializer:
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = '__all__'
views.py
class ClientRegister(GenericAPIView):
def post(self, request):
user = request.user
serializer = ClientSerializer(context={'user':user}, data= request.data)
if serializer.is_valid():
serializer.save()
return Response(status= status.HTTP_201_CREATED)
else:
return Response(data= 'Invalid Form', status= status.HTTP_400_BAD_REQUEST)
serializers.py
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['sex', 'location']
def create(self, validated_data):
sex = validated_data.get('sex')
location = validated_data.get('location')
user = self.context.get('user')
if user.is_client:
client = Client(user=user, sex=sex, location=location)
client.save()
return client
My Model is
class ChatRoom(models.Model):
name = models.CharField(max_length=55, verbose_name='Имя чата', unique=True)
users = models.ManyToManyField(
CustomUser,
verbose_name='Пользователи',
related_name='user_chatrooms',
null=True
)
My Serializer for this model
class ChatRoomSerializer(serializers.ModelSerializer):
users = UserInfoSerializer(many=True, read_only=False)
class Meta:
model = ChatRoom
fields = [
'name',
'users'
]
View looks
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def api_create_chat(request):
if request.method == 'POST':
serializer = ChatRoomSerializer(data=request.data)
data = {}
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I do request (here is body)
<QueryDict: {'name': ['benny'], 'users': ['11,1']}>
In this case I recieve Error
{'users': [ErrorDetail(string='This field is required.', code='required')]}
And I can't understand what is wrong here.
When I change parameter to read_only=True in UserInfoSerializer it works, but it doesn't add user to new Chat object.
{
"name": "benny",
"users": []
}
UPD
I tried to add create() method, but it didn't help
def create(self, validated_data):
users = validated_data.pop('users')
users = [int(id) for id in users.split(',')]
chat_instance = ChatRoom.objects.create(name=validated_data.pop('name'))
for id in users:
chat_instance.users.add(CustomUser.objects.get(pk=id))
return chat_instance
UPD 2
UserInfoSerializer
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['id',]
Problem solved by modifying serializer in view.py. It isn't good decision, but it works.
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def api_create_chat(request):
if request.method == 'POST':
serializer = ChatRoomSerializer(data=request.data)
data = {}
if serializer.is_valid():
serializer.save()
users_id = [int(id) for id in request.data['users'].split(',')]
chat = ChatRoom.objects.get(pk=serializer.data['id'])
for id in users_id:
user = CustomUser.objects.get(pk=id)
chat.users.add(user)
serializer.data['users'].append({'user': user.id, 'username': user.username})
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I got an error message: HyperlinkedIdentityField requires the request in the serializer context. Add context={'request': request} when instantiating the serializer. when I'm using API to get data. What am I doing wrong here?
models.py
class User(AbstractUser):
username = models.CharField(blank=True, null=True, max_length=255)
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
def __str__(self):
return "{}".format(self.email)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
title = models.CharField(max_length=5)
dob = models.DateField()
address = models.CharField(max_length=255)
country = models.CharField(max_length=50)
city = models.CharField(max_length=50)
zip = models.CharField(max_length=5)
photo = models.ImageField(upload_to='uploads', blank=True, )
serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('title', 'dob', 'address', 'country', 'city', 'zip', 'photo')
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = UserProfileSerializer(required=True)
class Meta:
model = User
fields = ('url', 'email', 'first_name', 'last_name', 'password', 'profile')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
profile_data = validated_data.pop('profile')
password = validated_data.pop('password')
user = User(**validated_data)
user.set_password(password)
user.save()
UserProfile.objects.create(user=user, **profile_data)
return user
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
profile = instance.profile
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.title = profile_data.get('title', profile.title)
profile.dob = profile_data.get('dob', profile.dob)
profile.address = profile_data.get('address', profile.address)
profile.country = profile_data.get('country', profile.country)
profile.city = profile_data.get('city', profile.city)
profile.zip = profile_data.get('zip', profile.zip)
profile.photo = profile_data.get('photo', profile.photo)
profile.save()
return instance
And views.py
class UserList(ListCreateAPIView):
'''
Return list all users or create a new user
'''
serializer_class = UserSerializer
pagination_class = CustomPagination
def get_queryset(self):
try:
user = User.objects.all()
return user
except:
content = {
'status': 'Not Found'
}
return Response(content, status=status.HTTP_404_NOT_FOUND)
def get(self, request):
users = self.get_queryset()
paginate_queryset = self.paginate_queryset(users)
serializer = self.serializer_class(paginate_queryset, many=True)
return self.get_paginated_response(serializer.data, )
def post(self, request):
try:
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except:
content = {
'status': 'Failed to create new user'
}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
class UserDetail(RetrieveUpdateDestroyAPIView):
def get_queryset(self, pk):
try:
user = User.objects.get(pk=pk)
return user
except User.DoesNotExist:
content = {
'status': 'Not Found'
}
return Response(content, status=status.HTTP_404_NOT_FOUND)
def get(self, request, pk):
try:
user = self.get_queryset(pk)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
except NameError:
content = {
'status': 'Failed to get user'
}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
try:
user = self.get_queryset(pk)
serializer = UserSerializer(user, request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except:
content = {
'status': 'Failed to modify user'
}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
try:
user = self.get_queryset(pk)
user.delete()
return Response("Delete user: " +user.username +"successfully",
status=status.HTTP_200_OK)
except:
content = {
'status': 'Failed to delete user'
}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
I'm try using this
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
and It's worked. I don't know why there is a difference. If I add context={'request': request} to my code. I will get a new error. It's very uncomfortable