Serialize ArrayModelField into JSON in Django DRF - django

I am using Djongo with Django 2.2. I am using MongoDb and using Djongo. i am facing issues in GET API. The Comment model is not serialized. Following is the code snippet.
models.py
import uuid
from djongo import models
class Comment(models.Model):
text = models.TextField();
author = models.TextField();
class Meta:
abstract = True
def __str__(self):
return self.author +self.text
class Scene(models.Model):
id = models.UUIDField(primary_key = True, default = uuid.uuid4,editable = False)
title = models.CharField(max_length=100)
comments = models.ArrayModelField(
model_container = Comment,
);
def __str__(self):
return self.title
serializers.py
from rest_framework import serializers
from planning.models import Scene, Comment
class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = ('text', 'author')
class SceneSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Scene
comments = CommentSerializer();
fields = ('id', 'title', 'comments')
viewsets.py
from planning.models import Scene, Comment
from .serializers import SceneSerializer, CommentSerializer
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from rest_framework.generics import RetrieveUpdateDestroyAPIView
class SceneViewSet(viewsets.ModelViewSet):
queryset = Scene.objects.all()
serializer_class = SceneSerializer
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
Output for GET scene model in DRF
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": "28db3aa8-34ef-4880-a3eb-57643814af22",
"title": "scene 1",
"comments": "[<Comment: edwardcomment1>, <Comment: edwardcomment2>]"
}
]
Expected output
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": "28db3aa8-34ef-4880-a3eb-57643814af22",
"title": "scene 1",
"comments": "[ { name : edward, text: comment1 },
{ name : edward, text: comment2 } ]"
}
]
The Comment which is ArrayModelField of Djongo is not serialized properly as expected.

Serialize comments outside of Meta:
from rest_framework import serializers
from planning.models import Scene, Comment
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('text', 'author')
class SceneSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Scene
fields = ('id', 'title', 'comments')
DRF nested relationships

Related

how to use objects.filter() for filtering a dictionary to a POST method in django rest framework

Models.py
class Category(models.Model):
name = models.CharField(max_length=50,null=False, blank=False)
def __str__(self):
return self.name
class Photo(models.Model):
category = models.ForeignKey(Category, on_delete=models.SET_NULL,related_name='category', null= True, blank= False)
image = models.ImageField(null= False, blank = False)
description = models.TextField(null=True, blank=True)
def __str__(self):
return self.description
Serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['name']
class PhotoSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=False)
class Meta:
model = Photo
fields = ['id','category','image','description']
view.py
from django.http import response
from rest_framework import viewsets
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from rest_framework.views import APIView
from .models import Category, Photo
from .serializers import PhotoSerializer
class DisplayAllViewSet(viewsets.ModelViewSet):
queryset = Photo.objects.all()
serializer_class = PhotoSerializer
class DisplayCategoryViseViewSet(APIView):
serializer_class = PhotoSerializer
def post(self, request, format=None):
data = self.request.data
print(data)
category = data['category']
print(category)
print(category['name'])
name=category['name']
queryset = Photo.objects.filter(category=name)
serializer = PhotoSerializer(queryset, many=True)
return Response(Serializer.data)
urls.py
from re import I
from django.db import router
from django.urls import path
from django.conf.urls import include
from rest_framework import routers
from .views import DisplayAllViewSet, DisplayCategoryViseViewSet
router = routers.DefaultRouter()
router.register('allImages',DisplayAllViewSet)
urlpatterns = [
path('', include(router.urls)),
path('category',DisplayCategoryViseViewSet.as_view()),
]
In Postman : GET request
http://127.0.0.1:8000/gallery/allImages
[
{
"id": 1,
"category": {
"name": "Sirsi"
},
"image": "http://127.0.0.1:8000/sunset.jpg",
"description": "Sunset view point"
},
{
"id": 2,
"category": {
"name": "Chickmangluru"
},
"image": "http://127.0.0.1:8000/tiger.jpg",
"description": "cheeta"
}
]
In Postman: POST request
http://127.0.0.1:8000/gallery/category
Body:
{
"category":{
"name":"Sirsi"
}
}
ValueError : Field 'id' is expected a number but got 'Sirsi'
I am not able to map request data with filter(category=name)
I know category is a Dict, But do we use Dict to map with name(Sirsi) in filter? to retrive the api using category wise.
Desired Outcome:
{
"id": 1,
"category": {
"name": "Sirsi"
},
"image": "http://127.0.0.1:8000/sunset.jpg",
"description": "Sunset view point"
},
Thank you in advance :)
In your post method of DisplayCategoryViseViewSet you need to make two changes as shown below. changes are added as comments in the code
class DisplayCategoryViseViewSet(APIView):
serializer_class = PhotoSerializer
def post(self, request, format=None):
data = self.request.data
print(data)
category = data['category']
print(category)
print(category['name'])
name=category['name']
queryset = Photo.objects.filter(category__name=name) #Since you are passing name, if you are passing id of the category, you can give category=name
print(queryset[0])
serializer = PhotoSerializer(queryset, many=True)
return Response(serializer.data) #Typo , Serializer should be serializer

