Sending tags via HTTP Post to Django web api endpoint - django-rest-framework

I am trying to send tags to my web application, using Django Rest Framework and Django Taggit. I tried to adapt the approach from here, but no luck so far. Here is my try:
models.py:
class Tool(models.Model):
name = models.CharField(max_length=100)
tags = TaggableManager(blank=True)
created = models.DateTimeField(auto_now_add=True)
def get_tags_display(self):
return self.tags.names()
serializers.py:
class ToolSerializer(TaggitSerializer, serializers.ModelSerializer):
name = serializers.CharField(max_length=100)
tags = serializers.Field(source='get_tags_display')
class Meta:
model = Tool
fields = ('tool_id', 'name', 'tags')
views.py:
class ToolList(generics.ListCreateAPIView):
queryset = Tool.objects.all()
serializer_class = ToolSerializer
def perform_create(self, serializer):
if 'tags' in self.request.data:
self.objects.add(*self.request.data['tags'])
return super(ToolList, self).perform_create(self, serializer)
using httpie:
http --form POST http://127.0.0.1:8000/tools/ name="Hammer" tags:='["new", "heavy"]'
Am I going in the right direction?

Related

Django REST error when serializing uploaded image

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

How to return a queryset as a JSON response in Django RESTFramework?

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

Change model field if requested particular url

Im making API using django rest framework . I only want to change one field in model which is the read field if i go to a particular url
my model:
class Notification(PolymorphicModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_by = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="creatednotifications")
created_on = models.DateTimeField(default=timezone.now)
created_for = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="receivednotifications")
read = models.DateTimeField(default=None, null=True, blank=True)
message = models.CharField(default=None, blank=True, null=True, max_length=800)
The APis i made lists the notifications for a logged in user.
What i want to implement is that :
notification/<:id>/markread
notification/<:id>/markunread
If i go to this particular url i want to modify the read field ..For example make it None if to mark unread. Also i need to check if the logged in user has received the notification with that id.I know the basics and how to create the urls
class NotificationMarkRead(generics.UpdateAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
queryset = Notification.objects.filter(created_for=self.request.user)
return queryset
class NotificationMarkUnread(generics.UpdateAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
queryset = Notification.objects.filter(created_for=self.request.user)
return queryset
def update
My initial try is to override the put method in update_API view
Write a simple function:
#api_view(['PUT'])
def notification_toggle_read_status(request, pk, read_status):
notification = Notification.objects.get(pk=pk)
if read_status == 'markread':
notification.read = timezone.now()
else:
notification.read = None
notification.save(update_fields=['read'])
serializer = NotificationSerializer(instance=notification)
return Response(serializer.data)
use this url path:
notifications/<int:pk>/<string:read_status>/
As you have already coding with DRF why not try with viewset link . And from front-end just pass update fields with put request.

Django REST Framework: integer fields default value not showing up in browseable API form

Using django 1.6 and rest framework 2.3.13
In model class:
class A(models.Model):
some_name = models.PositiveSmallIntegerField(default=15)
In serilizer:
class ASerializer(ModelSerializer):
class Meta:
model = A
fields = ( 'some_name' )
In view:
class AViewSet(viewsets.ModelViewSet):
queryset = A.objects.all()
serializer_class = ASerializer
But in the api form, it's showing as 0, any idea?
This problem can be solved by adding
if obj is None and self.default is not None:
return self.default
to rest_framework/fields.py
Original pull request:
https://github.com/tomchristie/django-rest-framework/pull/1248/files

Django 1.3 CreateView, ModelForm and filtering fields by request.user

I am trying to filter a field on a ModelForm. I am subclassing the generic CreateView for my view. I found many references to my problem on the web, but the solutions do not seem to work (for me at least) with Django 1.3's class-based views.
Here are my models:
#models.py
class Subscriber(models.Model):
user = models.ForeignKey(User)
subscriber_list = models.ManyToManyField('SubscriberList')
....
class SubscriberList(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=70)
....
Here is my view:
#views.py
class SubscriberCreateView(AuthCreateView):
model = Subscriber
template_name = "forms/app.html"
form_class = SubscriberForm
success_url = "/app/subscribers/"
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
return super(SubscriberCreateView, self).form_valid(form)
Here is my original form for adding a Subscriber, with no filter:
#forms.py
class SubscriberForm(ModelForm):
class Meta:
model = Subscriber
exclude = ('user', 'facebook_id', 'twitter_id')
Here is my modified form, attempting to filter, but doesn't work:
#forms.py
class SubscriberForm(ModelForm):
class Meta:
model = Subscriber
exclude = ('user', 'facebook_id', 'twitter_id')
def __init__(self, user, **kwargs):
super(SubscriberForm, self).__init__(**kwargs)
self.fields['subscriber_list'].queryset = SubscriberList.objects.filter(user=user)
If I change this modified form as so:
def __init__(self, user=None, **kwargs)
It works - It brings me NO subscriber lists. But any way I try to pass the request user, I invariably get a a name "request" or name "self" not defined error.
So, how can I modify my code to filter subscriber_list by the request.user, and still use Django 1.3's CreateView.
I see you've been posting this question in various places.. and the way I found that is because I was trying to figure out the same thing. I think I just got it working, and here's what I did. I overwrote get_form() from FormMixin to filter a specific form fields queryset:
class MyCreateView(CreateView):
def get_form(self, form_class):
form = super(MyCreateView,self).get_form(form_class) #instantiate using parent
form.fields['my_list'].queryset = MyObject.objects.filter(user=self.request.user)
return form

Resources