Merge pull request #21 from SchrodingersGat/master

Updated API URLs
This commit is contained in:
Oliver 2017-04-11 09:41:42 +10:00 committed by GitHub
commit d7c67fbaa4
9 changed files with 144 additions and 71 deletions

View File

@ -0,0 +1,12 @@
from django.conf.urls import url, include
from django.contrib import admin
admin.site.site_header = "InvenTree Admin"
urlpatterns = [
url(r'^stock/', include('stock.urls')),
url(r'^part/', include('part.api_urls')),
url(r'^supplier/', include('supplier.urls')),
url(r'^track/', include('track.urls')),
url(r'^project/', include('project.api_urls'))
]

View File

@ -8,10 +8,10 @@ from django.contrib.contenttypes.models import ContentType
class Company(models.Model): class Company(models.Model):
""" Abstract model representing an external company """ Abstract model representing an external company
""" """
class Meta: class Meta:
abstract = True abstract = True
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
URL = models.URLField(blank=True) URL = models.URLField(blank=True)
address = models.CharField(max_length=200, address = models.CharField(max_length=200,
@ -33,10 +33,10 @@ class InvenTreeTree(models.Model):
- Each Category has one parent Category, which can be blank (for a top-level Category). - Each Category has one parent Category, which can be blank (for a top-level Category).
- Each Category can have zero-or-more child Categor(y/ies) - Each Category can have zero-or-more child Categor(y/ies)
""" """
class Meta: class Meta:
abstract = True abstract = True
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
description = models.CharField(max_length=250, blank=True) description = models.CharField(max_length=250, blank=True)
parent = models.ForeignKey('self', parent = models.ForeignKey('self',
@ -44,95 +44,102 @@ class InvenTreeTree(models.Model):
blank=True, blank=True,
null=True, null=True,
related_name='children') related_name='children')
def getUniqueParents(self, unique=None): def getUniqueParents(self, unique=None):
""" Return a flat set of all parent items that exist above this node. """ Return a flat set of all parent items that exist above this node.
If any parents are repeated (which would be very bad!), the process is halted If any parents are repeated (which would be very bad!), the process is halted
""" """
if unique is None: if unique is None:
unique = set() unique = set()
else: else:
unique.add(self.id) unique.add(self.id)
if self.parent and self.parent.id not in unique: if self.parent and self.parent.id not in unique:
self.parent.getUniqueParents(unique) self.parent.getUniqueParents(unique)
return unique return unique
def getUniqueChildren(self, unique=None): def getUniqueChildren(self, unique=None):
""" Return a flat set of all child items that exist under this node. """ Return a flat set of all child items that exist under this node.
If any child items are repeated, the repetitions are omitted. If any child items are repeated, the repetitions are omitted.
""" """
if unique is None: if unique is None:
unique = set() unique = set()
if self.id in unique: if self.id in unique:
return unique return unique
unique.add(self.id) unique.add(self.id)
# Some magic to get around the limitations of abstract models # Some magic to get around the limitations of abstract models
contents = ContentType.objects.get_for_model(type(self)) contents = ContentType.objects.get_for_model(type(self))
children = contents.get_all_objects_for_this_type(parent=self.id) children = contents.get_all_objects_for_this_type(parent=self.id)
return unique return unique
@property
def children(self):
contents = ContentType.objects.get_for_model(type(self))
children = contents.get_all_objects_for_this_type(parent=self.id)
return children
def getAcceptableParents(self): def getAcceptableParents(self):
""" Returns a list of acceptable parent items within this model """ Returns a list of acceptable parent items within this model
Acceptable parents are ones which are not underneath this item. Acceptable parents are ones which are not underneath this item.
Setting the parent of an item to its own child results in recursion. Setting the parent of an item to its own child results in recursion.
""" """
contents = ContentType.objects.get_for_model(type(self)) contents = ContentType.objects.get_for_model(type(self))
available = contents.get_all_objects_for_this_type() available = contents.get_all_objects_for_this_type()
# List of child IDs # List of child IDs
childs = getUniqueChildren() childs = getUniqueChildren()
acceptable = [None] acceptable = [None]
for a in available: for a in available:
if a.id not in childs: if a.id not in childs:
acceptable.append(a) acceptable.append(a)
return acceptable return acceptable
@property @property
def parentpath(self): def parentpath(self):
""" Return the parent path of this category """ Return the parent path of this category
Todo: Todo:
This function is recursive and expensive. This function is recursive and expensive.
It should be reworked such that only a single db call is required It should be reworked such that only a single db call is required
""" """
if self.parent: if self.parent:
return self.parent.parentpath + [self.parent] return self.parent.parentpath + [self.parent]
else: else:
return [] return []
@property @property
def path(self): def path(self):
if self.parent: if self.parent:
return "/".join([p.name for p in self.parentpath]) + "/" + self.name return "/".join([p.name for p in self.parentpath]) + "/" + self.name
else: else:
return self.name return self.name
def __setattr__(self, attrname, val): def __setattr__(self, attrname, val):
""" Custom Attribute Setting function """ Custom Attribute Setting function
Parent: Parent:
Setting the parent of an item to its own child results in an infinite loop. Setting the parent of an item to its own child results in an infinite loop.
The parent of an item cannot be set to: The parent of an item cannot be set to:
a) Its own ID a) Its own ID
b) The ID of any child items that exist underneath it b) The ID of any child items that exist underneath it
Name: Name:
Tree node names are limited to a reduced character set Tree node names are limited to a reduced character set
""" """
if attrname == 'parent_id': if attrname == 'parent_id':
# If current ID is None, continue # If current ID is None, continue
# - This object is just being created # - This object is just being created
@ -153,14 +160,14 @@ class InvenTreeTree(models.Model):
# Prohibit certain characters from tree node names # Prohibit certain characters from tree node names
elif attrname == 'name': elif attrname == 'name':
val = val.translate({ord(c): None for c in "!@#$%^&*'\"\\/[]{}<>,|+=~`"}) val = val.translate({ord(c): None for c in "!@#$%^&*'\"\\/[]{}<>,|+=~`"})
super(InvenTreeTree, self).__setattr__(attrname, val) super(InvenTreeTree, self).__setattr__(attrname, val)
def __str__(self): def __str__(self):
""" String representation of a category is the full path to that category """ String representation of a category is the full path to that category
Todo: Todo:
This is recursive - Make it not so. This is recursive - Make it not so.
""" """
return self.path return self.path

