some parts of product - fix custom pagination - add id to pages list
This commit is contained in:
@@ -66,6 +66,7 @@ INSTALLED_APPS = [
|
|||||||
'apps.warehouse.apps.WarehouseConfig',
|
'apps.warehouse.apps.WarehouseConfig',
|
||||||
'apps.search.apps.SearchConfig',
|
'apps.search.apps.SearchConfig',
|
||||||
'apps.log.apps.LogConfig',
|
'apps.log.apps.LogConfig',
|
||||||
|
'apps.product.apps.ProductConfig',
|
||||||
'rest_captcha',
|
'rest_captcha',
|
||||||
'captcha',
|
'captcha',
|
||||||
'drf_yasg'
|
'drf_yasg'
|
||||||
@@ -155,8 +156,8 @@ REST_FRAMEWORK = {
|
|||||||
'rest_framework.authentication.BasicAuthentication',
|
'rest_framework.authentication.BasicAuthentication',
|
||||||
),
|
),
|
||||||
'EXCEPTION_HANDLER': 'apps.core.error_handler.custom_exception_handler',
|
'EXCEPTION_HANDLER': 'apps.core.error_handler.custom_exception_handler',
|
||||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
|
"DEFAULT_PAGINATION_CLASS": 'apps.core.pagination.CustomPageNumberPagination',
|
||||||
"PAGE_SIZE": 25,
|
"PAGE_SIZE": 20,
|
||||||
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,5 +37,6 @@ urlpatterns = [
|
|||||||
path('livestock/', include('apps.livestock.urls')),
|
path('livestock/', include('apps.livestock.urls')),
|
||||||
path('tag/', include('apps.tag.urls')),
|
path('tag/', include('apps.tag.urls')),
|
||||||
path('search/', include('apps.search.urls')),
|
path('search/', include('apps.search.urls')),
|
||||||
|
path('product/', include('apps.product.urls')),
|
||||||
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class PageSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Page
|
model = Page
|
||||||
fields = [
|
fields = [
|
||||||
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'code'
|
'code'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ def custom_exception_handler(exc, context):
|
|||||||
|
|
||||||
if response is not None:
|
if response is not None:
|
||||||
response.data['status_code'] = response.status_code
|
response.data['status_code'] = response.status_code
|
||||||
|
if response.data.get('detail'):
|
||||||
response.data['message'] = response.data.get('detail', str(exc))
|
response.data['message'] = response.data.get('detail', str(exc))
|
||||||
del response.data['detail']
|
del response.data['detail']
|
||||||
else:
|
else:
|
||||||
|
|||||||
7
apps/core/pagination.py
Normal file
7
apps/core/pagination.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from rest_framework.pagination import PageNumberPagination
|
||||||
|
|
||||||
|
|
||||||
|
class CustomPageNumberPagination(PageNumberPagination):
|
||||||
|
page_size = 20 # default
|
||||||
|
page_size_query_param = 'page_size' # set from client
|
||||||
|
max_page_size = 100 # maximum items to show
|
||||||
0
apps/product/__init__.py
Normal file
0
apps/product/__init__.py
Normal file
3
apps/product/admin.py
Normal file
3
apps/product/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
apps/product/apps.py
Normal file
6
apps/product/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ProductConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.product'
|
||||||
0
apps/product/management/__init__.py
Normal file
0
apps/product/management/__init__.py
Normal file
0
apps/product/management/commands/__init__.py
Normal file
0
apps/product/management/commands/__init__.py
Normal file
1
apps/product/management/commands/command.py
Normal file
1
apps/product/management/commands/command.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Your custom management commands go here.
|
||||||
56
apps/product/migrations/0001_initial.py
Normal file
56
apps/product/migrations/0001_initial.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Generated by Django 5.0 on 2025-06-03 12:19
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ReferenceProduct',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modify_date', models.DateTimeField(auto_now=True)),
|
||||||
|
('creator_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('modifier_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('trash', models.BooleanField(default=False)),
|
||||||
|
('name', models.CharField(default='empty', max_length=250)),
|
||||||
|
('type', models.CharField(choices=[('F', 'Free'), ('G', 'Governmental')], max_length=3)),
|
||||||
|
('img', models.CharField(default='empty', max_length=100)),
|
||||||
|
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Product',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modify_date', models.DateTimeField(auto_now=True)),
|
||||||
|
('creator_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('modifier_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('trash', models.BooleanField(default=False)),
|
||||||
|
('name', models.CharField(default='empty', max_length=250)),
|
||||||
|
('type', models.CharField(choices=[('F', 'Free'), ('G', 'Governmental')], max_length=3)),
|
||||||
|
('img', models.CharField(default='empty', max_length=100)),
|
||||||
|
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('reference', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reference_product', to='product.referenceproduct')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
54
apps/product/migrations/0002_attribute_attributevalue.py
Normal file
54
apps/product/migrations/0002_attribute_attributevalue.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Generated by Django 5.0 on 2025-06-03 13:14
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('product', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Attribute',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modify_date', models.DateTimeField(auto_now=True)),
|
||||||
|
('creator_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('modifier_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('trash', models.BooleanField(default=False)),
|
||||||
|
('name', models.CharField(default='empty', max_length=100)),
|
||||||
|
('type', models.CharField(default='empty', help_text='type of attribute like: calculate product by kilogram', max_length=255)),
|
||||||
|
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('reference_product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reference_attribute', to='product.referenceproduct')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AttributeValue',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modify_date', models.DateTimeField(auto_now=True)),
|
||||||
|
('creator_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('modifier_info', models.CharField(max_length=100, null=True)),
|
||||||
|
('trash', models.BooleanField(default=False)),
|
||||||
|
('value', models.IntegerField(default=0)),
|
||||||
|
('attribute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attribute_value', to='product.attribute')),
|
||||||
|
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_attribute_value', to='product.product')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
apps/product/migrations/__init__.py
Normal file
0
apps/product/migrations/__init__.py
Normal file
0
apps/product/mobile/api/__init__.py
Normal file
0
apps/product/mobile/api/__init__.py
Normal file
0
apps/product/mobile/api/v1/__init__.py
Normal file
0
apps/product/mobile/api/v1/__init__.py
Normal file
0
apps/product/mobile/api/v1/serializers.py
Normal file
0
apps/product/mobile/api/v1/serializers.py
Normal file
0
apps/product/mobile/api/v1/urls.py
Normal file
0
apps/product/mobile/api/v1/urls.py
Normal file
0
apps/product/mobile/api/v1/views.py
Normal file
0
apps/product/mobile/api/v1/views.py
Normal file
0
apps/product/mobile/tests/test_common_services.py
Normal file
0
apps/product/mobile/tests/test_common_services.py
Normal file
94
apps/product/models.py
Normal file
94
apps/product/models.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
from django.db import models
|
||||||
|
from apps.core.models import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
|
||||||
|
class ReferenceProduct(BaseModel):
|
||||||
|
""" Reference product - like: rice """
|
||||||
|
|
||||||
|
name = models.CharField(max_length=250, default='empty') # noqa
|
||||||
|
type_choices = (
|
||||||
|
('F', 'Free'), # free product
|
||||||
|
('G', 'Governmental') # government product
|
||||||
|
)
|
||||||
|
type = models.CharField(max_length=3, choices=type_choices)
|
||||||
|
img = models.CharField(max_length=100, default='empty')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'name: {self.name} - type: {self.type}'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super(ReferenceProduct, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Product(BaseModel):
|
||||||
|
""" Child of reference product - like: brown rice """
|
||||||
|
name = models.CharField(max_length=250, default='empty') # noqa
|
||||||
|
type_choices = (
|
||||||
|
('F', 'Free'), # free product
|
||||||
|
('G', 'Governmental') #
|
||||||
|
)
|
||||||
|
type = models.CharField(max_length=3, choices=type_choices)
|
||||||
|
img = models.CharField(max_length=100, default='empty')
|
||||||
|
reference = models.ForeignKey(
|
||||||
|
ReferenceProduct,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='reference_product',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'name: {self.name} - type: {self.type}'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super(Product, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Attribute(BaseModel):
|
||||||
|
"""
|
||||||
|
every reference product have multiple attributes
|
||||||
|
"""
|
||||||
|
reference_product = models.ForeignKey(
|
||||||
|
ReferenceProduct,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='reference_attribute',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
name = models.CharField(max_length=100, default='empty')
|
||||||
|
type = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
default='empty',
|
||||||
|
help_text='type of attribute like: calculate product by kilogram'
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.reference_product.name} - {self.name}'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(Attribute, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AttributeValue(BaseModel):
|
||||||
|
"""
|
||||||
|
every child product should have attribute value for
|
||||||
|
reference product attribute
|
||||||
|
"""
|
||||||
|
product = models.ForeignKey(
|
||||||
|
Product,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='product_attribute_value',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
attribute = models.ForeignKey(
|
||||||
|
Attribute,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='attribute_value'
|
||||||
|
)
|
||||||
|
value = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.product.name} - {self.attribute.name} - {self.value}'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(AttributeValue, self).save(*args, **kwargs)
|
||||||
0
apps/product/permissions.py
Normal file
0
apps/product/permissions.py
Normal file
0
apps/product/pos/api/__init__.py
Normal file
0
apps/product/pos/api/__init__.py
Normal file
0
apps/product/pos/api/v1/__init__.py
Normal file
0
apps/product/pos/api/v1/__init__.py
Normal file
0
apps/product/pos/api/v1/serializers.py
Normal file
0
apps/product/pos/api/v1/serializers.py
Normal file
0
apps/product/pos/api/v1/urls.py
Normal file
0
apps/product/pos/api/v1/urls.py
Normal file
0
apps/product/pos/api/v1/views.py
Normal file
0
apps/product/pos/api/v1/views.py
Normal file
0
apps/product/pos/tests/test_common_services.py
Normal file
0
apps/product/pos/tests/test_common_services.py
Normal file
0
apps/product/services.py
Normal file
0
apps/product/services.py
Normal file
3
apps/product/tests.py
Normal file
3
apps/product/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
0
apps/product/tools.py
Normal file
0
apps/product/tools.py
Normal file
5
apps/product/urls.py
Normal file
5
apps/product/urls.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('web/api/', include('apps.product.web.api.v1.urls'))
|
||||||
|
]
|
||||||
3
apps/product/views.py
Normal file
3
apps/product/views.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
0
apps/product/web/api/__init__.py
Normal file
0
apps/product/web/api/__init__.py
Normal file
0
apps/product/web/api/v1/__init__.py
Normal file
0
apps/product/web/api/v1/__init__.py
Normal file
93
apps/product/web/api/v1/api.py
Normal file
93
apps/product/web/api/v1/api.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
from apps.product.web.api.v1 import serializers as product_serializers
|
||||||
|
from rest_framework.exceptions import APIException
|
||||||
|
from apps.product import models as product_models
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework import status
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
|
||||||
|
def trash(queryset, pk):
|
||||||
|
""" sent object to trash """
|
||||||
|
obj = queryset.get(id=pk)
|
||||||
|
obj.trash = True
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
|
||||||
|
def delete(queryset, pk):
|
||||||
|
""" full delete object """
|
||||||
|
obj = queryset.get(id=pk)
|
||||||
|
obj.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class ProductViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = product_models.Product.objects.all()
|
||||||
|
serializer_class = product_serializers.ProductSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent product to trash """
|
||||||
|
try:
|
||||||
|
trash(self.queryset, pk)
|
||||||
|
except APIException as e:
|
||||||
|
return Response(e, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['post'],
|
||||||
|
detail=True,
|
||||||
|
url_name='delete',
|
||||||
|
url_path='delete',
|
||||||
|
name='delete'
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def delete(self, request, pk=None):
|
||||||
|
""" Full delete of product object """
|
||||||
|
try:
|
||||||
|
delete(self.queryset, pk)
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
except APIException as e:
|
||||||
|
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class ReferenceProductViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = product_models.ReferenceProduct.objects.all()
|
||||||
|
serializer_class = product_serializers.ReferenceProductSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent product to trash """
|
||||||
|
try:
|
||||||
|
trash(self.queryset, pk)
|
||||||
|
except APIException as e:
|
||||||
|
return Response(e, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['post'],
|
||||||
|
detail=True,
|
||||||
|
url_name='delete',
|
||||||
|
url_path='delete',
|
||||||
|
name='delete'
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def delete(self, request, pk=None):
|
||||||
|
""" Full delete of product object """
|
||||||
|
try:
|
||||||
|
delete(self.queryset, pk)
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
except APIException as e:
|
||||||
|
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
||||||
27
apps/product/web/api/v1/serializers.py
Normal file
27
apps/product/web/api/v1/serializers.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from apps.product import models as product_models
|
||||||
|
|
||||||
|
|
||||||
|
class ReferenceProductSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer of reference product """
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = product_models.ReferenceProduct
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class ProductSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer of product """
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = product_models.Product
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def to_representation(self, instance):
|
||||||
|
""" Custom output of product serializer """
|
||||||
|
|
||||||
|
representation = super().to_representation(instance)
|
||||||
|
if instance.reference:
|
||||||
|
representation['reference'] = ReferenceProductSerializer(instance.reference).data
|
||||||
|
|
||||||
|
return representation
|
||||||
11
apps/product/web/api/v1/urls.py
Normal file
11
apps/product/web/api/v1/urls.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from apps.product.web.api.v1 import api as api_views
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
router.register(r'product', api_views.ProductViewSet, basename='product')
|
||||||
|
router.register(r'reference', api_views.ReferenceProductViewSet, basename='reference')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('v1/', include(router.urls))
|
||||||
|
]
|
||||||
0
apps/product/web/api/v1/views.py
Normal file
0
apps/product/web/api/v1/views.py
Normal file
0
apps/product/web/tests/test_common_services.py
Normal file
0
apps/product/web/tests/test_common_services.py
Normal file
Reference in New Issue
Block a user