When posting this:
curl -X POST -H "Authorization: Token sometoken" -d "url=someurl" 127.0.0.1:8000/create/
I get the error:
{"user":["This field is required."] with the ItemSerializer,
I have seen other posts on SO talking about using perform_create, which I am trying to use to save the user object, but it doesn´t work for some reason. Perform_create works when user is defined like this:
user = serializers.CharField(
default=serializers.CurrentUserDefault()
)
But I want to use the user object, not only CharField storing the username
Serializers:
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = UserModel
fields = ('pk', 'username', 'email', 'first_name', 'last_name')
read_only_fields = ('email', )
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['cat']
class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = [
'comment',
]
class ItemSerializer(serializers.HyperlinkedModelSerializer):
user = UserDetailsSerializer()
category = CategorySerializer(many=True)
thecomments = CommentSerializer(many=True)
timestamp = serializers.SerializerMethodField('get_mytimestamp')
def get_mytimestamp(self, obj):
return time.mktime(datetime.datetime.now().timetuple())
class Meta:
model = Item
fields = [
"url",
"user",
"timestamp",
"categories",
"thecomments",
]
Model:
class Item(models.Model):
url = models.CharField(max_length=1000)
user = models.ForeignKey('auth.User', unique=False)
timestamp = models.DateTimeField(auto_now_add=True)
url = models.CharField(max_length=1000)
categories = models.ManyToManyField(Category)
View:
class ItemCreateAPIView(generics.CreateAPIView):
serializer_class = ItemSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Solution:
Serializer to create Item:
class CreateSerializer(serializers.HyperlinkedModelSerializer):
timestamp = serializers.SerializerMethodField('get_mytimestamp')
def get_mytimestamp(self, obj):
return time.mktime(datetime.datetime.now().timetuple())
class Meta:
model = Item
fields = [
"url",
"timestamp",
]
views.py
class ItemCreateAPIView(generics.CreateAPIView):
serializer_class = CreateSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
post:
curl -X POST -H "Authorization: Token sometoken" -d "url='someurl'" 127.0.0.1:8000/createitem/
class ItemSerializer(serializers.HyperlinkedModelSerializer):
user = UserDetailsSerializer()
category = CategorySerializer(many=True)
thecomments = CommentSerializer(many=True)
timestamp = serializers.SerializerMethodField('get_mytimestamp')
def get_mytimestamp(self, obj):
return time.mktime(datetime.datetime.now().timetuple())
class Meta:
model = Item
fields = [
"url",
"user",
"timestamp",
"categories",
"thecomments",
]
extra_kwargs = {'user': {'required': False}}
add extra_kwargs = {'user': {'required': False}} to your serializer Meta
i had the same issue.
added extra_kwargs = {'user': {'required': False}} to serializer Meta class and it worked
Related
I have an author and books model. An author has many books with him
class Author(Model):
id = UUIDField(primary_key=True, default=uuid4, editable=False)
name = CharField(max_length=50)
email = CharField(max_length=50)
class Book(Model):
id = UUIDField(primary_key=True, default=uuid4, editable=False)
name = CharField(max_length=50)
author = ForeignKey(Author, on_delete=models.CASCADE)
In my urls.py
author_router = SimpleRouter()
author_router.register(
r"author", AuthorViewSet, basename=author
)
nested_author_router = NestedSimpleRouter(author_router, r"author", lookup="author")
nested_author_router.register(r"book", BookViewSet)
In my searlizers.py
class BookSerializer(ModelSerializer):
class Meta:
model = Book
fields = (
"id",
"name",
"author",
)
extra_kwargs = {
"id": {"required": False},
"author": {"required": False},
}
class AuthorSerialzer(ModelSerializer):
class Meta:
model = Author
fields = (
"id",
"name",
"email",
)
extra_kwargs = {
"id": {"required": False},
}
In views.py
class BookViewSet(GenericViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def create(self, request, author_pk):
data = request.data
data["author"] = author_pk
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
Since books are related to the author and I am using nested routers the curl call would look like
curl --location --request POST 'localhost:8000/author/1/book' --data '{"name": "Book Name"}'
In my BookViewSet I end up manually adding the author_pk to the data object before calling serializer is_valid method. Is there a way to specify the source from URL route or any better way of doing this?
In this case you can pass the author_pk to save() to automatically set the author id of the newly created book, as explained here:
def create(self, request, author_pk):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(author_id=author_pk)
return Response(serializer.data)
I'm designing a mailbox with Django. My code is as follows:
#models.py
class Post(models.Model):
text = models.CharField(max_length=256)
sender = models.ForeignKey(User)
receiver = models.ForeignKey(User)
class Comment(models.Model):
post = models.ForeignKey(Post)
text = models.CharField(max_length=256)
#serializers.py
class CommentSerializer(serializers.ModelSerializer):
post = serializers.PrimaryKeyRelatedField()
class Meta:
model = Comment
fields = [
'id',
'text',
'post'
]
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = [
'id',
'text',
'sender',
'receiver',
]
class MainUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
I tried to customize serializer and have a serializer as follows:
class PostSerializer(serializers.Field):
def to_representation(self, value):
return PostSerializer(value, context={'request': self.context['request']}).data
def to_internal_value(self, id):
try:
id = int(id)
except ValueError:
raise serializers.ValidationError("Id should be int.")
try:
post = Post.objects.get(pk=id)
except User.DoesNotExist:
raise serializers.ValidationError("Such a post does not exist")
return user
I want to represent comment objects like this
{
"post":{
"text" = "Hello"
"sender" = 1
"receiver" = 2
}
"text": "Greate"
}
My code works great but The problem is it doesn't show the Combo Box for selecting the post. I also tried to customize the PrimaryKeyRelatedField's to_represent method in this way:
class PostSerializer(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
post_id = super(PostSerializer, self).to_representation(value)
post = Post.objects.get(pk=user_id)
return PostSerializer(
user, {"context":self.context['request']}
).data
but it says the unhashable type: 'ReturnDict' and as I understand we could return anything but simple things such as int or string. Is there a way to do this?
I can't figure out how to pass user object to the following serializer:
class ReviewSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Review
fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'review_text',)
I have this viewset:
class ReviewsViewSet(viewsets.ModelViewSet):
queryset = Review.objects.all()
serializer_class = ReviewSerializer
and this model:
class Review(models.Model):
title = models.CharField(max_length=255)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='reviews')
review_text = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{movie} review by {user}'.format(user=self.user, movie=self.movie)
My javascript request looks like this:
return axios({
method: 'post',
url: 'http://localhost:8000/api/reviews/',
data: { // Using data from Vue
title: this.review_title,
movie: this.id,
review_text: this.review_text,
user: JSON.stringify(this.user)
},
headers: {
'Content-Type': 'application/json',
Authorization: `JWT ${token}`
}
})
It gives me this traceback.
How should I pass the user object to the request?
Thanks in advance.
Remove read_only=True from serializer
class ReviewSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Review
fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'review_text',)
If you set read_only=True, the DRF will not takes the value from input source even if it's there
From the doc,
Read-only fields are included in the API output, but should not be
included in the input during create or update operations. Any
'read_only' fields that are incorrectly included in the serializer
input will be ignored.
Set this to True to ensure that the field is used when serializing a
representation, but is not used when creating or updating an instance
during deserialization.
Defaults to False
UPDATE
You should override the create() method of ReviewSerializer as
class ReviewSerializer(serializers.ModelSerializer):
user = UserSerializer()
def create(self, validated_data):
user_dict = validated_data.pop('user')
user_obj, created = User.objects.get_or_create(**user_dict)
return Review.objects.create(user=user_obj, **validated_data)
class Meta:
model = Review
fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'review_text',)
for debug purpose only
class ReviewsViewSet(viewsets.ModelViewSet):
queryset = Review.objects.all()
serializer_class = ReviewSerializer
def create(self, request, *args, **kwargs):
print(request.data) # print here <<<<
return super(ReviewsViewSet, self).create(request, *args, **kwargs)
This post has an update below.
I currently have these two models. I am trying to create a job using CreateAPIView. Before I show the view here are my models
class modelJobCategory(models.Model):
description = models.CharField(max_length=200, unique=True)
other = models.CharField(max_length=200, unique=False , blank=True , null=True)
class modelJob(models.Model):
category = models.ManyToManyField(modelJobCategory,null=True,default=None,blank=True)
description = models.CharField(max_length=200, unique=False)
These two are my serializers
class Serializer_CreateJobCategory(ModelSerializer):
class Meta:
model = modelJobCategory
fields = [
'description',
]
class Serializer_CreateJob(ModelSerializer):
class Meta:
model = modelJob
category = Serializer_CreateJobCategory
fields = [
'category',
'description',
]
def create(self, validated_data):
job = modelJob.objects.create(user=user,category=?,...) #How to get category ?
return job
Now this is my view
class CreateJob_CreateAPIView(CreateAPIView):
serializer_class = Serializer_CreateJob
queryset = modelJob.objects.all()
def post(self, request, format=None):
serializer = Serializer_CreateJob(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Now I am passing the following JSON
{
"category" :{
"description": "Foo"
},
"description" : "World"
}
However I get the exception
{
"category": [
"Incorrect type. Expected pk value, received str."
]
}
I came across the same question here and it mentions i need to define a slug field which I am not sure where. Any suggestion on how I can fix this ?
Update:
So my create Job serializer looks like this now however it returns back the error
Got AttributeError when attempting to get a value for field category
on serializer Serializer_CreateJob. The serializer field might be
named incorrectly and not match any attribute or key on the modelJob
instance. Original exception text was: 'ManyRelatedManager' object has
no attribute 'description'.
class Serializer_CreateJob(ModelSerializer):
category = serializers.CharField(source='category.description')
class Meta:
model = modelJob
category = Serializer_CreateJobCategory()
fields = [
'category',
'description',
]
def create(self, validated_data):
category_data = validated_data.pop('category')
category = modelJobCategory.objects.get(description=category_data['description'])
job = modelJob.objects.create(description=validated_data["description"])
job.category.add(category)
job.save()
return job
Any suggestions on how I can fix this now ?
Can you try this?
class Serializer_CreateJob(ModelSerializer):
category = serializers.SlugRelatedField(
many=True,
queryset=modelJobCategory.objects.all(),
slug_field='description'
)
class Meta:
model = modelJob
fields = [
'category',
'description',
]
Try to explicitly define category field and use source=category.description like this:
from rest_framework import serializers
class Serializer_CreateJob(ModelSerializer):
category = serializers.CharField(source='category.description')
class Meta:
model = modelJob
category = Serializer_CreateJobCategory
fields = [
'category',
'description',
]
def create(self, validated_data):
category_data = validated_data.pop('category')
category = Category.objects.get(description=category_data['description'])
job = modelJob.objects.create(description=validated_data['description'],category=category,...) #categy object found by it's description
return job
I want to save a sent json data to db by django-rest-framework.
the problem is, not saving the relation and returns error.
The bellow snippet is my models:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
family = models.CharField(max_length=50)
class Klass(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=500)
teacher = models.ForeignKey(Profile, related_name='teacher', on_delete=models.CASCADE)
I use below serializer for serializing/deserializing the Klass model.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('pk', 'name', 'family')
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
now when I prepare a JSON object and send it to the view, it returns error. the below is the view class:
class KlassView(APIView):
"""for SELECT, INSERT Queries"""
def get(self, request, pk):
# somthing
#csrf_exempt
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save()
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
and the error is:
The .create() method does not support writable nested fields by default.
Write an explicit .create() method for serializer mainp.serializers.KlassSerializer, or set read_only=True on nested serializer fields.
How can I save relation in KlassSerializer in order to save to db?
At first change your serializer like below:
class KlassSerializer(serializers.ModelSerializer):
# teacher = ProfileSerializer() # No need to this!
class Meta:
model = Klass
# fields = ('id', 'title', 'description', 'teacher')
fields = ('id', 'title', 'description') # Omit teacher
Then get profile from requested user and pass it to your serializer:
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save(teacher=request.user.profile) # Retrieve teacher and stroe
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
Just override the create method of ModelSerializer in KlassSerializer.
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
def create(self, validated_data):
profile = Profile.objects.filter(pk=validated_data['teacher']['pk'])
if profile:
k = Klass()
k.teacher = profile
...