I'm unable to get current user inside serializer. I have passed context but still i get error like
"user": [
"This field is required."
]
#Serializer.py
class AddressSerializer(ModelSerializer):
class Meta:
model = Address
fields = "__all__"
def create(self, validated_data):
request = self.context["request"]
validated_data["user"] = request.user
return super().create(validated_data)
#Views.py
class AddAddress(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
print(request.user)
serializer = AddressSerializer(data=request.data, context={"request":request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, 200)
return Response(serializer.errors)
#Models.py
class Address(models.Model):
user = models.ForeignKey(Account, on_delete=models.CASCADE)
full_name = models.CharField(max_length=35)
email = models.EmailField(max_length=100)
phone = models.BigIntegerField()
address_line_1 = models.TextField(max_length=500)
address_line_2 = models.TextField(max_length=500)
zip_code = models.IntegerField()
city = models.CharField(max_length=20)
state = models.CharField(max_length=15)
country = models.CharField(max_length=15)
class Meta:
verbose_name_plural = "Address"
def __str__(self):
return self.full_name
I exactly don't know the problem behind this
You can do this with the save() method
class AddAddress(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
print(request.user)
serializer = AddressSerializer(data=request.data, context={"request":request})
if serializer.is_valid():
serializer.save(user=request.user)
return Response(serializer.data, 200)
return Response(serializer.errors)
Try this:
request = self.context.get('request', None)
instead of this request = self.context["request"]
It because user is required in serializer, mean that user data must be sent when post request.
From your code, you want to add the address according to the user who is logged in. So, user no need send by frontend. Try below:
class AddressSerializer(ModelSerializer):
class Meta:
model = Address
fields = "__all__"
extra_kwargs = {
"user": {"read_only": True}
}
def validate(self, attrs):
# make sure that you have passed request in context serializer
attrs["user"] = self.context["request"].user
return attrs
It is a foreign key. You need to assign the id to it, instead of the entire user.
validated_data["user"] = request.user.id
Event hen if the error persists, then the issue seems to be there is no "user" field in your validated_data. There is issue with the data you are sending in the request to the serializer. If it doesnt match the required fields, then validated data will be empty. Print and check your validated data, and also your request data.
Related
I am trying to update the information field on my Profile model. The endpoint is receiving the data correctly, but it seems like the serializer does not get the data. I don't understand why and none of my attempts to fix it have worked.
Model:
class Profile(models.Model):
id = models.UUIDField(uuid.uuid4, primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
information = models.JSONField(null=True)
def __str__(self):
return f'{self.user} Profile'
Endpoint:
class ProfileView(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
def update(self, request, *args, **kwargs):
# Retrieve the user's profile
profile = Profile.objects.get(user=request.user)
# Update the information field with the data from the request
data = request.data
print(data) # This prints the data correctly
serializer = self.get_serializer(profile, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
Serializer:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ["user", "information"]
def validate(self, attrs):
print(attrs) # this prints an empty OrderedDict
return super().validate(attrs)
def update(self, instance, validated_data):
print(validated_data) # This prints an empty dictionary
# Update the information field with the data from the request
instance.information = validated_data["information"]
instance.save()
return instance
The data i'm passing through the request body:
JSON.stringify({"information": {"name": "xxx", "birthday": "xxx}})
How is it possible that the data just vanishes? Any help is very appreciated
Ok, I'm an idiot and forgot to set "Content-Type": "application/json" in my request header. Hopefully it will save someone else time.
I will try to make a check for the uniqueness of the field at the validation level in the serializer and can not understand why the validator is not called at all.
models.py
class Vendor(models.Model):
active = models.BooleanField(default=False)
...
class VendorContacts(models.Model):
vendor = models.ForeignKey('Vendors', related_name='contacts', on_delete=models.CASCADE)
email = models.CharField(max_length=80, blank=True, null=True)
.....
serializer.py
class VendorContactCreateSerializer(serializers.ModelSerializer):
email = serializers.CharField(validators=[RegexValidator(regex=r'[^#]+#[^\.]+\..+',
message='Enter valid email address')])
vendor = serializers.PrimaryKeyRelatedField(queryset=Vendors.objects.all(), required=False, allow_null=True)
class Meta:
model = VendorContacts
fields = (.....
)
def create(self, validated_data):
.....
#some logic
def validate_email(self, value):
print('Start validation')
exist_contact = VendorContacts.objects.filter(email=value)
if exist_contact:
vc = get_object_or_404(VendorContacts, email=value)
v = vc.vendor
if v.active:
raise serializers.ValidationError('Email {} already exists'.format(value))
return value
In the above serializer, I perform a check at the def validate_email() model field level.
print('Start validation') is not called.
I tried the same at the object level through def validate(): but it is not called either.
UPD
views.py
class VendorContactsCreateView(APIView):
permission_classes = [permissions.AllowAny, ]
serializer_class = VendorContactCreateSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = VendorContactCreateSerializer(data=data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
I can't save model with Foreignkey field.
Thanks to "azudo" problem solved. Solution below
For example I have simple models:
class User(AbstractUser):
class Meta:
pass
email_validator = EmailValidator()
username = models.CharField('Name', max_length=150, )
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField('Email', blank=True, unique=True, validators=[email_validator], )
...
class Package(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='packages')
description = models.CharField('Description', max_length=256, default='description')
weight = models.CharField('weight', max_length=256, default='weight')
...
View (the user is guaranteed to be in the request):
#api_view(["POST"])
def test(request):
data = request.data
data['user'] = User.objects.get(id=request.user.id)
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
My serializers:
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = '__all__'
class PackageSerializer(ModelSerializer):
class Meta:
model = Package
fields = (
'user', 'description', 'weight', 'dimensions', 'estimated_shipping_cost', 'deliver_to_date')
def to_representation(self, instance):
self.fields['user'] = UserSerializer(many=False, read_only=True)
self.fields['where_from'] = LocationSerializer(many=False, read_only=True)
self.fields['destination'] = LocationSerializer(many=False, read_only=True)
return super().to_representation(instance)
def create(self, validated_data):
user = User.objects.get(validated_data.pop('user'))
package = Package.objects.create(user=user, **validated_data)
return package
json in request:
{
"description": "Some package",
"weight": "12",
}
So, I'have user in database, and want create package for him. But in overridden create in PackageSerializer, validated_data doesn't have user. Please explain what I'm doing wrong.
Versions of django and drf:
django==2.2.4
djangorestframework==3.10.2
Solution:
Serializer:
class PackageSerializer(ModelSerializer):
user = UserSerializer(many=False, read_only=True)
class Meta:
model = Package
fields = (
'user', 'description', 'weight', 'dimensions', 'estimated_shipping_cost', 'deliver_to_date')
def create(self, validated_data):
user = User.objects.get(validated_data.pop('user'))
package = Package.objects.create(user=user)
return package
View:
#api_view(["POST"])
def create_package(request):
data = request.data
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save(user=request.user)
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
DRF will ignore included fields that are marked as read-only so the caller cannot include read-only data. If you want to include additional attributes simply pass them as keyword args to save:
https://www.django-rest-framework.org/api-guide/serializers/#passing-additional-attributes-to-save
e.g.
#api_view(["POST"])
def test(request):
data = request.data
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save(user=request.user)
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
I'm new to creating REST API so I might misunderstand something.
I'm creating REST API using Django Rest Framework. And I'm trying to create an object and send it from my mobile app.
However, API returns 400. I think it still cannot associate the object with the request user and I'm wondering how to do it.
models.py
class Item(models.Model):
item_name = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
and views.py
class ListItems(generics.ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
What I want to know is how to associate the object with the request user when posting the object like as we do like
if form.is_valid():
item = form.save(commit=False)
item.created_by = request.user
item.save()
I think the easiest approach is like this:
class ItemSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
Reference can be found here
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name',)
class ListItems(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
you can do this way
One of the possible way to overwrite serializer_create method. As user is not associated with request.data first we need to make sure, this is write_only field and also need to assign current user from modelSerializer's self.context.request.user. Following addition should solve the problem.
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
extra_kwargs = {'created_by': {'write_only': True}}
def create(self, validated_data):
item = Item(
item_name=validated_data['item_name'],
created_by=self.context.request.user
)
item.save()
return item
Reference link
It works for me
models.py
class Category(models.Model):
name = models.CharField('Category', max_length=200, unique=True, help_text='Name of the category')
slug = models.SlugField('Slug', max_length=100, db_index=True, unique=True, help_text='Name of the category in format URL')
def __str__(self):
return (self.name)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id', 'name', 'slug'
]
read_only_fields = [
'slug',
]
Finally, I get the user in the view, before to save the post.
views.py
class CategoryList(APIView):te a new category instance.
permission_classes = (IsAuthenticatedOrReadOnly,)
def get(self, request, format=None):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=self.request.user)
Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I am learning Django and trying to create a Game REST API using Django REST framework.I am not sure how to update the publisher field.Do I need to create another Serializer for my Publisher class and implement a save method?
I am using POSTMAN to send a PUT request and I am recieving this:
"publisher": [
"Incorrect type. Expected pk value, received unicode."
]
when I send a JSON request
{
"name": "Test",
"release_date": "1979-01-01",
"rating": 5,
"comment": "Terrible Game!",
"publisher": "Me"
}
models.py
class Publisher(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Game(models.Model):
name = models.CharField(max_length=100)
release_date = models.DateField(null=True, blank=True)
rating = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(0), MaxValueValidator(5)])
comment = models.TextField(null=True, blank=True)
publisher = models.ForeignKey(Publisher, null=True, blank=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ('name',)
serializers.py
from rest_framework import serializers
from game.models import Game, Publisher
class GameSerializer(serializers.ModelSerializer):
publisher = serializers.PrimaryKeyRelatedField(allow_null=True, queryset=Publisher.objects.all(), required=False)
class Meta:
model = Game
fields = ('pk','name', 'release_date', 'rating', 'comment', 'publisher')
def create(self, validated_data):
"""
Create and return a new 'Game' instance, given the validated data.
"""
return Game.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing 'Game' instance, given the validated data.
"""
instance.name = validated_data.get('name', instance.name)
instance.release_date = validated_data.get('release_date',instance.release_date)
instance.rating = validated_data.get('rating', instance.rating)
instance.comment = validated_data.get('comment', instance.comment)
instance.publisher = validated_data.get('publisher', instance.publisher)
instance.save()
return instance
views.py
class GameDetail(APIView):
"""
Retrieve, update or delete a game instance.
"""
def get_object(self,pk):
try:
return Game.objects.get(pk=pk)
except Game.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
game = self.get_object(pk)
serializer = GameSerializer(game)
return Response(serializer.data)
def put(self, request, pk, format=None):
game = self.get_object(pk)
serializer = GameSerializer(game, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
game = self.get_object(pk)
game.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
You're sending a string ("Me") as the publisher attribute. That's why you get your "Incorrect type. Expected pk value, received unicode." error.
Since publisher is a ForeignKey in your Game model, you'll have to get the instance of "Me" Publisher before saving it in your database.
One possible approach is to modify your update() method in your serializer and search for the right instance :
instance.publisher = Publisher.objects.get_or_create(name=validated_data.get('publisher')) and then call instance.save().