Отображение подкатегорий Django в категории и товарах в каждой категории json как Json Child - PullRequest
0 голосов
/ 03 июня 2018

Привет в моем проекте Django oscar, который реализует Django oscar.Я могу реализовать свой пользовательский API, который я использую для просмотра категорий и их отображения.Проблема с API сейчас заключается в том, что подкатегории категорий отображаются в моем представлении API как категории, и я хотел бы, чтобы они были в массиве, указывающем, что они являются подкатегориями.Код моей категории следующий:

класс сериализатора customapi

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ('id', 'numchild', 'name', 'description', 'image', 'slug')

Просмотров

class CategoryList(generics.ListAPIView):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer


class CategoryDetail(generics.RetrieveAPIView):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer

customapi/urls.py

url(r'^caty/$', CategoryList.as_view(), name='category-list'),
url(r'^caty/(?P<category_slug>[\w-]+(/[\w-]+)*)_(?P<pk>\d+)/$',
        CategoryDetail.as_view(), name='category'),

Json

[
    {
        "id": 2,
        "path": "0001",
        "depth": 1,
        "numchild": 4,
        "name": "Clothes",
        "description": "<p>Beautiful Clothes</p>",
        "image": null,
        "slug": "clothes"
    },
    {
        "id": 8,
        "path": "00010001",
        "depth": 2,
        "numchild": 0,
        "name": "c",
        "description": "",
        "image": null,
        "slug": "c"
    },
    {
        "id": 7,
        "path": "00010002",
        "depth": 2,
        "numchild": 0,
        "name": "b",
        "description": "",
        "image": null,
        "slug": "b"
    },
    {
        "id": 6,
        "path": "00010003",
        "depth": 2,
        "numchild": 0,
        "name": "a",
        "description": "",
        "image": null,
        "slug": "a"
    },
    {
        "id": 5,
        "path": "00010004",
        "depth": 2,
        "numchild": 0,
        "name": "MsWears",
        "description": "",
        "image": null,
        "slug": "mswears"
    },]

обратите внимание, что numchild равно 4 для первого, что означает, что это родительская категория, а остальныеявляются подкатегориями.

Подкатегории отображаются так из модели Джанго-Оскара

class AbstractCategory(MP_Node):
    """
    A product category. Merely used for navigational purposes; has no effects on business logic.

    Uses Django-treebeard.
    """
    name = models.CharField(_('Name'), max_length=255, db_index=True)
    description = models.TextField(_('Description'), blank=True)
    image = models.ImageField(_('Image'), upload_to='categories', blank=True,
                              null=True, max_length=255)
    slug = SlugField(_('Slug'), max_length=255, db_index=True)

    _slug_separator = '/'
    _full_name_separator = ' > '

    def __str__(self):
        return self.full_name

    @property
    def full_name(self):
        """
        Returns a string representation of the category and it's ancestors,
        e.g. 'Books > Non-fiction > Essential programming'.

        It's rarely used in Oscar's codebase, but used to be stored as a
        CharField and is hence kept for backward compatibility. It's also sufficiently useful to keep around.
        """
        names = [category.name for category in self.get_ancestors_and_self()]
        return self._full_name_separator.join(names)

    @property
    def full_slug(self):
        """
        Returns a string of this category's slug concatenated with the slugs
        of it's ancestors, e.g. 'books/non-fiction/essential-programming'.

        Oscar used to store this as in the 'slug' model field, but this field
        has been re-purposed to only store this category's slug and to not
        include it's ancestors' slugs.
        """
        slugs = [category.slug for category in self.get_ancestors_and_self()]
        return self._slug_separator.join(slugs)

    def generate_slug(self):
        """
        Generates a slug for a category. This makes no attempt at generating a unique slug.
        """
        return slugify(self.name)

    def ensure_slug_uniqueness(self):
        """
        Ensures that the category's slug is unique amongst its siblings.
        This is inefficient and probably not thread-safe.
        """
        unique_slug = self.slug
        siblings = self.get_siblings().exclude(pk=self.pk)
        next_num = 2
        while siblings.filter(slug=unique_slug).exists():
            unique_slug = '{slug}_{end}'.format(slug=self.slug, end=next_num)
            next_num += 1

        if unique_slug != self.slug:
            self.slug = unique_slug
            self.save()

    def save(self, *args, **kwargs):
        """
        Oscar traditionally auto-generated slugs from names. As that is often convenient, we still do so if a slug is not supplied through other means. If you want to control slug creation, just create instances with a slug already set, or expose a field on the appropriate forms.
        """
        if self.slug:
            # Slug was supplied. Hands off!
            super(AbstractCategory, self).save(*args, **kwargs)
        else:
            self.slug = self.generate_slug()
            super(AbstractCategory, self).save(*args, **kwargs)
            # We auto-generated a slug, so we need to make sure that it's
            # unique. As we need to be able to inspect the category's siblings
            # for that, we need to wait until the instance is saved. We
            # update the slug and save again if necessary.
            self.ensure_slug_uniqueness()

    def get_ancestors_and_self(self):
        """
        Gets ancestors and includes itself. Use treebeard's get_ancestors
        if you don't want to include the category itself. It's a separate function as it's commonly used in templates.
        """
        return list(self.get_ancestors()) + [self]

    def get_descendants_and_self(self):
        """
        Gets descendants and includes itself. Use treebeard's get_descendants
        if you don't want to include the category itself. It's a separate function as it's commonly used in templates.
        """
        return list(self.get_descendants()) + [self]

    def get_absolute_url(self):
        """
        Our URL scheme means we have to look up the category's ancestors. As that is a bit more expensive, we cache the generated URL. That is
        safe even for a stale cache, as the default implementation of
        ProductCategoryView does the lookup via primary key anyway. But if you change that logic, you'll have to reconsider the caching approach.
        """
        current_locale = get_language()
        cache_key = 'CATEGORY_URL_%s_%s' % (current_locale, self.pk)
        url = cache.get(cache_key)
        if not url:
            url = reverse(
                'catalogue:category',
                kwargs={'category_slug': self.full_slug, 'pk': self.pk})
            cache.set(cache_key, url)
        return url

    class Meta:
        abstract = True
        app_label = 'catalogue'
        ordering = ['path']
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')

    def has_children(self):
        return self.get_num_children() > 0

    def get_num_children(self):
        return self.get_children().count() 

