serializer.py
from rest_framework import serializers
from status.models import Status
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = Status
fields = [
'user',
'content',
'image'
]
def validate(self, data):
content = data.get('content', None)
if content == '':
content = None
image = data.get('image', None)
if content is None and image is None:
raise serializers.ValidationError('Content or image is required.')
return data
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from status.models import Status
from .serializers import StatusSerializer
class StatusListSearchAPIView(APIView):
permission_classes = []
authentication_classes = []
def get(self, request):
qs = Status.objects.all()
serializer = StatusSerializer(qs)
print(serializer)
return Response(serializer.data)
models.py
from django.db import models
from django.conf import settings
def upload_status_image(instance, filename):
return "updates/{user}/{filename}".format(user=instance.user, filename=filename)
class StatusQuerySet(models.QuerySet):
pass
class StatusManager(models.Manager):
def get_queryset(self):
return StatusQuerySet(self.model, using=self._db)
class Status(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, )
content = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to=upload_status_image, null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
objects = StatusManager()
def __str__(self):
return str(self.content)[:50]
class Meta:
verbose_name = 'Status post'
verbose_name_plural = 'Status posts'
error:
AttributeError: Got AttributeError when attempting to get a value for field user on serializer StatusSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the StatusQuerySet instance.
Original exception text was: 'StatusQuerySet' object has no attribute 'user'.
In views.py
Change this line :
serializer = StatusSerializer(qs)
to this line:
serializer = StatusSerializer(qs, many=True)
Related
When I try to retrieve all data from my REST API I get them correctly except from one field, that is the field 'name' from Skill class. Instead of the 'name' I get only its id. Can you help me to resolve this?----------------------------------------------
here is the output
Here is the code:
models.py
from django.db import models
from django.forms import ImageField
from django.utils.text import slugify
class Skill(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Project(models.Model):
title = models.CharField(max_length=200)
sub_title = models.CharField(max_length=200, null=True, blank=True)
front_page = models.ImageField(null=True, blank=True, upload_to="images", default="broken-image.png")
body = models.TextField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
skills = models.ManyToManyField(Skill, null=True)
slug = models.SlugField(null=True, blank=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if self.slug == None:
slug = slugify(self.title)
has_slug = Project.objects.filter(slug=slug).exists()
count = 1
while has_slug:
count += 1
slug = slugify(self.title) + '-' + str(count)
has_slug = Project.objects.filter(slug=slug).exists()
self.slug = slug
super().save(*args, **kwargs)
serializers.py
from rest_framework import serializers
from project.models import Project, Skill
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = '__all__'
views.py
from rest_framework.response import Response
from rest_framework.decorators import api_view
from project.models import Project, Skill
from .serializers import ProjectSerializer
from rest_framework import status
from django.shortcuts import get_object_or_404
#api_view(['GET'])
def getData(request):
project = Project.objects.all()
serializer = ProjectSerializer(project, many=True)
return Response(serializer.data)
#api_view(['POST'])
def create(request):
serializer= ProjectSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
#api_view(['POST'])
def update(request, pk):
item = Project.objects.get(pk=pk)
serializer = ProjectSerializer(instance=item, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
#api_view(['DELETE'])
def delete(request, pk):
item = get_object_or_404(Project, pk=pk)
item.delete()
return Response(status=status.HTTP_202_ACCEPTED)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.getData),
path('create/', views.create),
path('update/<int:pk>/', views.update),
path('<int:pk>/delete/', views.delete),
]
Use a SlugRelatedField [drf-doc] to specify that you want to use the name of the skills, so:
from rest_framework import serializers
from project.models import Project, Skill
class ProjectSerializer(serializers.ModelSerializer):
skills = serializers.SlugRelatedField(
many=True, slug_field='name', queryset=Skill.objects.all()
)
class Meta:
model = Project
fields = '__all__'
The nice thing of such SlugRelatedField is that this can work bidrectional: not only can you serialize data, but you can also use it to specify skills and thus create or update Projects.
You need to provide a SkillSerializer and refer to it in your ProjectSerializer. Also, it is better to specify the fields you want in your serializers explicitly, as __all__ may expose the fields you don't want to expose or cause unwanted database queries.
https://www.django-rest-framework.org/api-guide/relations/#nested-relationships
Problem: I trying to path like this --> path('Image/<str>', views.getImage, name='imageCategory'),
to get image filter by category --> http://127.0.0.1:8000/Image/TV
#-->Model.py
from django.db import models
class Post(models.Model):
Topic = models.CharField(max_length=250, default='')
Desc = models.CharField(max_length=750, default='')
Link = models.TextField(default='')
def __str__(self):
return str(self.Topic)
class Category(models.Model):
categoryName = models.CharField(max_length=50, default='')
def __str__(self):
return str(self.categoryName)
class PostImage(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, null=True, related_name='post_name')
category = models.ForeignKey(
Category, on_delete=models.CASCADE, null=True, related_name='category_name')
images = models.FileField(upload_to='images/')
def __str__(self):
return str(self.post)
#-->Serializer.py
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import Post, PostImage
class BlogSerializer(ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class ImageSerializer(ModelSerializer):
topic_link = serializers.CharField(source='post.Link', read_only=True)
Category = serializers.CharField(source='category', read_only=True)
class Meta:
model = PostImage
fields = '__all__'
#-->view.py
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Post, PostImage
from .Serializer import BlogSerializer, ImageSerializer
# Create your views here.
#api_view(['GET'])
def getNames(request):
Blog = Post.objects.all()
serializer = BlogSerializer(Blog, many=True)
return Response(serializer.data)
#api_view(['GET'])
def getName(request, pk):
Blog = Post.objects.get(id=pk)
serializer = BlogSerializer(Blog, many=False)
return Response(serializer.data)
#my problem part
#api_view(['GET'])
def getImage(request):
image = PostImage.objects.get(Category=request.Category)
serializer = ImageSerializer(image, many=True)
return Response(serializer.data)
#-->urls.py
from django.urls import path
from . import views
urlpatterns = [
path('blogs/', views.getNames, name='blog'),
path('blogs/<pk>', views.getName, name='blog'),
path('Image/', views.getImage, name='image'),
path('Image/<str>', views.getImage, name='imageCategory'),
]
First, you need to set the parameter name in urls.py.
urlpatterns = [
...
path('Image/', views.getImages, name='image'),
path('Image/<str:category>', views.getImage, name='imageCategory'),
]
Here the category should be the categoryName value of the Category model.
Next, in views.py, you need to pass that parameter to the getImage function.
And also you need to add getImages function for the case of no category.
#api_view(['GET'])
def getImage(request, category):
image = PostImage.objects.filter(category__categoryName=category)
serializer = ImageSerializer(image, many=True)
return Response(serializer.data)
#api_view(['GET'])
def getImages(request):
image = PostImage.objects.all()
serializer = ImageSerializer(image, many=True)
return Response(serializer.data)
I'm trying to make my nested serializer to be able to process updating when I'm only updating for the parent model. The default action in drf is that the serializer cant tell if ur child model is trying to updating or create, there's a solution where you just turn off the validation, which I don't think is ideal for the database and iots implication.
I've scour about this topic for like 2 days and I think the ideal approach to this is to override the is_valid method to be able to just return the object.
Idea from :
Django REST Framework ModelSerializer get_or_create functionality
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
class MyModelSerializer(serializers.ModelSerializer):
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Security.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
# Except not finding the object or the data being ambiguous
# for defining it. Then validate the data as usual
return super().is_valid(raise_exception)
else:
# If the object is found add it to the serializer. Then
# validate the data as usual
self.instance = obj
return super().is_valid(raise_exception)
else:
# If the Serializer was instantiated with just an object, and no
# data={something} proceed as usual
return super().is_valid(raise_exception)
class Meta:
model = models.MyModel
The problem is whenever I try to implment I get the error:
Field 'id' expected a number but got [{'id': 18, 'title': 'aDDD', 'body': 'aDDD', 'slug': 'aDDDbabe', 'author': 1, 'question': 25}].
obj = Question.objects.get(**self.initial_data)
This code triggers it, however When I
obj = Question.answers.get(**self.initial_data)
I get 'ReverseManyToOneDescriptor' object has no attribute 'get' .
Since mine is a nested serializer I would have to find another way to make this work. Any advice where I can fix it? :(
serializers.py
from rest_framework import serializers
# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/serializers.py#L839
from rest_framework.fields import ( # NOQA # isort:skip
CreateOnlyDefault, CurrentUserDefault, SkipField, empty
)
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from .models import Question, Answer
class AnswerSerializer(serializers.ModelSerializer):
"""Serialize Answer model"""
class Meta:
model = Answer
fields = ('id','title', 'body', 'slug', 'author', 'question')
# https://stackoverflow.com/questions/57249850/overwriting-nested-serializers-create-method-throws-typeerror-create-got-mul
read_only_fields = ('question',)
lookup_field = 'slug'
# https://stackoverflow.com/questions/55031552/how-to-access-child-entire-record-in-parent-model-in-django-rest-framework
class QuestionSerializer(serializers.ModelSerializer):
"""Serialize Question model"""
#This answer variable and the fields 'answer' you refer to has to be the SAME but
#These can change exp: 'aaa'
answers = AnswerSerializer(read_only=False, many=True,)
class Meta:
model = Question
fields = ('id','title', 'body', 'slug', 'author', 'category', 'answers',)
lookup_field = 'slug'
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Question.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
return print(obj)
else:
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
print(obj)
else:
return super().is_valid(raise_exception)
def create(self, validated_data):
answers_data = validated_data.pop('answers')
question = Question.objects.create(**validated_data)
for answer_data in answers_data:
#The above stackoverflow link at the answer serializers is realted to this
Answer.objects.create(question=question, **answer_data)
return question
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.body = validated_data.get('body', instance.body)
instance.slug = validated_data.get('slug', instance.slug)
instance.author = validated_data.get('author', instance.author)
instance.category = validated_data.get('category', instance.category)
instance.save()
# https://django.cowhite.com/blog/create-and-update-django-rest-framework-nested-serializers/
answers_data = validated_data.pop('answers')
aas = (instance.answers).all()
print("######")
print(aas)
print("######")
print(instance.answers.get().slug)
print("#########################################")
aas2 = list(aas)
print("######")
for f in validated_data:
print(f)
print(validated_data.items())
print(dir(validated_data))
print("######")
for answer_data in answers_data:
aas3 = aas2.pop(0)
aas3.title= answer_data.get('title', aas3.title)
aas3.body= answer_data.get('body', aas3.body)
aas3.slug= answer_data.get('slug', aas3.slug)
aas3.author= answer_data.get('author', aas3.author)
aas3.question= answer_data.get('question', aas3.question)
aas3.save()
return instance
views.py
from django.shortcuts import render
from .models import Question, Answer
from django.conf import settings
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication
from .serializers import QuestionSerializer
#from .permissions import UpdateOwnPrice
from rest_framework.permissions import IsAdminUser
class QuestionViewSet(viewsets.ModelViewSet):
"""CRUD """
serializer_class = QuestionSerializer
queryset = Question.objects.all()
authentication_classes = (TokenAuthentication,)
#permission_classes = (UpdateOwnPrice,)
lookup_field = 'slug'
extra_kwargs = {'slug': {'validators': []},}
models.py
from django.db import models
from django.conf import settings
from django.db.models import Q
from django.utils import timezone
from django.urls import reverse
class Category(models.Model):
name= models.CharField(max_length=100)
def __str__(self):
return self.name
class Question(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'questions')
category= models.ForeignKey(Category, on_delete= models.CASCADE, related_name = 'questions')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
class Answer(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'answers')
question= models.ForeignKey(Question, on_delete= models.CASCADE, related_name = 'answers')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
trying to add a post to DRF which takes the information of USER and TIME automatically, But this is the error which pops up when tried to add a Post.
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length = 200)
description = models.CharField(max_length = 2000)
date_posted = models.DateTimeField(default = timezone.now)
author = models.ForeignKey(User , on_delete = models.CASCADE)
def __str__(self):
return self.title
Views.py:
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
authentication_classes = [TokenAuthentication,SessionAuthentication]
def create(self, request):
obj = Post()
obj.title = request.data['title']
obj.description = request.data['description']
obj.date_posted = request.data['date_posted']
obj.user = request.user
obj.save()
return Response(status=status.HTTP_201_CREATED)
serializer.py:
from rest_framework import serializers
from django.contrib.auth.models import User
from . models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id', 'title','description','date_posted','author')
I am having problems with reproducing example from Julia Elman's book
models.py
class Sprint(models.Model):
name = models.CharField(max_length=100, blank=True, default='')
description = models.TextField(blank=True, default='')
end = models.DateField(unique=True)
def __str__(self):
return self.name or _('Sprint ending %s') % self.end
serializer.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from rest_framework.reverse import reverse
from .models import Sprint, Task
User = get_user_model()
class SprintSerializer(serializers.ModelSerializer):
links = serializers.SerializerMethodField('get_links')
class Meta:
model = Sprint
fields = ('id', 'name', 'description', 'end', 'links',)
def get_links(self, obj):
request = self.context['request']
return {'self': reverse('sprint-detail',kwargs={'pk': obj.pk},request=request),}
views.py
from django.contrib.auth import get_user_model
from rest_framework import authentication, permissions, viewsets
from .models import Sprint,Task
from .serializers import SprintSerializer,TaskSerializer, UserSerializer
User = get_user_model()
class DefaultsMixin(object):
authentication_classes = (authentication.BasicAuthentication,authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
paginate_by = 25
paginate_by_param = 'page_size'
max_paginate_by = 100
class SprintViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Sprint.objects.order_by('end')
serializer_class = SprintSerializer
I try to see repr from shell
from board.serializers import SprintSerializer
>>> s = SprintSerializer()
>>> print (repr(s))
But I have problem
AssertionError: The field 'links' was declared on serializer SprintSerializer, but has not been included in the 'fields' option.
My DRF
print (rest_framework.VERSION)
3.8.2
How to debug this issue?
Yes,what Alexandr Tartanov suggested works fine.We need to pass arguments with source
links = serializers.SerializerMethodField(source='get_links')
Output
print (repr(s))
SprintSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
description = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
end = DateField(validators=[<UniqueValidator(queryset=Sprint.objects.all())>])
links = SerializerMethodField(source='get_links')