Django Admin Shows Instance Created But Cannot See It In DRF View - django

I am following a course on DRF. It basically is creating a Q&A site. Now I tried answering a Question using the DRF interface.
But when I look at the question, it still shows answer count as zero.
But the answwers are visible on the Django Admin Panel.
My code for the same is:
models.py file
class Question(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
content = models.CharField(max_length=240)
slug = models.SlugField(max_length=240, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="questions")
def __str__(self):
return self.content
class Answer(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
body = models.TextField()
question = models.ForeignKey(Question,
on_delete=models.CASCADE,
related_name="answers", null=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
voters = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='votes')
def __str__(self):
return f'{self.body} by {self.author.username}'
The serializers.py file:
from rest_framework import serializers
from ..models import (Answer, Question)
class AnswerSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True)
created_at = serializers.SerializerMethodField(read_only=True)
likes_count = serializers.SerializerMethodField(read_only=True)
user_has_voted = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Answer
exclude = ['question', 'voters', 'updated_at']
def get_created_at(self, instance):
return instance.created_at.strftime("%B %d %y")
def get_likes_count(self, instance):
return instance.voters.count()
def get_user_has_voted(self, instance):
request = self.context.get('request')
return instance.voters.filter(pk=request.user.pk).exists()
class QuestionSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True)
created_at = serializers.SerializerMethodField(read_only=True)
slug = serializers.SlugField(read_only=True)
answers_count = serializers.SerializerMethodField(read_only=True)
user_has_answered = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Question
exclude = ['updated_at']
def get_created_at(self, instance):
return instance.created_at.strftime("%B %d %y")
def get_answers_count(self, instance):
return instance.answers.count()
def get_user_has_answered(self, instance):
request = self.context.get('request')
return instance.answers.filter(author=request.user.pk).exists()
And the views.py file
from rest_framework import generics, viewsets
from rest_framework.generics import get_object_or_404
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from ..models import Question, Answer
from .serializers import QuestionSerializer, AnswerSerializer
from .permissions import IsAuthorOrReadOnly
class QuestionViewSet(viewsets.ModelViewSet):
queryset = Question.objects.all()
lookup_field = 'slug'
serializer_class = QuestionSerializer
permissions_class = [IsAuthorOrReadOnly, IsAuthenticated]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class AnswerCreateAPIView(generics.CreateAPIView):
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
request_user = self.request.user
kwarg_slug = self.kwargs.get("slug")
question = get_object_or_404(Question, slug=kwarg_slug)
if question.answers.filter(author=request_user).exists():
raise ValidationError('You Have Already Answered')
serializer.save(author=self.request.user)

You need provide question for the creation of answer explicitly if you exlcude product in your AnswerSerializer
class AnswerCreateAPIView(generics.CreateAPIView):
...
def perform_create(self, serializer):
...
serializer.save(author=self.request.user, question=question)

Related

Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x000001D4CBCD96A0>": "Album.owner" must be a "User" instance

