InvenTree/InvenTree/part/api.py

356 lines
8.6 KiB
Python
Raw Normal View History

2019-04-27 12:18:07 +00:00
"""
Provides a JSON API for the Part app
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
2018-04-23 11:18:35 +00:00
from django_filters.rest_framework import DjangoFilterBackend
from django.conf import settings
from django.db.models import Sum
from rest_framework import status
from rest_framework.response import Response
from rest_framework import filters
2019-04-13 23:25:46 +00:00
from rest_framework import generics, permissions
2018-05-04 13:54:57 +00:00
from django.conf.urls import url, include
2019-05-09 12:23:56 +00:00
from django.urls import reverse
import os
from .models import Part, PartCategory, BomItem, PartStar
2018-05-02 13:42:57 +00:00
from .serializers import PartSerializer, BomItemSerializer
2018-05-04 13:54:57 +00:00
from .serializers import CategorySerializer
from .serializers import PartStarSerializer
from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool
2019-04-13 23:25:46 +00:00
class PartCategoryTree(TreeSerializer):
title = "Parts"
model = PartCategory
2019-05-09 12:23:56 +00:00
@property
def root_url(self):
return reverse('part-index')
def get_items(self):
return PartCategory.objects.all().prefetch_related('parts', 'children')
2018-05-04 13:54:57 +00:00
class CategoryList(generics.ListCreateAPIView):
2019-04-27 12:18:07 +00:00
""" API endpoint for accessing a list of PartCategory objects.
- GET: Return a list of PartCategory objects
- POST: Create a new PartCategory object
"""
2018-05-04 13:54:57 +00:00
queryset = PartCategory.objects.all()
serializer_class = CategorySerializer
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
2018-05-04 13:54:57 +00:00
filters.OrderingFilter,
]
filter_fields = [
'parent',
]
ordering_fields = [
'name',
]
ordering = 'name'
search_fields = [
'name',
'description',
]
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
2019-04-27 12:18:07 +00:00
""" API endpoint for detail view of a single PartCategory object """
serializer_class = CategorySerializer
queryset = PartCategory.objects.all()
2019-04-28 13:41:47 +00:00
class PartDetail(generics.RetrieveUpdateAPIView):
2019-04-27 12:18:07 +00:00
""" API endpoint for detail view of a single Part object """
queryset = Part.objects.all()
serializer_class = PartSerializer
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
2019-04-13 23:25:46 +00:00
class PartList(generics.ListCreateAPIView):
2019-04-27 12:18:07 +00:00
""" API endpoint for accessing a list of Part objects
- GET: Return list of objects
- POST: Create a new Part object
"""
serializer_class = PartSerializer
2018-04-15 15:02:17 +00:00
def list(self, request, *args, **kwargs):
"""
Instead of using the DRF serialiser to LIST,
we serialize the objects manuually.
This turns out to be significantly faster.
"""
queryset = self.filter_queryset(self.get_queryset())
data = queryset.values(
'pk',
'category',
'image',
'name',
'IPN',
'revision',
'description',
'keywords',
'is_template',
'URL',
'units',
'trackable',
'assembly',
'component',
'salable',
'active',
).annotate(
in_stock=Sum('stock_items__quantity'),
)
# TODO - Annotate total being built
# TODO - Annotate total on order
# Reduce the number of lookups we need to do for the part categories
categories = {}
for item in data:
if item['image']:
item['image'] = os.path.join(settings.MEDIA_URL, item['image'])
cat_id = item['category']
if cat_id:
if cat_id not in categories:
categories[cat_id] = PartCategory.objects.get(pk=cat_id).pathstring
item['category__name'] = categories[cat_id]
else:
item['category__name'] = None
return Response(data)
def get_queryset(self):
# Does the user wish to filter by category?
cat_id = self.request.query_params.get('category', None)
2019-04-15 12:39:28 +00:00
# Start with all objects
parts_list = Part.objects.all()
if cat_id:
try:
category = PartCategory.objects.get(pk=cat_id)
2019-06-17 15:38:43 +00:00
parts_list = parts_list.filter(category__in=category.getUniqueChildren())
except PartCategory.DoesNotExist:
pass
2019-04-14 02:30:06 +00:00
# Ensure that related models are pre-loaded to reduce DB trips
parts_list = self.get_serializer_class().setup_eager_loading(parts_list)
2019-04-15 12:39:28 +00:00
return parts_list
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
'is_template',
'variant_of',
'assembly',
'component',
'trackable',
'purchaseable',
'salable',
'active',
]
ordering_fields = [
'name',
]
ordering = 'name'
2018-04-23 11:18:35 +00:00
search_fields = [
2019-04-16 22:19:40 +00:00
'$name',
2018-04-23 11:18:35 +00:00
'description',
2019-05-06 01:23:55 +00:00
'$IPN',
'keywords',
2018-04-23 11:18:35 +00:00
]
class PartStarDetail(generics.RetrieveDestroyAPIView):
""" API endpoint for viewing or removing a PartStar object """
queryset = PartStar.objects.all()
serializer_class = PartStarSerializer
class PartStarList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of PartStar objects.
- GET: Return list of PartStar objects
- POST: Create a new PartStar object
"""
queryset = PartStar.objects.all()
serializer_class = PartStarSerializer
def create(self, request, *args, **kwargs):
# Override the user field (with the logged-in user)
data = request.data.copy()
data['user'] = str(request.user.id)
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter
]
filter_fields = [
'part',
'user',
]
search_fields = [
'partname'
]
class BomList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of BomItem objects.
2019-04-27 12:18:07 +00:00
- GET: Return list of BomItem objects
- POST: Create a new BomItem object
"""
2018-05-02 13:42:57 +00:00
serializer_class = BomItemSerializer
def get_serializer(self, *args, **kwargs):
# Do we wish to include extra detail?
try:
part_detail = str2bool(self.request.GET.get('part_detail', None))
sub_part_detail = str2bool(self.request.GET.get('sub_part_detail', None))
except AttributeError:
part_detail = None
sub_part_detail = None
kwargs['part_detail'] = part_detail
kwargs['sub_part_detail'] = sub_part_detail
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
2018-05-02 13:42:57 +00:00
2019-05-19 22:31:03 +00:00
def get_queryset(self):
queryset = BomItem.objects.all()
queryset = self.get_serializer_class().setup_eager_loading(queryset)
return queryset
2018-05-02 13:42:57 +00:00
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
'part',
'sub_part'
]
class BomDetail(generics.RetrieveUpdateDestroyAPIView):
2019-04-27 12:18:07 +00:00
""" API endpoint for detail view of a single BomItem object """
queryset = BomItem.objects.all()
serializer_class = BomItemSerializer
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
2019-04-13 23:25:46 +00:00
cat_api_urls = [
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
2018-05-04 13:54:57 +00:00
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
]
2019-04-25 13:56:03 +00:00
part_star_api_urls = [
url(r'^(?P<pk>\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'),
# Catchall
url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'),
]
2019-05-18 10:24:09 +00:00
part_api_urls = [
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'),
2018-05-04 13:54:57 +00:00
url(r'^category/', include(cat_api_urls)),
url(r'^star/', include(part_star_api_urls)),
2019-06-23 14:01:34 +00:00
url(r'^(?P<pk>\d+)/?', PartDetail.as_view(), name='api-part-detail'),
url(r'^.*$', PartList.as_view(), name='api-part-list'),
]
2019-05-18 10:24:09 +00:00
bom_api_urls = [
# BOM Item Detail
2019-06-23 14:01:34 +00:00
url(r'^(?P<pk>\d+)/?', BomDetail.as_view(), name='api-bom-detail'),
# Catch-all
url(r'^.*$', BomList.as_view(), name='api-bom-list'),
]