Как правильно описать models.py для того, чтобы были категории для книг, а у категорий были подкатегории, которые также можно указать для книг? И чтобы потом в админке можно было вручную выбрать подкатегории для книги, которые относятся к категориям, которые уже указаны у книги?
models.py
from django.db import models
class Subcategory(models.Model):
title = models.TextField()
class Category(models.Model):
title = models.TextField()
subcategories = models.ManyToManyField(
Subcategory,
blank=True
)
def __str__(self):
return self.title
class Author(models.Model):
name = models.TextField()
def __str__(self):
return self.name
class Book(models.Model):
title = models.TextField()
isbn = models.TextField(null=True, blank=True)
pagecount = models.IntegerField(null=True, blank=True)
publisheddate = models.TextField(null=True, blank=True)
thumbnailurl = models.TextField(null=True, blank=True)
shortdescription = models.TextField(null=True, blank=True)
longdescription = models.TextField(null=True, blank=True)
status = models.TextField(blank=True)
authors = models.ManyToManyField(
Author,
blank=True
)
categories = models.ManyToManyField(
Category,
blank=True
)
subcategories = models.ManyToManyField(
Subcategory,
blank=True
)
def __str__(self):
return self.title
class FeedBack(models.Model):
email = models.EmailField(blank=True)
name = models.TextField(blank=True)
comment = models.TextField(blank=True)
phone = models.TextField(blank=True)
def __str__(self):
return self.comment
views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.generics import CreateAPIView, ListAPIView
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from api.models import Book, Category, FeedBack
from api.serializers import BookSerializer, CategorySerializer, FeedBackSerializer
class ListRetrieveBook(ReadOnlyModelViewSet):
"""
Получение всех книг, книг по категории, конкретной книги.
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = (DjangoFilterBackend,)
filterset_fields = ('categories__title',
'title',
'authors',
'status',
'publisheddate',)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.is_detail_request = True
serializer = self.get_serializer(instance)
return Response(serializer.data)
class ListCategory(ListAPIView):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class CreateFeedback(CreateAPIView):
queryset = FeedBack.objects.all()
serializer_class = FeedBackSerializer
serializers.py
from rest_framework import serializers
from api.models import Book, Category, Subcategory, FeedBack, Author
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Subcategory
fields = (
'id',
'title',
)
class CategorySerializer(serializers.ModelSerializer):
subcategories = serializers.SerializerMethodField(
method_name='get_subcategories'
)
class Meta:
model = Category
fields = (
'id',
'title',
'subcategories'
)
def get_subcategories(self, obj):
subcategories = Subcategory.objects.filter(category=obj)
serializer = SubCategorySerializer(subcategories, many=True)
return serializer.data
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = (
'id',
'name'
)
class BookSerializer(serializers.ModelSerializer):
categories = CategorySerializer(
many=True,
read_only=True
)
authors = AuthorSerializer(
many=True,
read_only=True
)
related_books = serializers.SerializerMethodField(
method_name='get_related_books'
)
class Meta:
model = Book
fields = (
'id',
'title',
'isbn',
'pagecount',
'publisheddate',
'thumbnailurl',
'status',
'authors',
'categories',
'related_books'
)
def get_related_books(self, obj):
if hasattr(obj, 'is_detail_request') and obj.is_detail_request:
category = obj.categories.first()
related_books = Book.objects.filter(
categories=category).exclude(
id=obj.id).order_by('-publisheddate')[:5]
serializer = self.__class__(related_books, many=True)
return serializer.data
return None
class FeedBackSerializer(serializers.ModelSerializer):
class Meta:
model = FeedBack
fields = (
'id',
'email',
'name',
'comment',
'phone',
)
urls.py
from django.urls import include, path
from rest_framework.routers import SimpleRouter
from api.views import ListRetrieveBook, ListCategory, CreateFeedback
router = SimpleRouter()
router.register('books', ListRetrieveBook, basename='books')
urlpatterns = [
path('v1/', include(router.urls)),
path('v1/category/', ListCategory.as_view()),
path('v1/feedback/', CreateFeedback.as_view())
]
admin.py
from django.contrib import admin
from .models import Book, Author, Category, Subcategory
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = (
'title',
)
class CategoryInlineAdmin(admin.TabularInline):
model = Book.categories.through
extra = 1
class SubcategoryInlineAdmin(admin.TabularInline):
model = Book.subcategories.through
extra = 1
@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
list_display = (
'name',
)
class AuthorInlineAdmin(admin.TabularInline):
model = Book.authors.through
extra = 1
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
inlines = [
AuthorInlineAdmin,
CategoryInlineAdmin,
SubcategoryInlineAdmin]
list_display = (
'title',
'isbn',
'pagecount',
'publisheddate',
'thumbnailurl',
'shortdescription',
'longdescription',
'status',
)
list_filter = ('categories',
'title',
'status',
'publisheddate',)
@admin.register(Subcategory)
class SubcategoryAdmin(admin.ModelAdmin):
list_display = (
'title',
'category',
)
Результат который получаю сейчас:
{
"id": 1,
"title": "Unlocking Android",
"isbn": "1933988673",
"pagecount": 416,
"publisheddate": "2009-04-01T00:00:00.000-0700",
"thumbnailurl": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson.jpg",
"status": "PUBLISH",
"authors": [
{
"id": 1,
"name": "W. Frank Ableson"
},
{
"id": 2,
"name": "Charlie Collins"
},
{
"id": 3,
"name": "Robi Sen"
}
],
"categories": [
{
"id": 1,
"title": "Open Source",
"subcategories": []
},
{
"id": 2,
"title": "Mobile",
"subcategories": []
}
],
"related_books": null
},
админка:
бд:
получение категорий и категорий на 1 уровень ниже(подкатегорий):
Задача такая: необходимо получать все категории текущего уровня и на 1 уровень ниже и чтобы подкатегории можно было задавать через админку