How to prevent IntegrityError in a django model

I am learning django and I follow this blog project on codemy youtube channel: https://www.youtube.com/watch?v=_ph8GF84fX4&ab_channel=Codemy.comCodemy.com
And I wanted to improve my code with ForeignKey but I got stuck.
I want to add a Category into my Post model. So I used the ForeignKey. Not every post has a categroy since I added this model just recently, but I used the default argument in the Category class, which should solve this problem. However, trying several options, I cannot migrate my models and run the server again.
My code:
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class Category(models.Model):
cat_name = models.CharField(max_length=300, default="uncategorized")
def get_absolute_url (self):
return reverse("blog")
def __str__(self):
return self.cat_name
class Post(models.Model):
title = models.CharField(max_length=300)
author = models.ForeignKey(User, on_delete = models.CASCADE)
body = models.CharField(max_length=30000)
date = models.DateField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
def get_absolute_url (self):
return reverse("post", args = [str(self.id)])
The error:
django.db.utils.IntegrityError: The row in table 'blog_post' with primary key '1' has an invalid foreign key: blog_post.category_id contains a value 'uncategorized' that does not have a corresponding value in blog_category.id.
I have used the category in my forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ["title", "author", "category", "body"]
widgets = {
"title": forms.TextInput(attrs={"class": "form-control"}),
"author": forms.Select(attrs={"class": "form-control"}),
"category": forms.Select(attrs={"class": "form-control"}),
"body": forms.Textarea(attrs={"class": "form-control"}),
}
which is used in my views:
from django.shortcuts import render
from django.views.generic.base import TemplateView
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from . import models
from.forms import PostForm
from django.urls import reverse_lazy
class IndexView(TemplateView):
template_name = "index.html"
class PostListView(ListView):
model = models.Post
template_name = "post_list.html"
#ordering = ["-id"]
class PostDetailView(DetailView):
model = models.Post
template_name = "post_detail.html"
class AddPostView(CreateView):
model = models.Post
form_class = PostForm
template_name = "add_post.html"
#fields = ["title", "author", "body"]
class UpdatePostView(UpdateView):
model = models.Post
form_class = PostForm
template_name = "update_post.html"
#fields = ["title", "body"]
class DeletePostView(DeleteView):
model = models.Post
template_name = "delete_post.html"
success_url = reverse_lazy("blog")

POST and PATCH operations in ArrayModelField using Django DRF

