BugFix and docs for InvenTreeTree

This commit is contained in:
Oliver Walters 2017-03-27 22:55:21 +11:00
parent f17e9de37b
commit 8229f02db6
4 changed files with 64 additions and 32 deletions

View File

@ -5,6 +5,11 @@ from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
class InvenTreeTree(models.Model): 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) name = models.CharField(max_length=100)
description = models.CharField(max_length=250) description = models.CharField(max_length=250)
parent = models.ForeignKey('self', parent = models.ForeignKey('self',
@ -13,8 +18,10 @@ class InvenTreeTree(models.Model):
null=True) null=True)
#limit_choices_to={id: getAcceptableParents}) #limit_choices_to={id: getAcceptableParents})
# Return a flat set of all child items under this node
def getUniqueChildren(self, unique=None): 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: if unique is None:
unique = set() unique = set()
@ -33,8 +40,11 @@ class InvenTreeTree(models.Model):
return unique return unique
# Return a list of acceptable other parents
def getAcceptableParents(self): 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)) contents = ContentType.objects.get_for_model(type(self))
available = contents.get_all_objects_for_this_type() available = contents.get_all_objects_for_this_type()
@ -50,9 +60,15 @@ class InvenTreeTree(models.Model):
return acceptable return acceptable
# Return the parent path of this category
@property @property
def path(self): 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: if self.parent:
return self.parent.path + [self.parent] return self.parent.path + [self.parent]
else: else:
@ -60,12 +76,25 @@ class InvenTreeTree(models.Model):
return parent_path return parent_path
# Custom SetAttribute function to prevent parent recursion
def __setattr__(self, attrname, val): 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 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) # Parent cannot be set to same ID (this would cause looping)
if val == self.id: elif val == self.id:
return return
# Null parent is OK # Null parent is OK
elif val is None: elif val is None:
@ -76,7 +105,24 @@ class InvenTreeTree(models.Model):
if val in kids: if val in kids:
return 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) 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: class Meta:
abstract = True abstract = True

View File

@ -6,17 +6,16 @@ from django.core.exceptions import ObjectDoesNotExist
from InvenTree.models import InvenTreeTree from InvenTree.models import InvenTreeTree
class PartCategory(InvenTreeTree): class PartCategory(InvenTreeTree):
def __str__(self): """ PartCategory provides hierarchical organization of Part objects.
if self.parent: """
return "/".join([p.name for p in self.path]) + "/" + self.name
else:
return self.name
class Meta: class Meta:
verbose_name = "Part Category" verbose_name = "Part Category"
verbose_name_plural = "Part Categories" verbose_name_plural = "Part Categories"
class Part(models.Model): class Part(models.Model):
""" Represents a """
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)
IPN = models.CharField(max_length=100, blank=True) IPN = models.CharField(max_length=100, blank=True)

View File

@ -3,25 +3,10 @@ from __future__ import unicode_literals
from django.db import models from django.db import models
from part.models import Part from part.models import Part
from InvenTree.models import InvenTreeTree
class Warehouse(models.Model): class Warehouse(InvenTreeTree):
name = models.CharField(max_length=100) pass
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 StockItem(models.Model): class StockItem(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE) part = models.ForeignKey(Part, on_delete=models.CASCADE)

View File

@ -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")