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
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
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
``
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.
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.
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)