I am using Django 2.2, MongoDb with Djongo. I am facing issues in POST and PATCH API. I am facing three kinds of issues.
When performing a POST operation to create a new entry, API fails with error: Array items must be Model instances.
What is the correct way to refer an instance of Screenplay class in POST API. Is the id of the parent class sufficient?
How to perform a update to a specific field in Scene model including a text field in comments?
Following is the code snippet.
Sample POST API data
{
"title": "intro1",
"screenplay": "49423c83-0078-4de1-901c-f9176b51fd33",
"comments": [
{
"text": "hello world",
"author": "director"
}
]
}
models.py
import uuid
from djongo import models
class Screenplay(models.Model):
id = models.UUIDField(primary_key = True, default = uuid.uuid4,editable = False)
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Comment(models.Model):
text = models.TextField();
author = models.TextField();
def __str__(self):
return self.author +self.text
class Scene(models.Model):
id = models.UUIDField(primary_key = True, default = uuid.uuid4,editable = False)
title = models.CharField(max_length=100)
screenplay = models.ForeignKey(Screenplay, related_name='scenes', on_delete=models.CASCADE)
comments = models.ArrayModelField(
model_container = Comment,
);
def __str__(self):
return self.title
serializers.py
from rest_framework import serializers
from planning.models import Scene, Comment
class ScreenplaySerializer(serializers.ModelSerializer):
class Meta:
model = Screenplay
fields = ('id', 'title')
class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = ('text', 'author')
class SceneSerializer(serializers.HyperlinkedModelSerializer):
comments = CommentSerializer();
class Meta:
model = Scene
fields = ('id', 'title', 'comments')
viewsets.py
from planning.models import Screenplay, Scene, Comment
from .serializers import ScreenplaySerializer, SceneSerializer, CommentSerializer
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from rest_framework.generics import RetrieveUpdateDestroyAPIView
class ScreenplayViewSet(viewsets.ModelViewSet):
queryset = Screenplay.objects.all()
serializer_class = ScreenplaySerializer
class SceneViewSet(viewsets.ModelViewSet):
queryset = Scene.objects.all()
serializer_class = SceneSerializer
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
I suggest you read the documentation on Writable nested representations, it will help to dissipate your doubts.

How to get username of user's post in Django REST API instead of number?

I'm trying to get username for iOS app through REST API.
I could get user number.
How do I get actual username?
The "author" should be username of user post.
http://127.0.0.1:8000/api/posts/
Result
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"author": 1,
"title": "Test Title Post",
"contents": "Test contents post"
}
models.py
User = settings.AUTH_USER_MODEL
class PostDetail(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="PostDetail.author+")
title = models.CharField('*Title', max_length=50)
contents = models.TextField('*Contents', max_length=450)
serializer.py
from rest_framework import serializers
from .models import PostDetail
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostDetailSerializer(serializers.ModelSerializer):
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
apis.py
from rest_framework import viewsets, routers
from blog.models import PostDetail
from blog.serializer import PostDetailSerializer
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostViewSet(viewsets.ModelViewSet):
queryset = PostDetail.objects.all()
serializer_class = PostDetailSerializer
router = routers.DefaultRouter()
router.register(r'posts', PostViewSet)
I expect "author": 1, to be like "author": admin,.
You need to change your PostDetailSerializer to:
from rest_framework import serializers
from .models import PostDetail
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostDetailSerializer(serializers.ModelSerializer):
author = serializers.CharField(source='author.username', read_only=True)
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
You can use SerializerMethodField for this task. You need to define a function to get the username of the author.
This is how you do it:
class PostDetailSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
def get_author(self, obj):
return obj.author.username
The function should be named as get_<field_name>.
if you have to use for input value to, you can create the custom serializer fields
class ForeignKeyField(serializers.RelatedFields):
def to_representation(self, obj):
return obj.name
this fields can be use in the serializer
class PostDetailSerializer(serializers.ModelSerializer):
author = ForeignKeyField()
class Meta:
model =PostDetail
fields = (author, title, 'contents', )

TypeError: __init__() got multiple values for keyword argument 'view_name'