View File

@ -1,25 +1,10 @@
"""InvenTree URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib import admin from django.contrib import admin
admin.site.site_header = "InvenTree Admin" admin.site.site_header = "InvenTree Admin"
urlpatterns = [ urlpatterns = [
url(r'^api/', include('InvenTree.api_urls')),
url(r'^stock/', include('stock.urls')), url(r'^stock/', include('stock.urls')),
url(r'^part/', include('part.urls')), url(r'^part/', include('part.urls')),
url(r'^supplier/', include('supplier.urls')), url(r'^supplier/', include('supplier.urls')),

View File

@ -0,0 +1,17 @@
from django.conf.urls import url
from . import views
urlpatterns = [
# Display part detail
url(r'^(?P<pk>[0-9]+)/$', views.PartDetail.as_view()),
# Display a single part category
url(r'^category/(?P<pk>[0-9]+)/$', views.PartCategoryDetail.as_view()),
# Display a list of top-level categories
url(r'^category/$', views.PartCategoryList.as_view()),
# Display list of all parts
url(r'^$', views.PartList.as_view())
]

View File

@ -15,6 +15,12 @@ class PartCategory(InvenTreeTree):
verbose_name = "Part Category" verbose_name = "Part Category"
verbose_name_plural = "Part Categories" verbose_name_plural = "Part Categories"
@property
def parts(self):
parts_list = self.part_set.all()
print(parts_list)
return parts_list
class Part(models.Model): class Part(models.Model):
""" Represents a """ """ Represents a """

View File

@ -4,14 +4,21 @@ from .models import Part, PartCategory, PartParameter
class ParameterSerializer(serializers.ModelSerializer): class ParameterSerializer(serializers.ModelSerializer):
""" Serializer for a PartParameter
"""
class Meta: class Meta:
model = PartParameter model = PartParameter
fields = ('name', fields = ('pk',
'name',
'value', 'value',
'units') 'units')
class PartSerializer(serializers.ModelSerializer): class PartDetailSerializer(serializers.ModelSerializer):
""" Serializer for complete detail information of a part.
Used when displaying all details of a single component.
"""
params = ParameterSerializer(source='parameters', many=True) params = ParameterSerializer(source='parameters', many=True)
@ -26,11 +33,44 @@ class PartSerializer(serializers.ModelSerializer):
'params') 'params')
class PartCategorySerializer(serializers.ModelSerializer): class PartBriefSerializer(serializers.ModelSerializer):
""" Serializer for displaying overview of a part.
Used e.g. for displaying list of parts in a category.
"""
class Meta:
model = Part
fields = ('pk',
'name',
'IPN',
'description',
'category',
'stock')
class PartCategoryBriefSerializer(serializers.ModelSerializer):
class Meta:
model = PartCategory
fields = ('pk',
'name',
'description')
class PartCategoryDetailSerializer(serializers.ModelSerializer):
# List of parts in this category
parts = PartBriefSerializer(many=True)
# List of child categories under this one
children = PartCategoryBriefSerializer(many=True)
class Meta: class Meta:
model = PartCategory model = PartCategory
fields = ('pk', fields = ('pk',
'name', 'name',
'description', 'description',
'parent', 'parent',
'path') 'path',
'children',
'parts')

View File

@ -3,15 +3,9 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
# Display part detail # part landing page
url(r'^(?P<pk>[0-9]+)/$', views.PartDetail.as_view()), url(r'^$', views.part_index),
# Display a single part category # part category landing page
url(r'^category/(?P<pk>[0-9]+)/$', views.PartCategoryDetail.as_view()), url(r'^category/$', views.category_index)
# Display a list of top-level categories
url(r'^category/$', views.PartCategoryList.as_view()),
# Display list of parts
url(r'^$', views.PartList.as_view())
] ]

View File

@ -4,32 +4,38 @@ from django.http import HttpResponse, Http404
from rest_framework import generics from rest_framework import generics
from .models import PartCategory, Part from .models import PartCategory, Part
from .serializers import PartSerializer, PartCategorySerializer from .serializers import PartBriefSerializer, PartDetailSerializer
from .serializers import PartCategoryBriefSerializer, PartCategoryDetailSerializer
def index(request): def part_index(request):
return HttpResponse("Hello world. This is the parts page") return HttpResponse("Hello world. This is the parts page")
def category_index(request):
return HttpResponse("This is the category page")
class PartDetail(generics.RetrieveAPIView): class PartDetail(generics.RetrieveAPIView):
queryset = Part.objects.all() queryset = Part.objects.all()
serializer_class = PartSerializer serializer_class = PartDetailSerializer
class PartList(generics.ListAPIView): class PartList(generics.ListAPIView):
queryset = Part.objects.all() queryset = Part.objects.all()
serializer_class = PartSerializer serializer_class = PartBriefSerializer
class PartCategoryDetail(generics.RetrieveAPIView): class PartCategoryDetail(generics.RetrieveAPIView):
""" Return information on a single PartCategory
"""
queryset = PartCategory.objects.all() queryset = PartCategory.objects.all()
serializer_class = PartCategorySerializer serializer_class = PartCategoryDetailSerializer
class PartCategoryList(generics.ListAPIView): class PartCategoryList(generics.ListAPIView):
""" Return a list of all top-level part categories.
queryset = PartCategory.objects.all() Categories are considered "top-level" if they do not have a parent
serializer_class = PartCategorySerializer """
queryset = PartCategory.objects.filter(parent=None)
serializer_class = PartCategoryBriefSerializer

View File

@ -0,0 +1,6 @@
from django.conf.urls import url
from . import views
urlpatterns = [
]