mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
BugFix and docs for InvenTreeTree
This commit is contained in:
parent
f17e9de37b
commit
8229f02db6
@ -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
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
Loading…
Reference in New Issue
Block a user