mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
Tree parent filtering
- Prevents recursion
This commit is contained in:
parent
514bd7a6bc
commit
cc7593b44f
83
InvenTree/InvenTree/models.py
Normal file
83
InvenTree/InvenTree/models.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
class InvenTreeTree(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
description = models.CharField(max_length=250)
|
||||||
|
parent = models.ForeignKey('self',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
null=True)
|
||||||
|
#limit_choices_to={id: getAcceptableParents})
|
||||||
|
|
||||||
|
# Return a flat set of all child items under this node
|
||||||
|
def getUniqueChildren(self, unique=None):
|
||||||
|
|
||||||
|
if unique is None:
|
||||||
|
unique = set()
|
||||||
|
|
||||||
|
if self.id in unique:
|
||||||
|
return unique
|
||||||
|
|
||||||
|
unique.add(self.id)
|
||||||
|
|
||||||
|
# Some magic to get around the limitations of abstract models
|
||||||
|
contents = ContentType.objects.get_for_model(type(self))
|
||||||
|
children = contents.get_all_objects_for_this_type(parent = self.id)
|
||||||
|
|
||||||
|
for child in children:
|
||||||
|
child.getUniqueChildren(unique)
|
||||||
|
|
||||||
|
return unique
|
||||||
|
|
||||||
|
# Return a list of acceptable other parents
|
||||||
|
def getAcceptableParents(self):
|
||||||
|
contents = ContentType.objects.get_for_model(type(self))
|
||||||
|
|
||||||
|
available = contents.get_all_objects_for_this_type()
|
||||||
|
|
||||||
|
# List of child IDs
|
||||||
|
childs = getUniqueChildren()
|
||||||
|
|
||||||
|
acceptable = [None]
|
||||||
|
|
||||||
|
for a in available:
|
||||||
|
if a.id not in childs:
|
||||||
|
acceptable.append(a)
|
||||||
|
|
||||||
|
return acceptable
|
||||||
|
|
||||||
|
# Return the parent path of this category
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
if self.parent:
|
||||||
|
return self.parent.path + [self.parent]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
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
|
||||||
|
if attrname == 'parent_id':
|
||||||
|
# Parent cannot be set to same ID (this would cause looping)
|
||||||
|
if val == self.id:
|
||||||
|
return
|
||||||
|
# Null parent is OK
|
||||||
|
elif val is None:
|
||||||
|
pass
|
||||||
|
# Ensure that the new parent is not already a child
|
||||||
|
else:
|
||||||
|
kids = self.getUniqueChildren()
|
||||||
|
if val in kids:
|
||||||
|
print("ALREADY A CHILD")
|
||||||
|
return
|
||||||
|
|
||||||
|
super(InvenTreeTree, self).__setattr__(attrname, val)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
@ -38,7 +38,8 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
'part.apps.PartConfig'
|
'part.apps.PartConfig',
|
||||||
|
'stock.apps.StockConfig'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -3,4 +3,11 @@ from django.contrib import admin
|
|||||||
from .models import PartCategory, Part
|
from .models import PartCategory, Part
|
||||||
|
|
||||||
admin.site.register(Part)
|
admin.site.register(Part)
|
||||||
admin.site.register(PartCategory)
|
|
||||||
|
# Custom form for PartCategory
|
||||||
|
class PartCategoryAdmin(admin.ModelAdmin):
|
||||||
|
# TODO - Only let valid parents be displayed
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(PartCategory, PartCategoryAdmin)
|
@ -3,69 +3,19 @@ from __future__ import unicode_literals
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
class InvenTreeTree(models.Model):
|
from InvenTree.models import InvenTreeTree
|
||||||
|
|
||||||
# Return a flat set of all child items under this node
|
|
||||||
def getUniqueChildren(self, unique=None):
|
|
||||||
|
|
||||||
if unique is None:
|
|
||||||
unique = set()
|
|
||||||
|
|
||||||
if self.id in unique:
|
|
||||||
return unique
|
|
||||||
|
|
||||||
unique.add(self.id)
|
|
||||||
|
|
||||||
children = PartCategory.objects.filter(parent = self.id)
|
|
||||||
|
|
||||||
for child in children:
|
|
||||||
child.getUniqueChildren(unique)
|
|
||||||
|
|
||||||
return unique
|
|
||||||
|
|
||||||
# Return the parent path of this category
|
|
||||||
@property
|
|
||||||
def path(self):
|
|
||||||
if self.parent:
|
|
||||||
return self.parent.path + [self.parent]
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
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
|
|
||||||
if attrname == 'parent_id':
|
|
||||||
# Parent cannot be set to same ID (this would cause looping)
|
|
||||||
if val == self.id:
|
|
||||||
return
|
|
||||||
# Null parent is OK
|
|
||||||
elif val is None:
|
|
||||||
pass
|
|
||||||
# Ensure that the new parent is not already a child
|
|
||||||
else:
|
|
||||||
kids = self.getUniqueChildren()
|
|
||||||
if val in kids:
|
|
||||||
print("ALREADY A CHILD")
|
|
||||||
return
|
|
||||||
|
|
||||||
super(InvenTreeTree, self).__setattr__(attrname, val)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class PartCategory(InvenTreeTree):
|
class PartCategory(InvenTreeTree):
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
description = models.CharField(max_length=250)
|
|
||||||
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.parent:
|
if self.parent:
|
||||||
return "/".join([p.name for p in self.path]) + "/" + self.name
|
return "/".join([p.name for p in self.path]) + "/" + self.name
|
||||||
else:
|
else:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Part Category"
|
||||||
|
verbose_name_plural = "Part Categories"
|
||||||
|
|
||||||
class Part(models.Model):
|
class Part(models.Model):
|
||||||
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)
|
||||||
@ -80,5 +30,8 @@ class Part(models.Model):
|
|||||||
else:
|
else:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Part"
|
||||||
|
verbose_name_plural = "Parts"
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user