@daniil-orlovv
Junior Python-developer

Как правильно описать модели для того, чтобы создать несколько подкатегорий для категории?

Как правильно описать 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
        },


админка:
65f9a36317eda227793990.png
65f9a36deaff1639554194.png
бд:
65f9a382a4eb7458378620.png

получение категорий и категорий на 1 уровень ниже(подкатегорий):
65f9a3d260d4f893124621.png

Задача такая: необходимо получать все категории текущего уровня и на 1 уровень ниже и чтобы подкатегории можно было задавать через админку
  • Вопрос задан
  • 69 просмотров
Пригласить эксперта
Ответы на вопрос 1
fox_12
@fox_12 Куратор тега Django
Расставляю биты, управляю заряженными частицами
Примерно как-то так:

class Category(models.Model):
    title = models.TextField()
    category = models.ForeignKey(
        'self', on_delete=models.CASCADE, null=True, blank=True, related_name='subcategories'
    )

    def __str__(self):
        return self.title
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы