diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index aeb5bc81f6..f607ffa19a 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -5,6 +5,11 @@ from django.core.exceptions import ObjectDoesNotExist from django.contrib.contenttypes.models import ContentType class InvenTreeTree(models.Model): + """ Provides an abstracted self-referencing tree model for data categories. + - 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) + """ + name = models.CharField(max_length=100) description = models.CharField(max_length=250) parent = models.ForeignKey('self', @@ -13,8 +18,10 @@ class InvenTreeTree(models.Model): null=True) #limit_choices_to={id: getAcceptableParents}) - # Return a flat set of all child items under this node def getUniqueChildren(self, unique=None): + """ Return a flat set of all child items that exist under this node. + If any child items are repeated, the repetitions are omitted. + """ if unique is None: unique = set() @@ -33,8 +40,11 @@ class InvenTreeTree(models.Model): return unique - # Return a list of acceptable other parents def getAcceptableParents(self): + """ Returns a list of acceptable parent items within this model + Acceptable parents are ones which are not underneath this item. + Setting the parent of an item to its own child results in recursion. + """ contents = ContentType.objects.get_for_model(type(self)) available = contents.get_all_objects_for_this_type() @@ -50,9 +60,15 @@ class InvenTreeTree(models.Model): return acceptable - # Return the parent path of this category @property def path(self): + """ Return the parent path of this category + + Todo: + This function is recursive and expensive. + It should be reworked such that only a single db call is required + """ + if self.parent: return self.parent.path + [self.parent] else: @@ -60,12 +76,25 @@ class InvenTreeTree(models.Model): return parent_path - # Custom SetAttribute function to prevent parent recursion def __setattr__(self, attrname, val): - # Prevent parent from being set such that it would cause a recursion loop + """ Custom Attribute Setting function + + Parent: + Setting the parent of an item to its own child results in an infinite loop. + The parent of an item cannot be set to: + a) Its own ID + b) The ID of any child items that exist underneath it + + Name: + Tree node names are limited to a reduced character set + """ + if attrname == 'parent_id': + # If current ID is None, continue (as this object is just being created) + if self.id is None: + pass # Parent cannot be set to same ID (this would cause looping) - if val == self.id: + elif val == self.id: return # Null parent is OK elif val is None: @@ -76,7 +105,24 @@ class InvenTreeTree(models.Model): if val in kids: return + # Prohibit certain characters from tree node names + elif attrname == 'name': + val = val.translate({ord(c): None for c in "!@#$%^&*'\"\\/[]{}<>,|+=~`"}) + super(InvenTreeTree, self).__setattr__(attrname, val) + + def __str__(self): + """ String representation of a category is the full path to that category + + Todo: + This is recursive - Make it not so. + """ + + if self.parent: + return "/".join([p.name for p in self.path]) + "/" + self.name + else: + return self.name + class Meta: abstract = True \ No newline at end of file diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 78580e2ae3..12394719e4 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -6,17 +6,16 @@ from django.core.exceptions import ObjectDoesNotExist from InvenTree.models import InvenTreeTree class PartCategory(InvenTreeTree): - def __str__(self): - if self.parent: - return "/".join([p.name for p in self.path]) + "/" + self.name - else: - return self.name - + """ PartCategory provides hierarchical organization of Part objects. + """ + class Meta: verbose_name = "Part Category" verbose_name_plural = "Part Categories" class Part(models.Model): + """ Represents a """ + name = models.CharField(max_length=100) description = models.CharField(max_length=250, blank=True) IPN = models.CharField(max_length=100, blank=True) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 9d7c90daca..a07308a435 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -3,25 +3,10 @@ from __future__ import unicode_literals from django.db import models from part.models import Part +from InvenTree.models import InvenTreeTree -class Warehouse(models.Model): - name = models.CharField(max_length=100) - description = models.CharField(max_length=250, blank=True) - parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True) - - def __str__(self): - if self.parent: - return "/".join([p.name for p in self.path]) + "/" + self.name - else: - return self.name - - # Return path of this category - @property - def path(self): - if self.parent: - return self.parent.path + [self.parent] - else: - return [] +class Warehouse(InvenTreeTree): + pass class StockItem(models.Model): part = models.ForeignKey(Part, on_delete=models.CASCADE) diff --git a/InvenTree/track/views.py b/InvenTree/track/views.py index 91ea44a218..87db2335dc 100644 --- a/InvenTree/track/views.py +++ b/InvenTree/track/views.py @@ -1,3 +1,5 @@ -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404 +from django.http import HttpResponse -# Create your views here. +def index(request): + return HttpResponse("This is the Tracking page") \ No newline at end of file