I want to send an image array in a JSON as follows:
{"id":1,"timeIntervel":4,"images":["http://127.0.0.1:8000/images/i1.jpg","http://127.0.0.1:8000/images/i2.jpg","http://127.0.0.1:8000/images/i3.jpg","http://127.0.0.1:8000/images/i4.jpg","http://127.0.0.1:8000/images/i5.jpg"]}
I tried this with a foreignkey to my model. But failed to get a response as above.
model.py
class slidesModel(models.Model):
images = models.ImageField(upload_to='')
def __str__(self):
return str(self.images.name)
class slideImageArrayModel(models.Model):
timeIntervel=models.IntegerField()
images = models.ForeignKey(slidesModel, on_delete = models.CASCADE)
def __str__(self):
return str(self.id)
serializer.py
class slideSerializer(serializers.ModelSerializer):
class Meta:
model = slidesModel
fields='__all__'
class slideImgArraySerializer(serializers.ModelSerializer):
class Meta:
model = slideImageArrayModel
fields='__all__'
depth = 1
views.py
class slidesViewSet(viewsets.ViewSet):
def retrieve(self, request, pk=None):
queryset = slideImageArrayModel.objects.all()
user = get_object_or_404(queryset, pk=1) #it will always sends the first object
serializer = slideImgArraySerializer(user)
return Response(serializer.data)
My existing output is something which needs some modification to achieve the actual output:
{
"id": 1,
"timeIntervel": 4,
"images": {
"id": 8,
"images": "/images/b1.jpg"
}
}
I'm testing this in localhost and it's not showing complete url. I've included MEDIA_ROOT url already in settings and urls.py
Just add localhost:8000 in frontend part.
Like this code
<img src={"http://localhost:8000"+ note.img} alt="" role={'button'} onClick={(e)=> window.open(e.target.src)}/>
That will solve your issue.
Related
I have the following code working perfectly. I can create a Post object from DRF panel by selecting an image and a user. However I want DRF to populate the user field by the currently logged in user.
models.py
class Post(TimeStamped):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to='upload/')
hidden = models.BooleanField(default=False)
upvotes = models.PositiveIntegerField(default=0)
downvotes = models.PositiveIntegerField(default=0)
comments = models.PositiveIntegerField(default=0)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'user', 'photo']
views.py
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
serializer_class = PostSerializer
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
How can I do this?
Off the top of my head, you can just override the perform_create() method:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Give that a shot and let me know if it works
You can use CurrentUserDefault:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
It depends on your use case. If you want it to be "write-only", meaning DRF automatically populates the field on write and doesn't return the User on read, the most straight-forward implementation according to the docs would be with a HiddenField:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
If you want want it to be readable, you could use a PrimaryKeyRelatedField while being careful that your serializer pre-populates the field on write - otherwise a user could set the user field pointing to some other random User.
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
# set it to read_only as we're handling the writing part ourselves
read_only=True,
)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Finally, note that if you're using the more verbose APIView instead of generics.ListCreateAPIView, you have to overwrite create instead of perform_create like so:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
read_only=True,
)
def create(self, validated_data):
# add the current User to the validated_data dict and call
# the super method which basically only creates a model
# instance with that data
validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).create(validated_data)
You can avoid passing the user in your request and you won't see it in the output but DRF will populate it automatically:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.MyModel
fields = (
'user',
'other',
'fields',
)
As of DRF version 3.8.0 (Pull Request discussion), you can override save() in serializer.
from rest_framework import serializers
...
class PostSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Post
fields = ['id', 'user', 'photo']
def save(self, **kwargs):
"""Include default for read_only `user` field"""
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)
#DaveBensonPhillips's answer might work in your particular case for some time, but it is not very generic since it breaks OOP inheritance chain.
ListCreateAPIView inherits from CreateModelMixin which saves the serializer already. You should always strive to get the full chain of overridden methods executed unless you have a very good reason not to. This way your code stays DRY and robust against changes:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).perform_create(serializer)
You will have to override the default behavior of how generics.ListCreateAPIView creates an object.
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get_serializer_class(self):
if self.request.method == 'POST':
return CreatePostSerializer
else:
return ListPostSerializer
def create(self, request, *args, **kwargs):
# Copy parsed content from HTTP request
data = request.data.copy()
# Add id of currently logged user
data['user'] = request.user.id
# Default behavior but pass our modified data instead
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
The .get_serializer_class() is not necessary as you can specify which fields are read-only from your serializer, but based on the projects I have worked on, I usually end up with 'asymmetric' serializers, i.e. different serializers depending on the intended operation.
Try this:
def post(self, request, format=None)
serializer = ProjectSerializer(data=request.data)
request.data['user'] = request.user.id
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
This is what works for me in serializers.py, where I am also using nested data. I want to display created_by_username without having to lookup other users.
class ListSerializer(serializers.ModelSerializer):
"""
A list may be created with items
"""
items = ItemSerializer(many=True)
# automatically set created_by_id as the current user's id
created_by_id = serializers.PrimaryKeyRelatedField(
read_only=True,
)
created_by_username = serializers.PrimaryKeyRelatedField(
read_only=True
)
class Meta:
model = List
fields = ('id', 'name', 'description', 'is_public',
'slug', 'created_by_id', 'created_by_username', 'created_at',
'modified_by', 'modified_at', 'items')
def create(self, validated_data):
items_data = validated_data.pop('items', None)
validated_data['created_by_id'] = self.context['request'].user
validated_data['created_by_username'] = self.context['request'].user.username
newlist = List.objects.create(**validated_data)
for item_data in items_data:
Item.objects.create(list=newlist, **item_data)
return newlist
I wrote an extension to DRF's serializer below
from rest_framework import serializers
class AuditorBaseSerializer(serializers.Serializer):
created_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
updated_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
def save(self, **kwargs):
# if creating record.
if self.instance is None:
kwargs["created_by"] = self.fields["created_by"].get_default()
kwargs["updated_by"] = self.fields["updated_by"].get_default()
return super().save(**kwargs)
and it can be used as follows
class YourSerializer(serializers.ModelSerializer, AuditorBaseSerializer):
class Meta:
model = SelfEmployedBusiness
fields = (
'created_by',
'updated_by',
)
I am working on a Django application with two Models.
Messages - that contain twitter style messages
Feedback - response to messages including comments, likes, dislikes
I am writing APIView for Feedback, but it is not GETing or POSTing the relevant messages even though I can browse through the admin panel.
The code is as follows:
Models:
class Messages(models.Model):
postIdentifier = models.AutoField(primary_key=True)
title = models.CharField(max_length=100)
message = models.TextField(null=False)
class Feedback(models.Model):
isLiked = models.BooleanField(null=True)
isDisliked = models.BooleanField(null=True)
comment = models.TextField(null=True)
post = models.ForeignKey(Messages, on_delete=models.CASCADE)
Serializers:
class MessagesSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ['postIdentifier', 'title', 'message']
class FeedbackSerializer(serializers.ModelSerializer):
message = MessagesSerializer()
class Meta:
model = Feedback
fields = ['isLiked', 'isDisliked', 'comment', 'post']
APIView for Feedback model
def get(self, request, id):
related_message_object = Messages.objects.filter(id)
feedback = Feedback.objects.filter('post'= related_message_object)
serializer = FeedbackSerializer(feedback)
return Response({
'data': serializer.data
})
def post(self, request, *args, **kwargs):
serializer = FeedbackSerializer(data= request.data)
if serializer.is_valid():
serializer.save()
return Response({
'message': 'Feedback data posted successfully!',
'data': serializer.data
})
The URLPattern for the feedback is
path('feedback/< id >/', FeedbackAPIView.as_view()),
#I am aware of spaces here on either side of id as I am unable to post the id with brackets on.
I am going through the rest_framework documentation, but I can not find relevant documentation with regard to the problem. Can anyone kindly point out my mistake in the get() method of FeedbackAPIView. I am hoping to achieve that when I browse feedback/2/ I want to see feedback relavant to Message with postIdentifier = 2. Any feedback is much appreciated!
As the feedback resource url is path('feedback/< id >/', FeedbackAPIView.as_view()),
then at def get(self, request, id): you should filter feedback by id :
feedback = Feedback.objects.filter(id=id).first()
I'm using Django Rest Framework and I want to send a string array as response as follows:
['data2','data3']
These data are dynamically generated from the model where only values are selected corresponsing to key 'field1' with a filter criterion 'field3'='type1'
If following is the data I've in my model:
[{
'field1':'data1',
'field2':'nodata1',
'field3':'type2'
},
{
'field1':'data2',
'field2':'nodata2'
'field3':'type1'
},
{
'field1':'data3',
'field2':'nodata3',
'field3':'type1'
}]
I've tried with Response(). And using that I could able to send a string but not an array.
NB: I don't want to generate a JSON reponse as shown below.
[{
"field1": "data2"
},
{
"field1": "data3"
}]
Updating the answer:
You can achieve this by using the base APIView class. Below is the sample code:
views.py:
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Label
class LabelList(APIView):
def get(self, request):
response = []
labels = Label.objects.all()
response = [label.field1 for label in labels]
return Response(response)
------------Old Answer-------------
Use generics.ListAPIView to provide the data in an array and use serializer to only have field1.
Let see the sample code.
views.py:
# I am assuming your data to be a label, you can use any name for the class.
class LabelList(generics.ListAPIView):
serializer_class = LabelSerializer
def get_queryset(self):
# For perform any filters based upon loggedin user
return Label.objects.filter(owner=self.request.user)
serializers.py:
class LabelSerializer(serializers.ModelSerializer):
class Meta:
model = Label
fields = [
'field1',
]
How do I make sure that Many-To-Many relationships are considered in my POST-request to a Django Rest Framework API?
I have the following models:
models.py
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class Blog(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
tags = models.ManyToManyField(Tag, blank=True, related_name="blogs")
url = models.URLField(max_length=250, unique=True)
owner = models.ForeignKey(User, related_name="blogs", on_delete=models.CASCADE)
slug = models.CharField(max_length=20, default="blogs")
def __str__(self):
return self.name
And I am making the request like:
Frontend (don't mind the missing brackets)
const addContent = (content) => {
axiosInstance
.post(`/content/blogs/`, content, tokenConfig(auth.token))
.then((res) => {
dispatchMessages(
createMessage({ contentAdded: "Submitted successfully" })
);
The content object I am passing in looks like:
const content = {
name: "content title",
description: "content description",
url: "content URL",
tags: ["tag1", "tag2", "tag3"],
};
The POST request itself is going through and all the fields are posted correctly except for the tags, which appear empty.
Example Response:
{
"id": 2,
"tags": [],
"name": "Blog #1",
"description": "Its the best",
"url": "https://website.com",
},
My serializer looks like:
serializers.py
class BlogSerializer(serializers.ModelSerializer):
tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field="name")
owner = CustomOwnerField(read_only=True)
class Meta:
model = Blog
fields = "__all__"
And the viewset:
api.py
class BlogViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticatedOrReadOnly
]
serializer_class = BlogSerializer
def get_queryset(self):
return Blog.objects.all()
Thank you for any tips
you have done all the tedious work. The only thing that is not allowing the tags to get saved is the read_only=True in the SlugRelatedField argument. This argument ignores the field when it is posted. So you have to remove read_only=True so that tags get parsed. I would go a little further and add queryset in the slugrelatedfield as queryset=Tags.objects.all()
This would only work if you have already created tags in your db and then you add the same names in your list. If you want to create them dynamically when you post them you have to modify the default create method in your serializer(check here)
I am creating small Django/AngularJS app. User can create account, view all created posts and add his own posts.
The current version of app is here: GitHub
Models: models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
# Post model
class Post(models.Model):
date = models.DateTimeField(default=datetime.now)
text = models.CharField(max_length=250)
author = models.ForeignKey(User)
Views: views.py
from django.shortcuts import render
from rest_framework import generics, permissions
from serializers import UserSerializer, PostSerializer
from django.contrib.auth.models import User
from models import Post
from permissions import PostAuthorCanEditPermission
...
class PostMixin(object):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
class PostDetail(PostMixin, generics.RetrieveUpdateDestroyAPIView):
pass
...
Serializers: serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from models import Post
class UserSerializer(serializers.ModelSerializer):
posts = serializers.HyperlinkedIdentityField(view_name='userpost-list', lookup_field='username')
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'posts', )
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False)
def get_validation_exclusions(self, *args, **kwargs):
# Need to exclude `user` since we'll add that later based off the request
exclusions = super(PostSerializer, self).get_validation_exclusions(*args, **kwargs)
return exclusions + ['author']
class Meta:
model = Post
But when I create request (main.js) to add new post to DB like this:
var formPostData = {text: $scope.post_text};
$http({
method: 'POST',
url: '/api/posts',
data: formPostData,
headers: {'Content-Type': 'application/json'}
})
it raises error:
Request Method: POST
Request URL: http://127.0.0.1:8000/api/posts
Django Version: 1.7.6
Exception Type: IntegrityError
Exception Value:
NOT NULL constraint failed: nucleo_post.author_id
I thought, that this code adds author to the post model before saving. But it doesn't work correctly now.
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
So I need some help...
this is because the request does not have the user information. For request.user to work, the request should include the authorization related information. Inclusion of this information depends on what authorization mechanism you are using. If you are using token or session based authentication then token or session key should be the part of request header or query param(depends on server implementation). If you are using rest_framework's login view then you should pass username:password along with the request.