I am trying to update a foreign-field with a PUT request on a resource. My serializer.data and the http response is correct after callig .is_valid, but the object doesn't get updated.
View
def put(self, request, user_pk):
try:
userById = getUserById(user_pk)
except ChatUser.DoesNotExist:
raise Http404
serializer = ChatUserInputSerializer(userById, data=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)
Serializer
class IdentificationInputSerializer(serializers.ModelSerializer):
class Meta:
model = Identification
fields = "__all__"
read_only_fields = ["id"]
class ChatUserInputSerializer(serializers.ModelSerializer):
identification = IdentificationInputSerializer()
class Meta:
model = ChatUser
fields = ["id", "userId", "identification"]
read_only_fields = ["id", "userId"]
def update(self, instance, validated_data):
identification = validated_data.pop('identification')
instance.identification.salutation = identification.get('salutation', instance.identification.salutation)
instance.identification.firstname = identification.get('firstname', instance.identification.firstname)
instance.identification.name = identification.get('name', instance.identification.name)
instance.identification.street = identification.get('street', instance.identification.street)
instance.identification.plz = identification.get('plz', instance.identification.plz)
instance.identification.city = identification.get('city', instance.identification.city)
instance.identification.country = identification.get('country', instance.identification.country)
instance.save()
return instance
I pretty sure that django won't save related models when you call .save().
Try instance.identification.save()
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)
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
I have a model called CarDetailsAdd and VehicleDetails (field - description), VehicleDetails is a Foreignkey to CarDetailsAdd. I am using Nested Relationship in this serializer. Using this update function, I can update existing vehicle details. Add and update runs on the same screen, depending on the UI. If the user has updated the field, it should be updated or user has added a new field, it should be added. How do I do that if the user updates and adds at the same time.?
# Serializer
class CarDetailsSerializer(serializers.ModelSerializer):
vehicle_details = VehicleDetailsSerializer(many=True)
class Meta:
model = CarDetailsAdd
fields = (
'id', 'name', 'year', 'color', 'fuel_type',
'vehicle_details',
)
read_only_fields = ['id']
def update(self, instance, validated_data):
vehicle_data = validated_data.pop('vehicle_details')
vehicle = (instance.vehicle_details).all()
vehicle = list(vehicle)
instance.name = validated_data.get('name', instance.name)
instance.year = validated_data.get('year', instance.year)
instance.color = validated_data.get('color', instance.color)
instance.fuel_type = validated_data.get('fuel_type', instance.fuel_type)
instance.save()
for vehicle_data in vehicle_data:
vehicle = vehicle.pop(0)
vehicle.description = vehicle_data.get('description', vehicle.description)
vehicle.save()
return instance
# View
class CarDetailsView(APIView):
permission_classes = [IsAuthenticated]
def put(self, request, pk, format=None):
try:
car = self.get_object(pk)
serializer = CarDetailsSerializer(car, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'response': 'Update Success', 'result': serializer.data})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except:
return Response({'response': 'Oops! Something Went Wrong'}, status=status.HTTP_400_BAD_REQUEST)
Running into a little snag here with my DRF backend.
I am populating fields with choices on certain models.
I have a foreign key requirement on one model. When I create the model I want to save it under the foreign id.
When I request the models, I want the model with whatever the choice field maps to.
I was able to do this with SerializerMethodField, however when I try to create a model, I get a 400 error because the block is not valid. If I remove the SerializerMethodField, I can save, but get the number stored in the db from the request.
Any help would be appreciated.
class BlockViewSet(ModelViewSet):
model = apps.get_model('backend', 'Block')
queryset = model.objects.all()
serializer_class = serializers.BlockSerializer
permissions = ('All',)
def create(self, request, format=None):
data = request.data
data['user'] = request.user.id
data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
block = serializers.BlockSerializer(data=data, context={'request': request})
if block.is_valid():
new_block = block.save()
return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
else:
return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)
class WorkoutGoalSerializer(serializers.ModelSerializer):
class Meta:
model = apps.get_model('backend', 'WorkoutGoal')
fields = ('goal',)
goal = serializers.SerializerMethodField(read_only=True, source='get_goal')
def get_goal(self, obj):
return dict(WorkoutGoal.GOALS).get(obj.goal)
class BlockSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
goal = WorkoutGoalSerializer()
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
The above code returns the correct choice, but I can't save under it. Remove the goal = WorkoutGoalSerializer() and it saves but doesn't return the mapped choice.
I think this will work like a charm,
class WorkoutGoalSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'request' in self.context and self.context['request'].method == 'GET':
self.fields['goal'] = serializers.SerializerMethodField(read_only=True, source='get_goal')
class Meta:
model = apps.get_model('backend', 'WorkoutGoal')
fields = ('goal',)
goal = serializers.SerializerMethodField(read_only=True, source='get_goal') # remove this line
def get_goal(self, obj):
return dict(WorkoutGoal.GOALS).get(obj.goal)
How this Work?
It will re-initiate the goal field with SerializerMethodField, if the reuested method is GET.
Remember one thing, you should remove the line,
goal = serializers.SerializerMethodField(read_only=True, source='get_goal')
serializers.py
class BlockCreateSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
class BlockSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
goal = WorkoutGoalSerializer()
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
views.py
class BlockViewSet(ModelViewSet):
model = apps.get_model('backend', 'Block')
queryset = model.objects.all()
serializer_class = serializers.BlockSerializer
permissions = ('All',)
def get_serializer_class(self):
if self.action == 'create':
return serializers.BlockCreateSerializer
else:
return self.serializer_class
def create(self, request, format=None):
data = request.data
data['user'] = request.user.id
data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
block = self.get_serializer(data=data)
if block.is_valid():
new_block = block.save()
return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
else:
return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)
override get_serializer_class to return different serializer_class for create and other action(list\retrieve\update\partial_update)