Add serialized object to current user - django

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)

Related

Integrity Error: NOT NULL constraint failed

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

Querying and Filtering related models in DRF

I have Contact model to list the followers of an User object, I try to filter the contacts of a User but I still could not manage get a correct queryset. My Contact model is simple with two ForeignKey:
class Contact(models.Model):
user_from = models.ForeignKey(User,related_name='rel_from_set', on_delete=models.CASCADE,)
user_to = models.ForeignKey(User,related_name='rel_to_set', on_delete=models.CASCADE,)
def __str__(self):
return '{} follow {}'.format(self.user_from, self.user_to)
I have created serializers for User and Contact:
##Contact Serializer
class ContactsSerializer(serializers.ModelSerializer):
user_from = serializers.StringRelatedField(read_only=True)
user_to = serializers.StringRelatedField(read_only=True)
class Meta:
model = Contact
fields = ["user_from", "user_to"]
##UserSerializer
class UserInformationSerializer(serializers.ModelSerializer):
followers = ContactsSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['first_name', 'last_name', 'followers']
​
And try to make a query through views:
class FollowerListView(APIView):
queryset = Contact.objects.all()
serializer_class = ContactsSerializer
lookup_field = "username"
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).filter(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
Now I get error message:
AttributeError at /api/member/ytsejam/followers/
'QuerySet' object has no attribute 'username'
print(user.username)
If i try print(user) I can see the user an Object.
Can you guide me how to correct?
Thanks
filter will always return a queryset. If you expect to retrieve one single item, use get.
So that it looks like that:
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).get(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
You could, of course, do this on one take:
User.objects.get(is_active=1, username=kwarg_username)
But beware, if there are two rows in your model that would satisfy this call, Django will throw an error. Best make sure that the username has a unique constraint.

django.db.utils.IntegrityError: in POST request

I am creating a post function to save data in a model, Here's the code:
Views.py
class take_quizapi(CreateAPIView):
def post(self, request,pk, *args, **kwargs):
supplier = request.user.supplier
data = request.data.copy()
data["supplier_id"] = supplier.user_id
data["score"] = 0
data["quiz"] = pk
print("data is", data)
serializer = takenquizSerializer(data=data)
if serializer.is_valid():
serializer.save()
print("Serializer data", serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
when I call the API from postman it says:
django.db.utils.IntegrityError: null value in column "supplier_id" violates not-null constraint
But I am clearly providing the data["supplier_id"] = supplier.user_id.
What is it that I am doing wrong here ?
Serializers.py
class takenquizSerializer(serializers.ModelSerializer):
supplier = ReadOnlyField(source='supplier.supplier_fname')
class Meta:
model = TakenQuiz
fields = "__all__"
Models.py
class TakenQuiz(models.Model):
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, related_name='taken_quizzes')
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='taken_quizzes')
score = models.FloatField()
date = models.DateTimeField(auto_now_add=True)
least_bid = models.IntegerField(default=0)
confirmed = models.CharField(max_length=100, default='Not Confirmed')
supplier is a read only field. So there's no point in adding it to the data dict, since it will never be used.
You shouldn't be modifying the post data anyway - there's a reason that is immutable. Instead pass it in when saving:
serializer = takenquizSerializer(data=request.data)
if serializer.is_valid():
serializer.save(supplier_id=supplier.user_id, score=0, quiz=pk)

Adding a request.user in a ManyToMany field

I am trying to add a request.user to a manytomany field to create an item. But I receive this error:
TypeError at /api/v1/movies/
'User' object is not iterable
Models.py
class Movie(models.Model):
class Movie(models.Model):
title = models.CharField(max_length=100)
genre = models.CharField(max_length=100)
year = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
owners = models.ManyToManyField('auth.User', related_name='movies')
views.py
# Create a new movie
def post(self, request):
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owners=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Per comment and updated question, the desired behavior is multiple users. Assuming that your MovieSerializer is a subclass of ModelSerializer and that you have used the default (PrimaryKeyRelatedField) binding for the owners serializer field, you should be able to add the requestor in the initial data:
def post(self, request):
serializer = MovieSerializer(data=dict(request.data, owners=[request.user.id]))
if serializer.is_valid():
serializer.save()
...
NOTE: since owners is a ManyToMany relation, you need to stick the user in an array, and for serialization this needs to be the pk of the user, not the object.

Django REST Framework: create related model from POSTed data

Using the following models:
class Ticket(models.Model):
[some irrelevant fields]
class TicketComment(models.Model):
text = models.TextField()
creator = models.CharField(max_length=255)
ticket = models.ForeignKey(Ticket, models.CASCADE, related_name='comments')
I created the following serializers:
class TicketSerializer(serializers.ModelSerializer):
[irrelevant]
class TicketCommentSerializer(serializers.ModelSerializer):
class Meta:
model = TicketComment
fields = '__all__'
def create(self, validated_data):
return TicketComment.objects.create(**validated_data)
A view:
class TicketCommentView(APIView):
lookup_url_kwarg = 'ticket_id'
def post(self, request, ticket_id):
data = request.data
data['creator'] = 'joe'
try:
data['ticket'] = Ticket.objects.get(pk=ticket_id)
except Ticket.DoesNotExist:
raise NotFound('Ticket {} does not exist.'.format(ticket_id))
serializer = TicketCommentSerializer(data=data)
serializer.is_valid(raise_exception=True)
comment = serializer.save()
return Response(comment, status=HTTP_201_CREATED)
And URL pattern:
urlpatterns = [
path('ticket/<int:ticket_id>/comment', TicketCommentView.as_view()),
]
However, when trying to POST the data {"text": "test"}, it fails with:
"ticket": ["Incorrect type. Expected pk value, received Ticket."]
If I change the view to pass the ticket_id integer instead of the ticket instance, it complains about duplicate keys:
django.db.utils.IntegrityError: duplicate key value violates unique constraint "ticketcomment_pkey"
DETAIL: Key (id)=(41993) already exists.
How can I create a resource and attach it to an existing related object?
Instead of passing ticket as serializer data, pass it to serializer's save method directly(related part of docs):
def post(self, request, ticket_id):
data = request.data
try:
ticket = Ticket.objects.get(pk=ticket_id)
except Ticket.DoesNotExist:
raise NotFound('Ticket {} does not exist.'.format(ticket_id))
serializer = TicketCommentSerializer(data=data)
serializer.is_valid(raise_exception=True)
comment = serializer.save(ticket=ticket, creator='joe')
return Response(comment, status=HTTP_201_CREATED)
Note in TicketCommentSerializer you should leave only text field:
class TicketCommentSerializer(serializers.ModelSerializer):
class Meta:
model = TicketComment
fields = ['text']
First you should not include id in serializer, cause mostly it will be a auto increment value. That is why you are getting an Integrity Error.