robotHamster
robotHamster

Reputation: 649

Iterative access for nested objects

Description of the problem

The problem is a classical Bill of Materials (BoM) problem; Suppose we have the class BomEntry(object) defined as:

class BomEntry:
    def __init__(self, part, quantity=0, unit="", children=[]):
        self.part = part
        self.quantity = quantity
        self.unit = unit
        self.children = children

part is a django model, and quantity and unit are two of its members.

The Django model has a method make_bom(self) which returns an instance of BomEntry(a class which doesn't use django) . Asm is the django model keeping track of BoM data in the database

def make_bom(self, depth=1):
    if not self.is_asm:
        return BomEntry(self, 1, "", [])
    else:
        children = list(Asm.objects.filter(parent=self))
        children_bom = [BomEntry(obj.child, obj.quantity, obj.unit, []) for obj in children]
        bom = BomEntry(self, 1, "", children=children_bom)
        return bom

I'm currently including a parameter to decide the depth of the BoM, but I can't wrap my head around how I would use it.

I want to be able to traverse the nested objects, ending up with an output similar to this:

{
    'part': <PartAsm: 100-1822-R1-A>,
    'quantity': 1,
    'unit': '',
    'children':
        [
            {
                'part': <PartAsm: 100-1823-R1-A>, 
                'quantity': 1,
                'unit': '', 
                'children': 
                    []
            },
            {
                'part':
                <PartAsm: 100-1824-R1-A>, 
                'quantity': 1,
                'unit': '', 
                'children': 
                [
                    {
                        'part': <PartAsm: 100-1825-R1-A>, 
                        'quantity': Decimal('1.00'), 
                        'unit': 'g', 
                        'children': 
                        []
                    },
                    {
                        'part': <PartAsm: 100-1826-R1-A>, 
                        'quantity': Decimal('1.00'), 
                        'unit': 'g', 
                        'children': 
                        []
                    }
                ]
            }
        ]
}

The output above was acquired using the console, I would appreciate any advice on looping this or making it recursive. I hope I provided sufficient and clear information

Upvotes: 0

Views: 71

Answers (2)

Barmar
Barmar

Reputation: 781245

When depth is more than 1, make_bom() should recurse, decrementing depth in the call.

def make_bom(self, depth=1):
    if not self.is_asm:
        return BomEntry(self, 1, "", [])
    else:
        if depth > 1:
            children = list(Asm.objects.filter(parent=self))
            children_bom = [make_bom(BomEntry(obj.child, obj.quantity, obj.unit, []), depth-1) for obj in children]
        else:
            children_bom = []
        bom = BomEntry(self, 1, "", children=children_bom)
        return bom

Upvotes: 1

robotHamster
robotHamster

Reputation: 649

I finally got it working using @Barmar 's valuable input. The make_bom function now looks like:

def make_bom(self, quantity=1, unit="def_unit", depth=1):
    if not self.is_asm:

        return BomEntry(self, quantity, unit, [])
    else:
        if depth <= 1:
            children = list(Asm.objects.filter(parent=self))
            children_bom = [BomEntry(obj.child, quantity*obj.quantity, obj.unit, []) for obj in children]
            bom = BomEntry(self, quantity, unit, children=children_bom)
        else:
            bom = BomEntry(self, quantity, "def_unit",
                           children=[a.child.make_bom(quantity*a.quantity, a.unit, depth - 1) for a in
                                     list(Asm.objects.filter(parent=self))])
        return bom

I added in the quantity and unit params for completeness.

Thanks a million @Barmar

Upvotes: 0

Related Questions