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.
Related
I’m making a music player with a DRF backend.
I have two models, one is Song and the other is TrackQueue
In the browser, the “nowplaying” instance of TrackQueue shows the meta of the queued song with a link to the file in its meta.
What I need now is a url that always produces that instance of the “nowplaying” TrackQueue (id=1)
What would that url be and how can I create it?
Thank you
models.py
class Song(models.Model):
title = models.CharField(max_length=24)
file = models.FileField()
def __str__(self):
return self.title
class TrackQueue(models.Model):
title = models.CharField(max_length=64)
is_song = models.OneToOneField(Song, on_delete=models.CASCADE)
def __str__(self):
return self.title
Serializers.py
from rest_framework import serializers
from .models import Song, TrackQueue
class SongSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Song
fields = ('id' ,'title', 'file')
class TrackQueueSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TrackQueue
fields = ('id' , 'title', 'is_song')
views.py
from django.shortcuts import render
from .serializers import SongSerializer
from rest_framework import viewsets
from .models import Song, TrackQueue
from music.serializers import SongSerializer, TrackQueueSerializer
class SongView(viewsets.ModelViewSet):
serializer_class = SongSerializer
queryset = Song.objects.all()
class TrackQueueView(viewsets.ModelViewSet):
serializer_class = TrackQueueSerializer
queryset = TrackQueue.objects.all()
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")
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
Having the following Model:
class Book(models.Model):
name = models.CharField()
author = models.CharField()
date = models.DateField()
class Meta:
unique_together = ('name', 'author')
class BookSerializerWrite(serializers.ModelSerializer):
class Meta:
model = Book
class BookView(ApiView):
def put(self, request, *args, **kwargs):
serializer = BookSerializerWrite(data=request.data)
if serializer.is_valid():
serializer.save()
The view above does not work as the serializer.is_valid() is False.
The message is:
'The fields name, author must make a unique set'
Which is the constraint of the model.
How do I update the model?
I would rather not override the serializer's validation method.
I also cannot access the validated_data for an update as in
https://www.django-rest-framework.org/api-guide/serializers/#saving-instances
as this is empty due to the fact that the form does not validate.
Is there a builtin solution?
You can achieve it using UpdateAPIview
serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('name', 'author', 'date')
views.py
from rest_framework.generics import UpdateAPIview
from .serializers import BookSerializer
class BookUpdateView(UpdateAPIView):
serializer_class = BookSerializer
urls.py
from django.urls import path
from . import views
url_patterns = [
path('api/book/<int:pk>/update/', views.BookUpdateView.as_view(), name="book_update"),
]
Now, post your data to above url. It should work.
Reference: https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py
I am new to both Python and Django and I would appreciate some guidance with a problem I'm having with Django REST, nested JSON and the serializer.
I wish to post:
{ "Server": [
{
"serverSerialNumber": "0000",
"serverUniqueKey": "2222"
},
{
"serverSerialNumber": "0001",
"serverUniqueKey": "2223"
}
]
}
This is my serializer:
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import Api
class ApiSerializer(serializers.ModelSerializer):
"""Serializer to map the Model instance into JSON format."""
class Meta:
"""Meta class to map serializer's fields with the model fields."""
model = Api
fields = ('id', 'serverSerialNumber', 'serverUniqueKey', 'date_created', 'date_modified')
read_only_fields = ('date_created', 'date_modified')
depth = 1
I simply receive the following back:
{
"serverSerialNumber": [
"This field is required."
]
}
So I am not understanding how to use 'depth' or I'm doing something silly.
Adding View per request:
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from rest_framework import generics
from .serializers import ApiSerializer
from .models import Api
class CreateView(generics.ListCreateAPIView):
"""This class defines the create behavior of our rest api."""
queryset = Api.objects.all()
serializer_class = ApiSerializer
def perform_create(self, serializer):
"""Save the post data when creating a new item."""
serializer.save()
Ok, so I've stumbled through some documentation and had another bash at this.
Still not working but the code seems to make more sense, here is the new code and error:
serializers.py
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import Blade, Servers
class BladeSerializer(serializers.ModelSerializer):
class Meta:
model = Blade
fields = ('id', 'serverSerialNumber', 'serverUniqueKey', 'date_created', 'date_modified')
read_only_fields = ('date_created', 'date_modified')
class ServersSerializer(serializers.ModelSerializer):
Server = BladeSerializer(many=True)
class Meta:
model = Servers
fields = ['Server']
def create(self, validated_data):
servers_data = validated_data.pop('Server')
srv = Servers.objects.create(**validated_data)
for server_data in servers_data:
Blade.objects.create(srv=srv, **server_data)
return srv
views.py
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from api.serializers import UserSerializer, GroupSerializer
from rest_framework import generics
from .serializers import BladeSerializer, ServersSerializer
from .models import Blade, Servers
class CreateView(generics.ListCreateAPIView):
queryset = Servers.objects.all()
serializer_class = ServersSerializer
def perform_create(self, serializer):
serializer.save()
models.py
from django.db import models
from inventory.models import Server
class Blade(models.Model):
instanceId = models.CharField(max_length=255, blank=True, unique=False)
chassisUniqueKey = models.CharField(max_length=255, blank=True, unique=False)
serverUniqueKey = models.CharField(max_length=255, blank=True, unique=False)
serverSerialNumber = models.CharField(max_length=255, blank=False, unique=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return "{}".format(self.name)
class Servers(models.Model):
Servers = models.CharField(max_length=10, blank=True, unique=False)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.name)
The error
Got AttributeError when attempting to get a value for field Server on serializer ServersSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Servers instance.
Original exception text was: 'Servers' object has no attribute 'Server'.
Try this,
class CreateView(generics.ListCreateAPIView):
queryset = Api.objects.all()
serializer_class = ApiSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
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)
def perform_create(self, serializer):
serializer.save()