2

so what i'm trying to do is add a new product to my data base using django's restapi but a product may contain multiple categories which are related throught a third many to many model and extra pictures which are ForeignKeyed to the product

this is my models.py

class Products(models.Model):
    product_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=35, null=False, unique=True)
    description = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0.)
    main_image = models.FileField(upload_to='shop/images')
    created_on = models.DateTimeField(blank=True, default=datetime.now)

class Category(models.Model):
    category_id = models.AutoField(primary_key=True)
    category = models.CharField(max_length=20, null=True, blank=True)
    created_on = models.DateTimeField(blank=True, default=datetime.now)

    class Meta:
        db_table = 'Category'

class ProductsCategory(models.Model):
    productscategory_id = models.AutoField(primary_key=True)
    category = models.ForeignKey(to=Category, on_delete=models.CASCADE)
    product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
    created_on = models.DateTimeField(blank=True, default=datetime.now)

    class Meta:
        db_table = 'ProductsCategory'

class Pictures(models.Model):
    picture_id = models.AutoField(primary_key=True)
    image = models.FileField(upload_to='shop/images')
    product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
    created_on = models.DateTimeField(blank=True, default=datetime.now)

    class Meta:
        db_table = 'Pictures'

and heres what i've tryed:

@api_view(['POST'])
@permission_classes([IsModerator])
def create_product(request):
    product_details = ProductsSerializer(request.POST, request.FILES)
    pictures = PicturesSerializer(request.POST, request.FILES, many=True)
    category_list = request.POST.getlist("category")

            if product_details.is_valid() and validate_file_extension(request.FILES.get("main_image")):
            try:
                product = product_details.save()
                if len(category_list) > 0:
                    for i in category_list:
                        category = Category.objects.get(category=i)
                        ProductsCategory.objects.create(category=category, product=product)
                if pictures:
                    for image in request.FILES.getlist("image"):
                        if validate_file_extension(image):
                            Pictures.objects.create(image=image, product=product)
                        else:
                            error = {"error": "invalid extra pictures extension"}
                            return Response(error)
                return Response((product_details.data, pictures.data, category_list), status=status.HTTP_201_CREATED)
            except Exception as e:
                return Response(e)
        else:
            return Response((product_details._errors, pictures._errors), status=status.HTTP_400_BAD_REQUEST)

and the output: result

how am i supposed to use this content input? or if you know a better for my main question of saving multiple models in the database and their relationships please leave an answer, thanks in advance

1 Answer 1

1

I suggest you change your models.py structure to this:

from django.db import models

class Category(models.Model):
    category = models.CharField(max_length=20, null=True, blank=True)
    created_on = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name_plural  =  "Categories"

class Picture(models.Model):
    image = models.FileField(upload_to='shop/images')
    product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
    created_on = models.DateTimeField(blank=True, default=datetime.now)
    
class Product(models.Model):
    name = models.CharField(max_length=35, null=False, unique=True)
    description = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0.)
    main_image = models.FileField(upload_to='shop/images')
    more_images = models.ManyToManyField(Pictures, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now=True)

Then in your serializer.py add:

from rest_framework import serializers
from .models import Category, Picture, Product

class CategorySerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Category
        fields = "__all__"

class PictureSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Picture
        fields = "__all__"

class ProductSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Product
        fields = "__all__"

In your views, I suggest you use ViewSets: views.py

from .models import Category, Picture, Product
from .serializer import CategorySerializer, PictureSerializer, ProductSerializer
from rest_framework import viewsets
# import custom permissions if any

class CategoryViewSet(viewsets.ModelViewSet):
    
    serializer_class = CategorySerializer
    queryset = Category.objects.all()

class PictureViewSet(viewsets.ModelViewSet):
    
    serializer_class = PictureSerializer
    queryset = Picture.objects.all()

class ProductViewSet(viewsets.ModelViewSet):
    
    serializer_class = ProductSerializer
    queryset = Product.objects.all()
    permission_classes = [IsModerator]

In your app's urls.py, add the router for your viewsets and it will create the paths for your views automatically:

from django.urls import path
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'category', views.CategoryViewSet, basename='category')
router.register(r'picture', views.PictureViewSet, basename='picture')
router.register(r'product', views.ProductViewSet, basename='product')

urlpatterns = [
    path('', include(router.urls)),
]

Changes log:

  • You do not need to add an ID field to every model, Django does that for you. Unless it's a particular case.
  • Your database tables are named after your model by default. So no need to specify that too.
  • I simplified your models' structure to make it cleaner. But it still does what you want it to do.
  • Django adds an s to create a plural name for every model. So you can name it in singular form unless needed to specify. eg. categories.
  • The viewsets will reduce your work by providing you with listing and retrieval actions.

To access a specific instance of eg. a product, you will just add a /<product id> after the product listing and creation endpoint.

Note: You have to add the id without the brackets.

I also suggest you go through this DRF tutorial. It will improve your understanding of Django REST framework.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.