I am trying to allow user to update the name of Lists they have created. However, when I attempt to POST the data I return the following error:
user: ["This field is required."]
I have racked my brain trying to solve this, hardcoded the username etc. but I keep turning up empty. I'd be grateful for some more expertise to assist me.
Here is my view:
class UpdateUserListViewSet(viewsets.ModelViewSet):
serializer_class = UserListSerializer
queryset = UserList.objects.all()
def update(self, instance, validated_data):
serializer_class = UserListSerializer
if self.request.method == "POST":
list_id = request.data.get('id')
user = UserList(user=self.request.user.id)
list_name = request.data.get('list_name')
data = {'id':int(list_id), 'list_name': list_name}
serializer = serializer_class(user, data=data)
if serializer.is_valid():
serializer.update()
return Response({'status' : 'ok'}, status=200)
else:
return Response({'error' : serializer.errors}, status=400)
And here is my serializer:
class UserListSerializer(serializers.ModelSerializer):
class Meta:
model = UserList
fields = ['id', 'user', 'list_name']
So actually you are trying to update with POST request, you have to check wheather your code had been reached inside this update function and if it is reaching there then you have to pass partial=True in update otherwise serailizer will try to validate all the required fields so for that you can change as :-
serializer = serializer_class(user, data=data, partial=True)
Related
I have a model which has an attribute "transaction_id" which is a customized ID field and it's value has to be calculated in order to be saved in the database.
I have a model:
class Transaction(models.Model):
field = models.IntegerField(default=0)
transaction_id = models.UUIDField(unique=True)
This is the seriaizer:
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = '__all__'
read_only_fields = ['id', 'transaction_id']
def set_tn_number(self):
tn_number = "some_string/"
#I have to perform some calculation in order to get the relevant value
tn_number = tn_number + str(10)
return tn_number
Now in my post method of the view, i am performing the following:
def post(self, request):
serializer = TransactionSerializer(data=request.data)
if serializer.is_valid():
serializer.tn_number = serializer.set_tn_number()
serializer.save()
message = {'message': "Transaction Created Successfully"}
return Response(message, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
But i am still getting integrity error:
NOT NULL constraint failed: transaction_transaction.transaction_id
Can someone please help me with this?
Thank you for your time
Update validated data or pass the necessary model attributes to save method as kwargs.
def post(self, request):
serializer = TransactionSerializer(data=request.data)
if serializer.is_valid():
tn_number = serializer.set_tn_number()
serializer.save(transaction_id=tn_number)
message = {'message': "Transaction Created Successfully"}
return Response(message, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Recently started working with Django REST framework. I have a user table and another table that stores some relevant data for each user. I've set up POST/GET/DELETE methods fine, but I can't get the method for perform_update working - I keep getting a KeyError at /api/sdgdata/1
'user_id' error in Postman when I attempt a put request for a user. Plz see code below:
Models:
class TestDataTwo(models.Model):
user = models.ForeignKey("auth.User", related_name="testdatatwo", on_delete=models.CASCADE)
test_name = models.CharField(max_length=1024, null=True, default="N/A")
Serializers:
class TestDataTwoSerializer(serializers.ModelSerializer):
class Meta:
model = TestDataTwo
fields = (
"id",
"test_name",
)
def update(self, instance, validated_data):
# get user id from validated data:
user_id = validated_data.pop('user_id')
# get user:
user = User.objects.get(id=user_id)
# set user on instance:
instance.user = user
instance.save()
# continue with update method:
super().update(instance, validated_data)
Views:
class TestDataTwoViewSet(ModelViewSet):
queryset = TestDataTwo.objects.all().order_by('id')
serializer_class = TestDataTwoSerializer
paginator = None
# CREATE NEW TESTDATATWO ROW FOR USER
def perform_create(self, serializer):
serializer.save(user=self.request.user)
# GET ALL ENTRIES OF TESTDATATWO FOR SPECIFIC USER, EXCEPT IF SUPERUSER, THEN RETURN ALL
def get_queryset(self):
# if self.request.user.is_superuser:
# return self.queryset
# else:
return self.queryset.filter(user=self.request.user)
def perform_update(self, serializer):
instance = self.get_object()
serializer.save(user=self.request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
# DELETE TESTDATATWO ID
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
Postman GET:
Postman PUT:
I've tried a number of variations on the perform_update method, but I am guessing I am missing a reference to the user's user_id somehow...Appreciate any help.
This error is caused by validated_data.pop('user_id'), since user_id was not defined as a field on the serializer nor was it passed in the request data. It seems you are aiming to save the current user as the user field of the TestDataTwo instance being updated.
In that case you don't need to override update, since saving the user based on self.request.user on a TestDataTwo instance is already done by the serializer's save here:
def perform_update(self, serializer):
serializer.save(user=self.request.user)
This means that you don't have to do any processing in the serializer anymore, so it can be just:
class TestDataTwoSerializer(serializers.ModelSerializer):
class Meta:
model = TestDataTwo
fields = ("id", "test_name",)
# No need to override update for saving self.request.user to `TestDataTwo`'s user
For more information on this functionality, you can have a read here..[DRF-docs-Passing-additional-attributes-to-save].
I want to save a simple model with Django REST Framework. The only requirement is that UserVote.created_by is set automatically within the perform_create() method. This fails with this exception:
{
"created_by": [
"This field is required."
]
}
I guess it is because of the unique_together index.
models.py:
class UserVote(models.Model):
created_by = models.ForeignKey(User, related_name='uservotes')
rating = models.ForeignKey(Rating)
class Meta:
unique_together = ('created_by', 'rating')
serializers.py
class UserVoteSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
created_by = UserSerializer(read_only=True)
class Meta:
model = UserVote
fields = ('id', 'rating', 'created_by')
views.py
class UserVoteViewSet(viewsets.ModelViewSet):
queryset = UserVote.objects.all()
serializer_class = UserVoteSerializer
permission_classes = (IsCreatedByOrReadOnly, )
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
How can I save my model in DRF without having the user to supply created_by and instead set this field automatically in code?
Thanks in advance!
I had a similar problem and I solved it by explicitly creating and passing a new instance to the serializer. In the UserVoteViewSet you have to substitute perform_create with create:
def create(self, request, *args, **kwargs):
uv = UserVote(created_by=self.request.user)
serializer = self.serializer_class(uv, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I was able to solve this with one-liner in views.py
def create(self, request, *args, **kwargs):
request.data.update({'created_by': request.user.id})
return super(UserVoteViewSet, self).create(request, *args, **kwargs)
Since this view expects user to be authenticated, don't forget to extend permission_classes for rest_framework.permissions.IsAuthenticated
The other weird way you can do is use signals like this
#receiver(pre_save, sender=UserVote)
def intercept_UserVote(sender, instance, *args, **kwargs):
import inspect
for frame_record in inspect.stack():
if frame_record[3]=='get_response':
request = frame_record[0].f_locals['request']
break
else:
request = None
instance.pre_save(request)
Then basically you can define pre_save in your model
def pre_save(self, request):
# do some other stuff
# Although it shouldn't happen but handle the case if request is None
self.created_by = request.user
The advantage of this system is you can use same bit of code for every model. If you need to change anything just change in pre_save(). You can add more stuff as well
Add the following to the ViewSet:
def perform_create(self, serializer):
serializer.save(user=self.request.user)
And the following on the Serializer:
class Meta:
extra_kwargs = {
'user': {
'required': False,
},
}
Below code worked for me.
Even I was facing same error after many experiments found something, so added all fields in serializer.py in class meta, as shown below -
class Emp_UniSerializer( serializers.ModelSerializer ):
class Meta:
model = table
fields = '__all__' # To fetch For All Fields
extra_kwargs = {'std_code': {'required': False},'uni_code': {'required': False},'last_name': {'required': False},'first_name': {'required': False}}
Here, we can update any field which are in "extra_kwargs", it wont show error ["This field is required."]
I have a POST method which is going to be used to retrieve a JSON object, which is then going to be used to retrieve the first_name, last_name, and username -- although I can't figure out how to get the fields (i.e. username) after I serialize it. What's the best way to go about that?
views.py
#api_view(['POST'])
def createUser(request):
# Making a Connection w/ MongoClient
client = MongoClient('mongodb+srv://test_user:0FP33TLJVWrjl8Vy#cluster0.5sacp.mongodb.net/sample_clubs?retryWrites=true&w=majority')
# Getting the Database
db = client['sample_clubs']
# Getting the Collection/Table
collection = db['users']
serializer = MyUserSerializer(data=request.data)
# Gives bug if next 2 lines aren't here
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
serializers.py
class MyUserSerializer(serializers.ModelSerializer):
def get_first_name(self, obj):
# obj is model instance
return obj.first_name
def get_last_name(self, obj):
# obj is model instance
return obj.last_name
def get_user_name(self, obj):
# obj is model instance
return obj.user_name
class Meta:
model = MyUser
fields = ['first_name', 'last_name', 'username']
# fields = '__all__'
models.py
class MyUser(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
username = models.CharField(max_length=200)
def __str__(self):
return self.username
A serializer's save method in DRF will return the instance that has been saved. So you can simply call any of its field like this:
if serializer.is_valid():
obj = serializer.save()
print(obj.user_name)
The data will also be available through the serializer's validated data:
if serializer.is_valid():
print(serializer.validated_data.get('user_name')
You can also use the raw JSON that's been generated by serializer:
# note that serializer.data won't be available if 'is_valid()` returns False
print(serializer.data["user_name"])
Also, you shouldn't return serializer.data outside of the is_valid scope. If is_valid() is False, then there won't be any data so you will run to an error. The proper way would be this:
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
If you only want to return the user_name as response:
if serializer.is_valid():
obj = serializer.save()
return Response({"username": obj.user_name})
return Response(serializer.errors)
I´m trying to create an instance of a serializer on a POST request, but it is ignoring the model instance im passing as the first argument
if request.method == 'POST':
if string_pk in reviewed_user_pk:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = CreateReviewSerializer(review, data=request.data)
I get user instance from the request:
try:
user = request.user
except user.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
The problem here is that the instance of Review which has both user instances (author and reviewed_user) is being ignored by the ReviewSerializer, here is the serializer:
class CreateReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['author', 'reviewed_user','title', 'rating', 'comment', 'date_published']
The oter fields in request.data are being serialized but not the Review instance, what can be causing this problem? the error i get from serializer.errors is the following:
{
"author": [
"This field is required."
],
"reviewed_user": [
"This field is required."
]
}
Here is the complete function view:
#api_view(['POST'])
#permission_classes((IsAuthenticated,))
def api_create_review_view(request, user_pk): #user_pk is the pk of the reviewed_user
try:
user = request.user
except user.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
reviewed_user_pk = user.worked_with.split(',')
string_pk = str(user_pk)
data = {
}
if request.method == 'POST':
if string_pk in reviewed_user_pk:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = ReviewSerializer(review, data=request.data)
if serializer.is_valid():
serializer.save()
reviews_count = reviewed_user.reviews_count
rating = ((reviewed_user.rating * reviews_count) / (reviews_count + 1)) + ((serializer.rating) / (reviews_count + 1))
reviews_count += 1
reviewed_user.rating = rating
reviewed_user.reviews_count = reviews_count
reviewed_user.save()
return Response(serializer.data)
data = serializer.errors
return Response(data)
else:
data = {
'forbidden':'users have not worked together'
}
return Response(data=data)
And here is the Review model:
class Review(models.Model):
reviewed_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='reviewed_user')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='author')
rating = models.IntegerField(default=5)
title = models.CharField(max_length=50)
comment = models.CharField(max_length=500)
date_published = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Right now im not worried that the code inside the if statement that checks if the serializer is valid works, i just want to know how can i pass the instance of Review with both user instances inside to the serializer so the serializer is valid
if you are try to create a new instance of Review. Try this two method, i am not sure which one can work.
first way, change the request.data:
from copy import deepcopy
reviewed_user = User.objects.get(pk=user_pk)
serializer_data = deepcopy(request.data)
serializer_data['user'] = user
serializer_data['reviewed_user'] = reviewed_user
serializer = ReviewSerializer(serializer_data)
if serializer.is_valid():
serializer.save()
second way,just change serializer to partial with set partial=partial in Serializer:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = ReviewSerializer(review, data=request.data, partial=partial)
if serializer.is_valid():
third way, not validate user and reviewed_user in serializer, just save it,
remove 'author', 'reviewed_user' in serializer:
class CreateReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['title', 'rating', 'comment', 'date_published']
reviewed_user = User.objects.get(pk=user_pk)
serializer = ReviewSerializer(data=request.data)
if serializer.is_valid():
serializer.save(author=user, reviewed_user=reviewed_user) # save user and reviewed_user here