Search code examples
django-modelsdjango-rest-frameworkdjango-views

KeyFeatures for Product implementation


i've been trying to implement a new feature on a django ecommerce project that i'm working on. i created a separate model in the models.py file just for key features and i linked this model to the product model using the manytomanyfield method. the problem is that i can't get the data to be output at all. when i try printing it, it just outputs None even when the field is populated. is there a better and cleaner way to implement this feature??

i've provided code bits below

views.py file

class ProductDetail(APIView):
    def get_object(self, category_slug, product_slug):
        try:
            return Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
        except Product.DoesNotExist:
            raise Http404
        
    def get(self, request, category_slug, product_slug, format=None):
        product = self.get_object(category_slug, product_slug)
        feature = product.key_features
        features_serializer = KeyFeatureSerializer(feature)
        serializer = ProductSerializer(product)
        print(feature)
        return Response(serializer.data)

models.py file

class KeyFeature(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self) -> str:
        return self.name


class Product(models.Model):
    category = models.ForeignKey(Category, related_name = 'products', on_delete = models.CASCADE)
    name = models.CharField(max_length = 255)
    slug = models.SlugField()
    description = models.TextField(blank=True, null=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    image = models.ImageField(upload_to='uploads/', blank=True, null=True)
    thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
    date_added = models.DateTimeField(auto_now_add=True)
    key_features = models.ManyToManyField(KeyFeature, related_name='products')
    


    class Meta:
        ordering = ('-date_added',)

    def __str__(self) -> str:
        return self.name

serializers.py file

from rest_framework import serializers
from .models import *

# a serializer allows python data to be converted to json form

class KeyFeatureSerializer(serializers.ModelSerializer):
    class Meta:
        model = KeyFeature
        fields = ('id', 'name',) 
class ProductSerializer(serializers.ModelSerializer):
    key_features = KeyFeatureSerializer(many=True, read_only=True)
    class Meta:
        model = Product
        fields = (
            'id',
            'name',
            'get_absolute_url',
            'description',
            'price',
            'get_image',
            'get_thumbnail',
            'key_features'
        )


Solution

  • You are printing the manager not a QuerySet, hence the None, you can print this with:

    from django.shortcuts import get_object_or_404
    
    
    class ProductDetail(APIView):
        def get_object(self):
            return get_object_or_404(
                Product,
                category__slug=self.kwargs['category_slug'],
                slug=self.kwargs['product_slug'],
            )
    
        def get(self, request, category_slug, product_slug, format=None):
            product = self.get_object()
            feature = product.key_features.all()
            features_serializer = KeyFeatureSerializer(feature, many=True)
            serializer = ProductSerializer(product)
            print(feature)
            return Response(serializer.data)

    It also makes not much sense to call the KeyFeatureSerializer itself, since it is a sub-serializer of your ProductSerializer. In fact most of the boilerplate code can be removed by using a RetrieveAPIView [drf-doc]:

    from django.shortcuts import get_object_or_404
    from rest_framework.generics import RetrieveAPIView
    
    
    class ProductDetailView(RetrieveAPIView):
        queryset = Product.objects.all()
        serializer_class = ProductSerializer
    
        def get_object(self):
            return get_object_or_404(
                self.get_queryset(),
                category__slug=self.kwargs['category_slug'],
                slug=self.kwargs['product_slug'],
            )