I don't know why I have this error, I didn't have it befor, I'm sure that I'm logged in before trying call the api, please find below the code :
views.py
class AlbumCreate(generics.CreateAPIView):
serializer_class = AlbumsSerializer
def perform_create(self, serializer):
owner2 = self.request.user
serializer.save(owner=owner2)
class AlbumList(generics.ListAPIView):
permission_classes = [IsAuthenticated]
queryset = Album.objects.all()
serializer_class = AlbumsSerializer
class AlbumDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Album.objects.all()
serializer_class = AlbumsSerializer
Serializer
class AlbumsSerializer(serializers.ModelSerializer):
owner = serializers.StringRelatedField(read_only=True)
class Meta:
model = Album
fields = "__all__"
models
def upload_path(instance, filname):
return '/'.join(['covers', str(instance.title), filname])
class Album(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
cover = models.IntegerField(default=0)
photos_number = models.IntegerField(default=0)
image = models.ImageField(blank=True, null=True, upload_to=upload_path)
def __str__(self):
return self.title

How to Get All Posts with post_status django rest api?

In my project, How can I get all posts with post_status like- publish, pending, draft, spam.
I want to query with post_status.
Post Model
`
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.CharField(max_length=255, unique= True, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
subtitle = models.CharField(max_length=255, null=True, blank=True)
description = models.TextField(max_length=5555, null=True, blank=True)
image = models.ImageField(blank=True, upload_to=post_image_path)
image_caption = models.CharField(max_length=255, blank=True)
post_status = models.CharField(max_length=255)
comment_status = models.CharField(max_length=255)
post_type = models.CharField(max_length=50)
comment_count = models.IntegerField(null=True, blank=True)
categories = models.ManyToManyField(Category, blank=True)
tags = models.ManyToManyField(Tag, blank=True)
createdAt = models.DateTimeField(auto_now_add=True)
updatedAt = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
`
Serializer.py
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
read_only_fields = ['author']
How to implement in view?
The below snippet should work for your query.
class PostListView(generics.ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
queryset = Post.objects.all()
post_status = self.request.query_params.get('post_status')
if username is not None:
queryset = queryset.filter(post_status=post_status)
return queryset
Snipped adapted from Django Rest Framework - Filtering
views.py
from django.db.models import Q
from .serializers import PostSerializer
from rest_framework.generics import ListAPIView
from .models import Post
class PostListAPIView(ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
queryset = Post.objects.filter(Q(post_status__contains='publish') | Q(post_status__contains='pending') | Q(post_status__contains='draft') | Q(post_status__contains='spam'))
return queryset
``

how to fix django Model instance

error messages
ValueError: Cannot assign "<User: user0#example.com>": "Diagnoses.owner" must be a "Patient" instance
i get the above error when i try to create either a new card or diagnoses.
The card and diagnoses are meant to be an instance of the patient, just like the way a patient has a card and also diagnoses. The patient class has a foregin key referencing the User.
Here is the code
views.py
from django.shortcuts import render
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from .serializers import PatientsSerializer, PatientsCardSerializer, PatientsDiagnosesSerializer
from .models import Patient, Card, Diagnoses
from rest_framework import permissions
from .permissions import IsOwner
# Patient Views
class PatientListAPIView(ListCreateAPIView):
serializer_class = PatientsSerializer
queryset = Patient.objects.all()
permission_classes = (permissions.IsAuthenticated, IsOwner,)
def perform_create(self, serializer):
return serializer.save(owner=self.request.user)
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
class PatientDetailAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = PatientsSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
queryset = Patient.objects.all()
lookup_field = "id"
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated,)
def perform_create(self, serializer):
return serializer.save(owner=self.request.user)
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
class PatientCardDetailAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = PatientsCardSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
queryset = Card.objects.all()
lookup_field = "id"
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
class PatientDiagnosesListAPIView(ListCreateAPIView):
serializer_class = PatientsDiagnosesSerializer
queryset = Diagnoses.objects.all()
permission_classes = (permissions.IsAuthenticated,)
def perform_create(self, serializer):
return serializer.save(owner=self.request.user)
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
class PatientDiagnosesDetailAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = PatientsDiagnosesSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
queryset = Diagnoses.objects.all()
lookup_field = "id"
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
Models.py
class Patient(models.Model):
name = models.CharField(max_length=255, null=True)
country = models.CharField(max_length=255, null=True)
state = models.CharField(max_length=255, null=True)
phone = models.CharField(max_length=255, null=True)
email = models.CharField(max_length=255, null=True)
owner = models.ForeignKey(to=User, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Card(models.Model):
name = models.CharField(max_length=255, null=True)
card_number = models.CharField(max_length=255, null=True)
owner = models.OneToOneField(Patient, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return (self.patient.name)+"'s card"
class Diagnoses(models.Model):
sickness = models.CharField(max_length=255, null=True)
note = models.TextField(max_length=255, null=True)
owner = models.ForeignKey(Patient, null=True, on_delete=models.SET_NULL)
def __str__(self):
return (self.patient.name)+"'s diagnoses"
So far, self.request.user is a User Object. You cannot save it to the API or save to the model because of that. And for clarity, it's not a Patient Object.
Based on your situation, I would queue the Patient object with the User object (if this was a one-way reference object), and put that in the argument and do the save() method.
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated,)
def perform_create(self, serializer):
getPatientFromUser = Patient.objects.get(owner=self.request.user)
# getPatientFromUser returns `Patient` Instance. So you could bind it in `Card` Model.
return serializer.save(owner=getPatientFromUser)
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
Notes:
This is a solution for a one-way reference. (User being referred to as Patient)
If you have a cross-reference relationship between User and Patience (where User and Patience has a ForeignKey on each other), then declare your User Model.
The solution queries the Patient from User. As User is incompatible with that Foreign Object.

Unable to join two tables with django rest framework

I am trying to join two tables and serialize them as an API. I have referred to the docs of the Django rest framework and tried a code. It didn't work. Could not resolve the problem even after trying so many times. I am trying to get a JSON file like
{
'album_name': 'The Grey Album',
'artist': 'Danger Mouse',
'tracks': [
{'order': 1, 'title': 'Public Service Announcement'},
{'order': 2, 'title': 'What More Can I Say'},
{'order': 3, 'title': 'Encore'},
...
],
}
But what I get is
{
'album_name': 'The Grey Album',
'artist': 'Danger Mouse',
}
This is the model file I am using
Model.py
from django.db import models
from django.contrib.auth.models import User
STATUS_CHOICE = (
('simple', 'simple'),
('intermediate', 'intermediate'),
)
class Quiz(models.Model):
quiz_name = models.CharField(max_length=1000)
video_id = models.ForeignKey("youtube.Youtube", on_delete=models.CASCADE)
questions_count = models.IntegerField(default=0)
description = models.CharField(max_length=70, null=True)
created = models.DateTimeField(auto_now_add=True)
slug = models.SlugField()
pass_mark = models.IntegerField()
class Meta:
ordering = ['created']
def __str__(self):
return self.quiz_name
class Category(models.Model):
category = models.CharField(max_length=20, choices=STATUS_CHOICE, default='simple')
quiz_id = models.ForeignKey(Quiz, on_delete=models.CASCADE)
def __str__(self):
return self.category
class Questions(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
question = models.CharField(max_length=1000)
mark = models.IntegerField()
def __str__(self):
return self.question
class Choice(models.Model):
question = models.ForeignKey(Questions, on_delete=models.CASCADE)
choice_1 = models.CharField(max_length=1000)
choice_2 = models.CharField(max_length=1000)
choice_3 = models.CharField(max_length=1000)
choice_4 = models.CharField(max_length=1000)
answer = models.CharField(max_length=1000, default=choice_1)
def __str__(self):
return self.answer
Serializer.py
from rest_framework import serializers
from rest_framework.permissions import IsAuthenticated
from .models import Category, Quiz, Questions, Choice
from django.contrib.auth import authenticate
from django.contrib.auth.hashers import make_password
class QuizSerializer(serializers.ModelSerializer):
class Meta:
model = Quiz
fields = '__all__'
class QuestionsSerializer(serializers.ModelSerializer):
class Meta:
model = Questions
fields = '__all__'
class ChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Choice
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
quiz_name = QuizSerializer(read_only=True)
class Meta:
model = Category
fields = ['id','category','quiz_name']
View.py
from rest_framework import generics, permissions, mixins
from rest_framework.response import Response
from .serializer import CategorySerializer
from .models import Category
class ViewQuiz(generics.ListCreateAPIView):
permission_classes = [
permissions.AllowAny,
]
queryset = Category.objects.all()
serializer_class = CategorySerializer
def list(self, request):
queryset = self.get_queryset()
serializer = CategorySerializer(queryset, many=True)
print(serializer.data)
return Response(serializer.data)
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id','category','quiz_id']
def to_representation(self, instance):
response = super().to_representation(instance)
response['quiz_id'] = QuizSerializer(instance.quiz_id).data
return response
This will produce the result you want, I made an change in how the serializer represent the data. I have some of my serializer doing the same, but my views are working a bit different from yours.
Looks like you are trying to get questions serializes in quiz.
To do that you need to:
1. In Questions model include related_name in quiz field:
class Questions(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name="questions")
question = models.CharField(max_length=1000)
mark = models.IntegerField()
def __str__(self):
return self.question
In QuizSerializer include questions field and set many to True:
class QuizSerializer(serializers.ModelSerializer):
questions = QuestionsSerializer(source="questions", many=True)
class Meta:
model = Quiz
fields = ("questions", ... other needed fields)
Include source attribute in QuizSerializer in CategorySerializer:
class CategorySerializer(serializers.ModelSerializer):
quiz_name = QuizSerializer(read_only=True, source="quiz_id")
class Meta:
model = Category
fields = ['id', 'category', 'quiz_name']
Your Quiz was not serialized because the relation between Category and Quiz in tables are called quiz_id but your field is called quiz_name, so the framework did not know where it should take quiz, because it was looking at quiz_name relation which does not exist.

Django_filter on an apiview

I have been trying to use the Django_filter on an APIView, but it just does not work. I am trying to implement a filter search, on some fields on a model.
below is how the model is set up
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=250)
picture = models.TextField(null=True, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
class Skill(models.Model):
name = models.CharField(max_length=60)
subcategory = models.CharField(max_length=60, blank=True, null=True)
created_on = models.DateTimeField(auto_now=True)
updated_on = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING)
the views.py set up is also as shown below
from django_filters import rest_framework as filters
class UserFilter(filters.FilterSet):
email = filters.CharFilter(lookup_expr='icontains')
name = filters.CharFilter(lookup_expr='icontains')
profiles__skills = filters.CharFilter(lookup_expr='icontains')
class Meta:
model = User
fields = ('email', 'name', 'profiles__skills')
class ListUsersView(APIView, MyPaginationMixin):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
filterset_class = UserFilter
def get(self, request):
page = self.paginate_queryset(self.queryset)
if page is not None:
serializer_context = {"request": request}
serializer = self.serializer_class(page, context=serializer_context, many=True)
return self.get_paginated_response(serializer.data)
and finally my serializer.py
class UserSerializer(serializers.ModelSerializer):
slug = serializers.SlugField(read_only=True)
class Meta:
model = User
fields = ('email', 'name', 'slug', 'picture')
read_only_fields = ('email', 'name', 'slug',)
my urls.py
path('users/', qv.ListUsersView.as_view(), name='list-users'),
this is how my result looks like
please, how can I get the Django filter to work on the APIView
It seems you are trying to get the similar or exact behavior of DRF ListAPIView by using APIView. I would suggest using ListAPIView over APIView in your case.
from rest_framework import generics
from django_filters import rest_framework as filters
class ListUsersView(generics.ListAPIView):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
filterset_class = UserFilter
filter_backends = (filters.backends.DjangoFilterBackend,)
To add filtering capability in APIView,
class MyAPIViewKlass(APIView):
filter_backends = (filters.DjangoFilterBackend,)
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
def get(self, request, *args, **kwargs):
base_qs = MyModel.objects.all()
filtered_qs = self.filter_queryset(base_qs)
serializer = MySerializer(filtered_qs, many=True)
return Response(serializer.data)