, когда категория выбрана, соответствующий JSON выглядит так

 {
        "url": "http://127.0.0.1:8000/nativapi/products/16/",
        "id": 16,
        "title": "Deall",
        "images": [],
        "price": {
            "currency": "NGN",
            "excl_tax": "1000.00",
            "incl_tax": "1000.00",
            "tax": "0.00"
        },
        "availability": "http://127.0.0.1:8000/nativapi/products/16/availability/"
    },
    {
        "url": "http://127.0.0.1:8000/nativapi/products/13/",
        "id": 13,
        "title": "ada",
        "images": [
            {
                "id": 8,
                "original": "http://127.0.0.1:8000/media/images/products/2018/05/f3.jpg",
                "caption": "",
                "display_order": 0,
                "date_created": "2018-05-26T17:24:34.762848Z",
                "product": 13
            },]

это означает, что возвращаются только товары этой категории.и если у категории есть номер, если ребенок, номер ребенка должен быть возвращен как массив объектов.

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

django-oscar использует django-treebeard для реализации материализованного пути , в значительной степени противоположность вложенной иерархии, которую вы хотите получить.

У меня нет опыта в написании сериализаторов вместе сДревобород, но я уверен, что вам нужно будет переписать ваш сериализатор на что-то вроде

# Get all categories from /caty
class CategorySerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField('get_children')

    def get_children(self, obj):
        if obj.numchild == 0:
            return None
        # Use treebeards built-in tree generation
        [CategorySerializer(child) for child in Category.get_tree(obj)]

    class Meta:
        model = Category

Обратите внимание, что я НЕ проверял ничего из этого, я просто пытаюсь указать вам направление, которое может привестиВы ближе к решению.

0 голосов
/ 08 июня 2018

Я бы предложил хранить отдельные данные по категориям отдельно (на странице подробностей) и иметь только API продуктов.

Для получения продуктов определенной категории вы можете сделать что-то вроде -

views.py

from django.shortcuts import get_object_or_404
from oscar.core.loading import get_model
from rest_framework import generics
from oscarapi.serializers import ProductsSerializer


Category = get_model('catalogue', 'Category')
Product = get_model('catalogue', 'Product')


class CategoryProductsView(generics.ListAPIView):
    serializer_class = ProductsSerializer

    def get_queryset(self):
        cat_id = self.kwargs.get('pk', None)
        if cat_id is not None:
            category = get_object_or_404(Category, id=cat_id)
            return Product.objects.filter(
                categories__path__startswith=category.path).all()
        else:
            return Product.objects.none()

urls.py

from views import CategoryProductsView

urlpatterns = [
    ...
    url(r'^caty/(?P<pk>[0-9]+)/products/$', CategoryProducts.as_view(), name='category-products'),
    ...
]

Поскольку мы используем categories__path__startswith, мы получим все продукты под этимКатегория, в том числе в подкатегории данной категории и т. д.

Обновление

Что касается подкатегорий, которые вы хотите перечислить, вы можете просто добавить SerializerMethodField()сделать это для вас.Я бы предложил получить список идентификаторов для подкатегорий, чтобы облегчить дальнейшую выборку деталей этой подкатегории, учитывая ее идентификатор (простой поиск из существующего списка категорий)

serializers.py

from oscarapi.utils import OscarModelSerializer
from rest_framework import serializers


class CategorySerializer(OscarModelSerializer):
    subcategories = serializers.SerializerMethodField()

    class Meta:
        model = Category
        fields = ('id', 'numchild', 'name', 'description', 'image', 'slug',
                  'path', 'depth', 'subcategories')

    def get_subcategories(self, obj):
        return Category.objects.filter(path__startswith=obj.path,
                                       depth=obj.depth+1
                              ).values_list('id', flat=True)

пример вывода

"results": [
    {
        "id": 1,
        "numchild": 1,
        "name": "Cat1",
        "description": "",
        "image": "http://localhost:8001/media/categories/images/categories/cat1.jpg",
        "slug": "cat1",
        "path": "0001",
        "depth": 1,
        "subcategories": [
            2
        ]
    },
    {
        "id": 2,
        "numchild": 0,
        "name": "SubCat1",
        "description": "",
        "image": null,
        "slug": "subcat1",
        "path": "00010001",
        "depth": 2,
        "subcategories": [
        ]
    },
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...