I am using the Django Rest Framework, and I am not sure why I am getting this error.
models.py
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
followers = models.ManyToManyField('self', related_name='followees', symmetrical=False)
class Post(models.Model):
author = models.ForeignKey(User, related_name = 'posts')
title = models.CharField(max_length = 255)
body = models.TextField(blank = True, null = True)
class Photo(models.Model):
post = models.ForeignKey(Post, related_name = 'photos')
image = models.ImageField(upload_to = '%Y/%m/%d')
serializers.py
from rest_framework import serializers
from .models import *
class UserSerializer(serializers.ModelSerializer):
# Getting the list of posts made by particular users using the username.
posts = serializers.HyperlinkedIdentityField(
'posts',
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)
photos = serializers.HyperlinkedIdentityField(
'photos',
view_name = 'postphoto-list'
)
def get_validated_exclusions(self):
# Need to exclude 'author' since we'll add that later
# based off the request user
exclusions = super(PostSerializer, self).get_validated_exclusions()
return exclusions + ['author']
class Meta:
model = Post
class PhotoSerializer(serializers.ModelSerializer):
image = serializers.Field('image.url')
class Meta:
model = Photo
views.py
from rest_framework import generics, permissions
from .serializers import *
from .models import *
class UserList(generics.ListCreateAPIView):
model = User
serializer_class = UserSerializer
permission_classes = [
permissions.AllowAny # Publically available to anyone
]
class UserDetail(generics.RetrieveAPIView):
model = User
serializer_class = UserSerializer
lookup_field = 'username'
class PostList(generics.ListCreateAPIView):
model = Post
serializer_class = PostSerializer
permission_classes = [
permissions.AllowAny
]
class PostDetail(generics.RetrieveAPIView):
model = Post
serializer_class = PostSerializer
permission_classes = [
permissions.AllowAny
]
class UserPostList(generics.ListAPIView):
"""
Lists all the posts of a particular User.
"""
model = Post
serializer_class = PostSerializer
def get_queryset(self):
queryset = super(UserPostList, self).get_queryset()
return queryset.filter(author__username = self.kwargs.get('username'))
class PhotoList(generics.ListCreateAPIView):
model = Photo
serializer_class = PhotoSerializer
permission_classes = [
permissions.AllowAny
]
class PhotoDetail(generics.RetrieveAPIView):
model = Photo
serializer_class = PhotoSerializer
permission_classes = [
permissions.AllowAny
]
class PostPhotoList(generics.ListAPIView):
model = Photo
serializer_class = PhotoSerializer
def get_queryset(self):
queryset = super(PostPhotoList, self).get_queryset()
return queryset.filter(post__pk = self.kwargs.get('pk'))
urls.py in my app directory
from django.conf.urls import patterns, url, include
from .views import *
urlpatterns = [
# User URLs
url(r'^users/$', UserList.as_view(), name='user-list'),
url(r'^users/(?P<username>[0-9a-zA-Z_-]+)/$', UserDetail.as_view(), name='user-detail'),
url(r'^users/(?P<username>[0-9a-zA-Z_-]+)/posts/$', UserPostList.as_view(), name='userpost-list'),
# Post URLs
url(r'^posts/$', PostList.as_view(), name='post-list'),
url(r'^posts/(?P<pk>\d+)/$', PostDetail.as_view(), name='post-detail'),
url(r'^posts/(?P<pk>\d+)/photos/$', PostPhotoList.as_view(), name='postphoto-list'),
# Photo URLs
url(r'^photos/$', PhotoList.as_view(), name='photo-list'),
url(r'^photos/(?P<pk>\d+)/$', PhotoDetail.as_view(), name='photo-detail'),
]
When I try to run the check command on my terminal, or runserver, I get this error:
TypeError: init() got multiple values for keyword argument 'view_name'
What am I doing wrong exactly, and how can I fix this problem?
The first argument to HyperlinkedIdentityField is view_name. You're passing an extra initial argument, which seems to be the same as the field name; remove this argument.