I am creating APIView for book list and add, delete.
Is this serialization the right way?
I want to show a list of books, but I do not know what to put in '?'.
member/models.py
class MyUser(AbstractBaseUser, PermissionsMixin):
username = models.EmailField(unique=True, max_length=256)
nickname = models.CharField(max_length=256)
is_staff = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
mybook = models.ManyToManyField(Book)
book/models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=256)
author = models.CharField(max_length=200)
def __str__(self):
return self.title
book/seralizers.py
from rest_framework import serializers
from book.models import Book
class MyBookSerializer(serializers.ModelSerializer):
mybook = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Book
fields = (
'title',
'author',
)
apiview.py
class MyBook(APIView):
def get(self, request):
mybook = Book.objects.filter(myuser=?)
serializer = MyBookSerializer(mybook)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request):
pass
def delete(self, request):
pass
If you want create a CRUD for model, depends on a request.user you can simply use ModelViewSet:
from rest_framework import viewsets
class MyBookViewSet(viewsets.ModelViewSet):
serializer_class = MyBookSerializer
def get_queryset(self, request):
return Book.objects.filter(myuser=self.request.user)
That's all for view. You only need to specify router for a viewset.
urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register('books', MyBookViewSet)
urlpatterns = []
urlpatterns += router.urls
Include this usl file in a root urls and you are done.
You also need to specify related_name for m2m field if you want to use myuser name:
mybook = models.ManyToManyField(Book, related_name='myuser')
And you don't need this field in a serializer:
mybook = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
viewsets docs
routers docs
Related
I'm getting a serializer error:
"Upload a valid image. The file you uploaded was either not an image
or a corrupted image"
When trying to serialize an uploaded image
My code:
Models:
class Post(models.Model):
text = models.TextField(max_length=10000)
class Image(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
image = models.ImageField(blank=False, null=False, upload_to='test')
View:
class CreateNewPostAPIView(generics.CreateAPIView):
serializer_class = serializers.NewPostSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
if self.request.data.get('image', None):
context['image'] = self.request.data.pop('image', None)
return context
Serializers:
class NewPostSerializer(serializers.ModelSerializer):
def create(self, validated_data):
post = Post.objects.create(**validated_data)
image = self.context['image'][0] # working with only one image for now
# image is of type <class'django.core.files.uploadedfile.InMemoryUploadedFile'>
serializer = ImageSerializer(data={'post': post.pk, 'image': image})
is_valid = serializer.is_valid() # it's always False!
return post
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('post', 'image')
I already tried doing that without a serializer and it's working with this code:
class NewPostSerializer(serializers.ModelSerializer):
def create(self, validated_data):
post = Post.objects.create(**validated_data)
image = self.context['image']
Image.objects.create(post=post.pk, image=image)
return post
But I need to do it using a serializer.
The easy way for this would be like:
Models:
from django.db import models
# Create your models here.
class Post(models.Model):
text = models.TextField()
def __str__(self):
return self.text[:50]
class Image(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE,
related_name='images', editable=False
)
image = models.ImageField(upload_to='images/')
def __str__(self):
return self.image.name
def delete(self, *args, **kwargs):
self.image.delete()
super().delete(*args, **kwargs)
Serializer:
from rest_framework import serializers
from .models import Post, Image
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
class PostWithImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(write_only=True)
# This is for the GET request or the response of the POST request
# We can also work with a separate serializer for such cases
images = ImageSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = "__all__"
def create(self, validated_data):
image_data = validated_data.pop('image')
post = Post.objects.create(**validated_data)
Image.objects.create(post=post, image=image_data)
return post
Views as:
from rest_framework.response import Response
from rest_framework import status
from .serializers import *
# Create your views here.
class AddPostWithAnImageView(APIView):
serializer_class = PostWithImageSerializer
def post(self, request):
serializer = self.serializer_class(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)
For testing purposes, please use Postman where you can upload images with so much ease. The request form should only require the text and the image fields.
Thanks to #pKiran, who gave me an idea, now I have a working code for saving one or more images. It's, also, quite concise and readable. There's quite a lot going on here with so little of code. Django REST is quite confusing.
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('image', 'thumb')
class NewPostSerializer(serializers.ModelSerializer):
image = serializers.ListField(child=serializers.ImageField(), write_only=True, required=False)
def create(self, validated_data):
images = validated_data.pop('image', None)
post = Post.objects.create(**validated_data)
if images:
img_models = [Image(post=post,
image=image, thumb=make_thumb(image))
for image in images]
Image.objects.bulk_create(img_models)
return post
I am making a POST api using DRF. In that api, I need only few fields(name, size, customer_name, customer_address), but don't require this fields(status, ordered_time) because these fields I want to save these fields in run time as status='open' and ordered_time=DateTimeField.now()
views.py
class PizzaOrderCustomerView(APIView):
def post(self, request):
orders = request.data.get('orders')
# Create an article from the above data
serializer = ArticleSerializer(data=orders)
if serializer.is_valid(raise_exception=True):
article_saved = serializer.save()
return Response({"success": "Article '{}' created successfully".format(article_saved.name)})
models.py
class PizzaOrder(models.Model):
name = models.CharField(max_length=120)
size = models.CharField(max_length=10, choices=SIZE_CHOICE, default='MEDIUM')
customer_name = models.CharField(max_length=120)
customer_address = models.TextField()
ordered_time = models.DateTimeField(default=timezone.now, editable=False)
status = models.CharField(max_length=20, default='open', editable=False)
serializers.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
# fields = '__all__'
read_only_fields = ('status',)
But when I try to create an order, it needed status and ordered_time also. But it should save at the time of creating order automatically.
Suggest a good way to do it.
from rest_framework import viewsets, mixins
class PizzaViewsets(viewsets.ViewSet, mixins.CreateModelMixin):
model = PizzaOrder
serializer_class = OrderSerializer
queryset = model.objects.all(
serializer, it is always good practise to mention all fields instead of
all
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = ('status','ordered_time','name', 'size', 'customer_name', 'customer_address',)
read_only_fields = ('status','ordered_time',)
I have basic django app that returns data from Postgres tables in an AWS RDS instance.
I have a few endpoint which relate to few tables in the RDS.
What I want to do is update records in table. If the record exists it should be updated, if it does not exist then nothing should happen Orit should throw an error.
This is my models.py file -
from django.db import models
class Campaigns(models.Model):
id_campaign = models.CharField(max_length=250, blank=True, null=True)
status = models.CharField(max_length=250, blank=True, null=True)
id_taste_cluster = models.CharField(max_length=250, blank=True, null=True)
I want to update the status field in this table where status = ' '.
This is my serialiser file -
from rest_framework import serializers
from .models import Campaigns
class Campaigns_1_Serializer(serializers.ModelSerializer):
class Meta:
model = Campaigns
fields = ('id_campaign', 'status', 'id_taste_cluster')
This is my views file -
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import generics
from .models import Campaigns
from .serializers import Campaigns_1_Serializer
class UpdateView(viewsets.ModelViewSet):
def update(self, request, *args, **kwargs):
data = request.DATA
queryset = Campaigns.objects.filter(status='')
serializer = Campaigns_1_Serializer(queryset, data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
I want to give the users the option to update only those campaigns for which the status is ' '(blank).
How can I do that ?
You need to change your update method :
#your imports
from rest_framework import serializers
class YourModelViewset(ModelViewSet):
#your code
def update(self, request, *args, **kwargs):
instance = self.get_object()
# checks if the object staus is empty
If object.status == "" or object.status is None :
raise serializers.ValidationError("your error message")
return super(YourModelViewset, self).update(request, *args, **kwargs)
I am creating Serializers in my DRF and so far it's working good the problem is that it is showing data of all the users
serializers.py
from rest_framework import serializers
from .models import Quiz
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Quiz
fields = ('foo', 'bar')
How do I make my Serializers User Specific such that it only returns the data of the user who is using the app?
Views.py
class TodoView(viewsets.ModelViewSet):
serializer_class = TodoSerializer
queryset = Quiz.objects.all()
User specific filtering has nothing to do with serializers. Serializers are used to convert complex python objects to/from native python datatypes that can be rendered in JSON/XML etc. It is convenient to do your filtering in your view. Here is an example by using mixins:
# views.py
from .models import Quiz
from .serializers import TodoSerializer
from rest_framework import mixins, viewsets
from rest_framework.response import Response
class TodoListViewSet(viewsets.GenericViewSet, mixins.ListModelMixin):
queryset = models.Quiz.objects.all()
serializer_class = TodoSerializer
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
# filter your todos here
queryset = queryset.filter(user=request.user)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
I am trying to return a queryset as a JSON response in DRF but I always get a TypeError Object Not JSON serializable.
I have tried different methods but none of them worked. I have tried to use the JSONRenderer class and I have also tried to serialize a single object using SentSerializer. But nothing seems to solve the issue. I have just started learning DRF so it is a little confusing to me and really don't understand how serializers work and I am not sure if I have written them correctly or using them correctly.
# models
...
from django.contrib.auth.models import User
class Sentence(models.Model):
sent = models.CharField(max_length=255)
sent_correct = models.CharField(max_length=255, null=True)
pub_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='sentences')
# serializers
...
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
username = serializers.CharField(max_length=50)
class Meta:
model = User
# Tuple of serialized model fields (see link [2])
fields = ( "id", "username", "password", )
class SentSerializer(serializers.ModelSerializer):
sent = serializers.CharField(max_length=255)
sent_correct = serializers.CharField(max_length=255, required=False)
author = UserSerializer(read_only=True, required=False)
class Meta:
model = Sentence
fields = (
'sent', 'sent_correct', 'author'
)
# views
...
class SentCreateAPIView(APIView):
serializer_class = SentSerializer
permission_classes = (IsAuthenticated,)
def post(self, request):
ss = Sentence.objects.filter(author=request.user)[0:1]
ss = list(ss)
print("sentences " + str(ss))
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
vd = serializer.validated_data
sent_str = vd['sent']
s = Sentence(sent=sent_str, sent_correct=sent_str)
s.author = request.user
print(vd)
print(request.user)
s.save()
sent = nlp(sent_str)
tokens = [t.text for t in sent] # this list returns successfully
return Response(
{ 'sent': sent_str,
'sent_correct': sent_str,
'tokens': tokens,
'ss': ss, # this list throws TypeError},
status=status.HTTP_201_CREATED
)
Jakub Maślanka answered my question on Facebook in Django Python Web Framework group. I had to serialize the queryset like this:
# views.py
...
return Response(
{ 'sent': sent_str,
'sent_correct': sent_str,
'tokens': tokens,
'ss': SentSerializer(ss, many=True).data},
status=status.HTTP_201_CREATED
)
ss -> dict
Why dont you use generics.ListAPIView.
It has get_query_set method
You just need to override that method by your own query set
Less code is better -> class based and framework