I have products defined by their Name and Location. Each product has a unique pair of Name/Location.
I'm writing a view to be able to create a product and I would like to first check if it exists in DB. If yes then keep the ID somewhere to return it to my front app. If no then create it and get the ID also.
From my research, overriding the perform_create method should be the solution but I can't figure out how to.
Any help would be appreciated.
urls.py
from django.conf.urls import url
from main.views import product_view
urlpatterns = [
url(r'^products/$', product_view.ProductCreate.as_view()),
url(r'^products/(?P<pk>[0-9]+)/$', product_view.ProductDetail.as_view()),
]
product_view.py
from rest_framework import generics
from rest_framework import permissions
from main.models import Product
from main.serializers import ProductSerializer
class ProductCreate(generics.CreateAPIView):
"""
Create a new product.
"""
permission_classes = (permissions.IsAuthenticated,)
serializer_class = ProductSerializer
queryset = Product.objects.all()
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = create_product(product_name, product_location)
else:
product = product_list[0]
serializer = ProductSerializer(product)
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ProductDetail(generics.RetrieveUpdateAPIView):
"""
Retrieve, update or delete a product.
"""
permission_classes = (permissions.IsAuthenticated,)
serializer_class = ProductSerializer
queryset = Product.objects.all()
serializer.py
from django.contrib.auth.models import User
from rest_framework import serializers
from main.models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id',
'product_name',
'product_shop',
'product_url',
'product_img',
'product_location')
EDIT
product model :
class Product(models.Model):
product_name = models.CharField(max_length=200, blank=True)
product_shop = models.CharField(max_length=200, blank=True)
product_url = models.CharField(max_length=400, blank=False)
product_img = models.CharField(max_length=400, blank=True)
product_location = models.CharField(max_length=200, blank=False)
product_creation_date = models.DateTimeField(default=datetime.now, blank=True)
productgroup = models.ForeignKey(ProductGroup, blank=True, null=True, on_delete=models.CASCADE)
def __str__(self):
return '#' + str(self.pk) + ' ' + self.product_name + ' (' + self.product_shop + ')'
A product is automatically created depending on its name and location. A specific function is handling the creation and fulfill the data.
The result I get in my front app is missing some data using this code. Here is an example using httpie :
Request : http POST http://127.0.0.1:8000/products/ product_name="Product test" product_location="Loc1" product_img="www.myimg.com"
Result : HTTP/1.0 201 Created Allow: POST, OPTIONS Content-Length: 247 Content-Type: application/json Date: Thu, 08 Mar 2018 13:58:18 GMT Server: WSGIServer/0.2 CPython/3.5.3 Vary: Accept X-Frame-Options: SAMEORIGIN
{ "product_location": "Loc1", "product_name": "Product test", "product_img": "www.myimg.com" }
In DB the product exists and has values for product_shop and product_url, and of course has an ID.
EDIT 2
I did some more test and logged as many things as possible.
Here is my perform_create function and the results from the logger :
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = create_product(product_name, product_location)
else:
product = product_list[0]
logger.info('product id : ' + str(product.id)) # Generated automatically
logger.info('product name : ' + product.product_name) # From the request
logger.info('product url : ' + product.product_url) # Generated by my create_product function
serializer = ProductSerializer(product)
logger.info('serializer.data['id'] : ' + str(serializer.data['id']))
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Here are the results and they are good :
product id : 3713
product name : Product 1
product url : /products/Product1/...
serializer.data['id'] : 3713
In the result of the request I now have only the product_url and the product_location....
Is there any other way to achieve what I want?
You need to check is serializer is valid at first. Then you can call serializer.save() to create new object, or just create new serializer object and pass to it already existing product:
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = serializer.save()
else:
product = product_list[0]
serializer = ProductSerializer(product)
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
If serializer's data is not valid you have to return serializer.errors
.
Ok so I solved my problem NOT using perform_create. I came back to an easy APIView and defined the POST method as wanted. Works perfectly now.
class ProductCreate(APIView):
"""
Create a new product.
"""
permission_classes = (permissions.IsAuthenticated,)
def post(cls, request, format=None):
serializer = ProductSerializer(data=request.data)
if serializer.is_valid():
............
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With