Integrity Error: NOT NULL constraint failed - django

I created a custom create field in the tournament serializer to create and update nested field.
I have been trying to make that work for a long time, but I can't get rid of errors.
When I try to post data on the tournament update it returns this error:
NOT NULL constraint failed: api_tournament.organizer_id
Here, api is the name of the app.
models.py
class tournament(models.Model):
tournament_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=60)
organizer = models.ForeignKey(
client, null=False, blank=False, on_delete=models.CASCADE)
logo = models.ImageField(null=True, blank=False)
game = models.CharField(max_length=20, choices=GAMES)
fees = models.IntegerField()
def __str__(self):
return self.title
views.py
# tournament
#api_view(['GET'])
def tournamentList(request):
tournaments = tournament.objects.all()
serializer = tournamentSerializer(tournaments, many=True)
return Response(serializer.data)
#api_view(['GET'])
def tournamentDetail(request, tournamentId):
singleTournament = tournament.objects.get(tournament_id=tournamentId)
serializer = tournamentSerializer(singleTournament, many=False)
return Response(serializer.data)
#api_view(['POST'])
def tournamentCreate(request):
serializer = tournamentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
#api_view(['POST'])
def tournamentUpdate(request, tournamentId):
singleTournament = tournament.objects.get(tournament_id=tournamentId)
serializer = tournamentSerializer(
instance=singleTournament, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
#api_view(['DELETE'])
def tournamentDelete(request, tournamentId):
singleTournament = tournament.objects.get(tournament_id=tournamentId)
singleTournament.delete()
return Response("Deleted Item")
serializers.py
class tournamentSerializer(serializers.ModelSerializer):
organizer = clientSerializer(many=False, read_only=False)
class Meta:
model = tournament
fields = "__all__"
def create(self, validated_data):
organizer_data = validated_data.pop('organizer')
new_tournament = tournament.objects.create(**validated_data)
client.objects.create(organizer_data)
return new_tournament

"I created cusotm create field in the tournament serializer to create and update nested field, trying to make that work for a long time now but can't get rid of errors."
This error has nothing to do with create/update nested fields. But if you really go down that road:
When you initialize a serializer with an instance and call save(), it's not going to call the create method. It'll call the serializer update method. Here is the source code for proof.
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
return self.instance
But the serializer update method does not seem to be in the code you provided. It'll default to the ModelSerializer's update, which would raise an error about nested writes. Since that's not what happened, the bug must be earlier. This probably happened inside serializer.is_valid().
"When I try to post data on the tournament update it gives the error as
NOT NULL constraint failed: api_tournament.organizer_id
"
The error says that a constraint preventing null values has been violated. From the looks of it, the request.data might not have an "organizer_id" or "organizer" key. If you want to update a tournament without posting all the data during an update, either give the serializer field argument required=False or initialize the serializer with the argument partial=True

Related

Django Rest Framework: validate HiddenField CurrentUserDefault property

I am using DRF to create an API in a single page application.
I have a customer user class to which I have only added a is_manager flag and a managerEntity model where users that have the is_manager flag as True can create managerEntities becoming owners of them.
The problem is that I can't seem to figure out how to validate the data from the serializer before create method to check whether the is_manager is set or not. If set, the managerEntity should be created, if not, raise an exception.
class DeepmetricsUser(AbstractUser):
is_manager = models.BooleanField(default=False)
class managerEntity(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
team = models.ManyToManyField(get_user_model(), blank=True)
views.py
class managersEntityViewSet(viewsets.ModelViewSet):
queryset = managerEntity.objects.all()
serializer_class = managerEntityModelSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return self.queryset.filter(Q(owner = self.request.user) | Q(team=self.request.user.id))
def create(self, request, *args, **kwargs):
serializer = managerEntitySerializer(data=request.data, context={"request": self.request})
serializer.is_valid(raise_exception=True)
res = serializer.save()
data = managerEntityModelSerializer(res).data
return Response(data, status=status.HTTP_201_CREATED)
serializer.py
class managerEntitySerializer(serializers.Serializer):
name = serializers.CharField(max_length=255)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
def create(self, data):
res = managerEntity.objects.create(**data)
return res
You need to override the validate method in Serializer
def validate(self, attrs):
if not self.context["request"].user.is_manager:
raise serializers.ValidationError("Validation error")
return attrs
I found a solution that fits better my needs by using permissions. The answer provided by Shakeel is correct as I asked for validation and that should be done as he suggested but, what I really wanted to do was checking for enough clearance for the user to manipulate a resource, then, permissions is what's best fits:
class createManagerEntity(BasePermission):
message = "Not enough privilegies"
def has_permission(self, request, view):
return request.user.is_manager

Django REST Framework field serializer validation is not being called

After some hours of research, I still can't find the answer to the issue I am facing.
I am trying to add some simple validation to a field in one of my models.
In serializers.py:
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = '__all__'
def validate_name(self, value):
"""
Check that the brand name has at least 20 characters
"""
if len(value) < 20:
raise serializers.ValidationError(
"The name of the brand should be longer than 20 characters")
return value
I am using function based views:
#api_view(['POST'])
def brandCreate(request):
data = request.data
try:
brand = Brand.objects.create(
name=data['name'],
)
serializer = BrandSerializer(brand, many=False)
return Response(serializer.data)
except:
message = {'detail': 'Brand is not created'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
And this is my model:
class Brand(models.Model):
name = models.CharField(max_length=80)
logo = models.ImageField(null=True, blank=True)
def __str__(self):
return str(self.name)
After sending a POST request with postman, the record is successfully created, even though the name is shorter than 20 characters!
Any hints on what I am missing here? Thank you!
You're not using it correctly. You need to call is_valid method.
#api_view(['POST'])
def brandCreate(request):
data = request.data
serializer = BrandSerializer(data=data)
if serializer.is_valid():
serialiazer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

django rest framework - POST request causes 400 status code

I am trying to perform a POST request to create an article and I am getting this error Request failed with status code 400 Bad Request: /api/articles/create/.
An article needs 3 attributes to be created:
(1) title
(2) body
(3) author (the current user)
The router works fine since the POST request goes into the post method of the ArticleCreateView class. But I'm guessing that serializer.is_valid() is returning False for some reason.
Also print(request.data) returns {'title': 'hello', 'body': 'stuff'}
Another mystery about Django is, how does the serializer know if I want to get, create or update something? In all the examples I've seen, the serializer magically seems to know this.
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
body = models.TextField(max_length=100)
date_updated = models.DateTimeField(auto_now=True)
date_created = models.DateTimeField(auto_now_add=True)
According to your serializer, the validation process needs author details, which is not passing through POST payload.
So, make the author field in the serializer a not required field by adding required=False or use a ReadOnlyField() or specify read_only_fields in Meta class. Also pass context data as #nishant mentioned
# views.py
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data, context={"request": request}) # change here
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
# serializer.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('author',)
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
add this
serializer = ArticleSerializer(data=request.data, context={'request':request})

Update ONLY an auth.user field using request.user and django rest framework serializers

I'm currently writing an API using the django rest framework for the first time. However, I've run in to a problem when trying to update an object using only the request.user field
from what I understand, when updating an object with request.data fields, all you have to do is:
def put(self, request, pk, format=None):
chore = self.get_object(pk)
serializer = ChoreSerializer(chore, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
however, if I try to leave the data=request.data out, I'll get errors when trying to call .is_valid(). and in turn .save(). I managed to get around this error by serializing the object I want to change based on a primary key, and then de-serializing it again, using the serialized data like this:
seri = ChoreSerializer(chore)
serializer = ChoreSerializer(chore, data=seri.data)
if serializer.is_valid():
serializer.save(assigned_to=self.request.user)
return Response(serializer.data)
My question is whether this is actually the correct way to do this, or whether there's a better method
here's my Model:
class Chore(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100, blank=True, default='')
owner = models.ForeignKey('auth.User', related_name='chores')
assigned_to = models.ForeignKey('auth.User',related_name='assigned_to', blank=True, null=True)
and here's my Serializer:
class ChoreSerializer(serializers.ModelSerializer):
class Meta:
model = Chore
fields = ('id', 'name', 'owner', 'assigned_to')
owner = serializers.ReadOnlyField(source='owner.username')
assigned_to = serializers.ReadOnlyField(source='assigned_to.username')
def create(self, validated_data):
return Chore.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.assigned_to = validated_data.get('assigned_to', instance.assigned_to)
instance.save()
return instance

Add serialized object to current user

So I have a view that accepts serialized Album object and assign the owner to current user
class AlbumListViewer(APIView):
def post(self, request, format = None):
request.data['user_id'] = request.user.id
serializer = AlbumSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(status.HTTP_201_CREATED)
return Response(status = status.HTTP_400_BAD_REQUEST)
The error I get is
null value in column "user_id" violates not-null constraint
My Serializer looks like this
class AlbumSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Album
fields = ('id','album_name', 'date_created','user')
And finally my Model looks like this
class Album(models.Model):
user = models.ForeignKey(User)
album_name = models.CharField(max_length=30)
date_created = models.DateTimeField(editable=False, auto_now_add=True)
I have tried assigning a User ID in the JSON data but it is not being recognized by the serializer, anyway to assign this serialized object and give it a owner before saving?
Your have some problems in your AlbumListViewer. Do not try to add to the validatated_data anything. Instead pass on partial=True to the serializer, and after validation, use the save() method to save the missing values.
class AlbumListViewer(APIView):
def post(self, request, format = None):
serializer = AlbumSerializer(data=request.data, partial=True)
if serializer.is_valid():
user = request.user
serializer.save(user=user)
return Response(status=status.HTTP_201_CREATED)
return Response(status = status.HTTP_400_BAD_REQUEST)