I'd like to allow users to perform POST request to create a blog post, and to use the topic name instead of the topic id as an option.
A minimal model might look as follows.
models.py
class Topic(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=100)
class Post(models.Model):
name = models.CharField(max_length=30)
topic = models.ForeignKey(Topic, on_delete=models.PROTECT)
created_on = models.DateTimeField(blank=True, auto_now_add=True, editable=False)
Now there are 2 possible approaches that I've considered:
1) Keep the views simple.
views.py
class PostList(ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
serializers.py
class PostSerializer(serializers.ModelSerializer):
topic_name = serializers.CharField()
class Meta:
model = Topic
fields = ('name', 'topic_name', 'created_on')
read_only_fields = ('created_on',)
def validate_topic_name(self, value):
"""Verify that the topic exists."""
if not Topic.objects.filter(name=value).exists():
raise serializers.ValidationError("Specified Topic Name does not exist!")
else:
return value
def create(self, validated_data):
"""Create a Post."""
topic_name = validated_data.pop('topic_name', None)
topic = Topic.objects.get(name=topic_name)
return Post.objects.create(topic=topic, **validated_data)
2) Keep the serializers simple.
views.py
class PostList(ListCreateAPIView):
queryset = Post.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
topic = get_object_or_404(Topic, name=serializer.data['topic_name'])
post = Post.objects.create(
name=serializer.data['name'],
topic=topic)
return Response({'name': serializer.data['name'], 'description': serializer.data['topic_name']},
status=status.HTTP_201_CREATED)
serializers.py
class PostSerializer(serializers.ModelSerializer):
topic_name = serializers.CharField()
class Meta:
model = Topic
fields = ('name', 'topic_name', 'created_on')
read_only_fields = ('created_on',)
My question are:
Where should I put the complexity? In the serializer on in the views?
Is there a better way to proceed?
There's no need to put this complexity into your code at all, because this function is already provided by serializers.SlugRelatedField, which allows you to refer to a related field using a string instead of an id. Here's how it looks with your model:
views.py
class PostList(ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
serializers.py
class PostSerializer(serializers.ModelSerializer):
topic = serializers.SlugRelatedField(slug_field='name',
queryset=Topic.objects.all())
class Meta:
model = Post
fields = '__all__'
Related
currently when i send a get request i get what i want. But when i do a Post it throws an error saying {"user": ["This field is required."]} even though i put 'user' in read_only_fields.
heres the code:
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
class SaleHistorySerializier(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = SaleHistory
fields =['id', 'user', 'product', 'date_bought']
read_only_fields = ('user',)
depth = 1
models.py
class SaleHistory(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner')
product = models.ForeignKey(SaleItems, on_delete=models.RESTRICT, default=None)
date_bought = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f' {self.product}, {self.user}, {self.id}'
api.py create part
class SaleHistoryVS(viewsets.ViewSet):
permission_classes = [permissions.IsAuthenticated]
def create(self, request):
serializer = SaleHistorySerializier(data=request.data, many=True)
if serializer.is_valid():
serializer.save(user = request.user)
return Response(serializer.data, status= status.HTTP_201_CREATED)
return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST)
how do i make it so that i get to create post request without having to user in my post data.
Replace user = UserSerializer() with user = UserSerializer(read_only=True)
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 a student studying django rest framework
I tried to upload multiple file with form-data
when i send request like this in postman
error occurs and this is error message
images_data = self.context.get('request').request.FILES
AttributeError: 'NoneType' object has no attribute 'request'
this is model
class Post(models.Model):
text = models.CharField(max_length=5000)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Image(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
image = models.FileField(blank=True)
And views
class AddPost(APIView):
serializer_class = PostSerializer
def post(self, request, format=None):
serializer = PostSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return JsonResponse({'status':status.HTTP_200_OK, 'message':"sucess", 'data':""})
serializers
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
images = FileSerializer(source='image_set', many=True, read_only=True)
class Meta:
model = Post
fields = ('id', 'text', 'owner', 'created_at', 'images')
def create(self, validated_data):
images_data = self.context.get('request').request.FILES
images_data = self.context.get('request').request.FILES
post = Post.objects.create(text=validated_data.get('text'),owner=validated_data.get('owner'))
for image_data in images_data.values():
Image.objects.create(post=post, image=image_data)
return post
i already saw this link
https://codeday.me/en/qa/20190306/12057.html
thanks for your help
Just pass context to your serializer in views.py
PostSerializer(data=request.data, context={'request': request})
And then you will be able to get request in the context in serializers.py
self.context.get('request')
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 have A models.py as this
models.py
class User(AbstractUser, BaseModel):
full_name = models.CharField(max_length=64)
addresss=models.CharField(max_length=40)
phoneno=models.IntegerField(null=True, blank=True)
email=models.EmailField()
password=models.CharField(max_length=40)
re_password=models.CharField(max_length=40)
gender=models.IntegerField(choices=gender_choice,default='0')
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('full_name','id','addresss','phoneno','email','gender')
views.py
class StudentViewset(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
http_methods = ['get', 'post','put','delete']
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
row = serializer.data
user=User.objects.get_or_create(
defaults={'full_name':row['user']['full_name'],'addresss':row['addresss'],
'phoneno':row['phoneno'],'password':row['password'] ,'re_password':row[['re_password'],
'gender':row['gender'] ,'username':username})
My Question:
After Registering Student, I want to generate an email like his/her(address#gmail.com).
How can i do This ?