Reputation: 21
I understand that my question is repeated often, but I'm stuck with this. I want to make a simple API with DRF. I have two models: models.py
class Rubrics(models.Model):
id = models.AutoField(primary_key=True)
rubric = models.CharField(max_length=255, blank=True, null=True)
class Books(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255, blank=True, null=True)
author = models.CharField(max_length=255, blank=True, null=True)
date = models.CharField(max_length=255, blank=True, null=True)
rubrics = models.ForeignKey('Rubrics', on_delete=models.DO_NOTHING, related_name='books', blank=True, null=True)
I'd like to view serialized results like this:
[
rubric1: [
{
title: "title1",
author:"author1"
},
book_obj2,
so on
],
rubric2: [
book_obj4,
book_obj5
]
]
views.py
class BooksByRubricView(APIView):
"""List of books by rubrics"""
def get(self, request):
last_date = Books.objects.latest("date").date
books_last = Books.objects.filter(date=last_date)
serializer = RubricsSerializer(books_last, many=True)
return Response(serializer.data)
I try a lot of examples in this theme:
#sorry this garbage
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Books
#fields = ("title",)
exclude = ()
class RubricsSerializer(serializers.ModelSerializer):
rubrics = BookSerializer(read_only=True)
class Meta:
model = Rubrics
fields = ("rubrics",)
#exclude = ()
"""
class RubricSerializer(serializers.ModelSerializer):
#rubrics = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
#books = RecursiveSerializer(many=True)
#print(books)
rubrics = RubricSerializer(read_only=True)
#books = BooksListSerializer(many=True)
class Meta:
model = Books
fields = ("title", "rubrics",)
#fields = ['rubric', 'rubrics']
#fields = ['books', 'rubrics']
"""
But maybe I don't understand the principles of reverse relationships and serializing in DRF. Please, tell me WAIDW. Thanks.
UPD.
I want to operate only on specified sets of data (for example, work with the last uploaded books), not on the whole table Books
. So I want to view only rubrics, that are in this set, not all rubrics from table Rubrics. Yes, books have only one rubric id in a specified field, and the rubric
has a one-to-many relationship to books.
Well, that's JSON I want to see (of course, all characters appearing in this work, are fictitious. Any resemblance to actual persons, living or dead, is purely coincidental.):
{
"rubric1": [{
"author": "Marley W. Watkins",
"title": "A Step-By-Step Guide to Exploratory Factor Analysis with Stata"
}, {
"author": "Robert E. Davis",
"title": "Auditing Information and Cyber Security Governance; A Controls-Based Approach"
}, {
"author": "Gerhard X. Ritter, Gonzalo Urcid",
"title": "Introduction to Lattice Algebra: With Applications in AI, Pattern Recognition, Image Analysis, and Biomimetic Neural Networks"
}],
"rubric2": [{
"author": "Richard Cross and JT Paasch",
"title": "The Routledge Companion to Medieval Philosophy"
}, {
"author": "Nicholas Allott (editor), Terje Lohndal (editor), Georges Rey (editor)",
"title": "A Companion to Chomsky"
}, {
"author": "Olakunle George",
"title": "A Companion to African Literatures"
}, {
"author": "Tim Byrnes, Ebubechukwu O. Ilo-Okeke",
"title": "Quantum Atom Optics: Theory and Applications to Quantum Technology"
}],
"rubric3": [{
"author": "Hiroyoshi Naito",
"title": "Organic Semiconductors for Optoelectronics"
}, {
"author": "Bassem R. Mahafza, Scott C. Winton",
"title": "Handbook of Radar Signal Analysis"
}, {
"author": "Sean McManus, Mike Cook",
"title": "Raspberry Pi For Dummies, 4th Edition"
}]
}
I realize it in plain
Django:
class BooksByRubricView(APIView):
"""List of books by rubrics"""
def get(self, request):
last_date = Books.objects.using('books').latest("date").date
books_last = Books.objects.using('books').filter(date=last_date)
categories = []
res_dict = {}
for item in books_last:
categories.append(item.rubrics_id)
categories = set(categories)
for item in categories:
temp_list = []
temp_qs = books_last.filter(rubrics_id=item)
for i in temp_qs:
temp_list.append({"author": i["author"], "title": i["title"]})
res_dict["rubric"+str(item)]=list(temp_list)
# res = json.dumps(res_dict)
return JsonResponse(res_dict, safe=False, json_dumps_params={'ensure_ascii': False})
can I realize it with DRF
serializers or simplest way is not f*cking any brains and return JSON as above?
Update2:
Well, after some magic with serializers and generic.APIView
I've got a result not exactly expected, but very closest to that. Example (of course, all characters appearing in this work, are fictitious. Any resemblance to real persons, living or dead, is purely coincidental)
views.py
class BooksByRubricView2(generics.ListAPIView):
"""Books grouped by rubrics"""
serializer_class = RubricsSerializer2
queryset = Rubrics.objects.all()
#OR without generic
class BooksByRubricView3(APIView):
def get(self, request):
r = Rubrics.objects.all()
serializer=RubricsSerializer2(r,many=True)
return Response(serializer.data)
serializers.py
class FilteredListSerializer(serializers.ListSerializer):
"""Serializer to filter Book table, look for latest date for every rubric"""
def to_representation(self, data):
latest_data = data.latest("date").date
data = data.filter(date=latest_data)
return super(FilteredListSerializer, self).to_representation(data)
class BookSerializer2(serializers.ModelSerializer):
class Meta:
model = Books
list_serializer_class = FilteredListSerializer
fields = (
"title",
"author",
"date")
class RubricsSerializer2(serializers.ModelSerializer):
books = BookSerializer2(many=True, read_only=True)
class Meta:
model = Rubrics
fields = ("rubric", "books",)
result:
[
{
"rubric": "Computers",
"books": [
{
"title": "A Step-By-Step Guide to Exploratory Factor Analysis with Stata",
"author": "Marley W. Watkins",
"date": "2021-08-08"
},
{
"title": "Auditing Information and Cyber Security Governance; A Controls-Based Approach",
"author": "Robert E. Davis",
"date": "2021-08-08"
}
]
},
{
"rubric": "Education",
"books": [
{
"title": "The Routledge Companion to Medieval Philosophy",
"author": "Richard Cross and JT Paasch",
"date": "2021-08-08"
}
]
},
so on
}
It's a dirty way because every Rubric
from table Rubrics
creates its own query to table Books
and each Rubric
has its latest date. But DRF filtering will be the next step.
Upvotes: 2
Views: 1233
Reputation: 656
First i've tried to get the result that you wanted using your view and just dit not worked! So I've created another view:
from .serializers import *
from rest_framework import generics
from .models import *
class BooksByRubricView(generics.ListAPIView):
serializer_class = RubricsSerializer
queryset = Rubric.objects.all()
# You should try!
And course I also had to create a path (Just for tests! Ignore it!):
from django.urls import path
from .views import BooksByRubricView
urlpatterns = [
path('books/', BooksByRubricView.as_view(), name='books')
]
In your models:
from django.db import models
class Rubric(models.Model):
id = models.AutoField(primary_key=True) # Do not use plural in the models name!
rubric_name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
ordering = ['id'] # Create a ordering!
def __str__(self):
return self.rubric_name
class Book(models.Model):
rubric = models.ForeignKey(Rubric, related_name='rubrics', on_delete=models.CASCADE)
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255, blank=True, null=True)
author = models.CharField(max_length=255, blank=True, null=True)
date = models.CharField(max_length=255, blank=True, null=True)
class Meta:
ordering = ['id'] # Create a ordering!
In your serializers.py:
from rest_framework import serializers
from .models import *
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = (
"title",
"author",
"date"
)
class RubricsSerializer(serializers.ModelSerializer):
rubrics = BookSerializer(many=True, read_only=True)
class Meta:
model = Rubric
fields = (
"id", # Put an Id if you want!
"rubrics",
)
Maybe the only problem was with your view! So I just did all this to get the result that you want but localy; images of the result:
Upvotes: 1
Reputation: 816
There is no field rubrics
in Rubrics
model, available fields are:
id
,rubric
from model itself,books_set
which represent all books that have FK reference to that Rubrics
instance. Take a look at this official docsBook have one rubric (rubrics foreign key), and rubrics have multiple books (books_set). I would also suggest changing FK name from rubrics
to rubric
since there can be only one.
Try this and work from there:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Books
#fields = ("title",)
exclude = ()
class RubricsSerializer(serializers.ModelSerializer):
class Meta:
model = Rubrics
fields = ("books_set",)
#exclude = ()
Upvotes: 1