Coverage for src/backend/InvenTree/part/migrations/0102_auto_20230314_0112.py: 26%
46 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1# Generated by Django 3.2.18 on 2023-03-14 01:12
3import hashlib
4import logging
6from django.db import migrations
7from jinja2 import Template
9from InvenTree.helpers import normalize
12logger = logging.getLogger('inventree')
15def update_bom_item(apps, schema_editor):
16 """Update all existing BomItem instances, and cache the 'validated' field.
18 The 'validated' field denotes whether this individual BomItem has been validated,
19 which previously was calculated on the fly (which was very expensive).
20 """
22 BomItem = apps.get_model('part', 'bomitem')
23 InvenTreeSetting = apps.get_model('common', 'inventreesetting')
25 n = BomItem.objects.count()
27 if n > 0: 27 ↛ 29line 27 didn't jump to line 29 because the condition on line 27 was never true
29 for item in BomItem.objects.all():
30 """For each item, we need to re-calculate the "checksum", based on the *old* routine.
31 Note that as we cannot access the ORM models, we have to do this "by hand"
32 """
34 # Construct the 'full_name' for the sub_part (this is no longer required, but *was* required at point of migration)
35 try:
36 setting = InvenTreeSetting.objects.get(key='PART_NAME_FORMAT')
37 full_name_pattern = str(setting.value)
38 except Exception:
39 full_name_pattern = "{{ part.IPN if part.IPN }}{{ ' | ' if part.IPN }}{{ part.name }}{{ ' | ' if part.revision }}{{ part.revision if part.revision }}"
41 template = Template(full_name_pattern)
43 full_name = template.render({'part': item.sub_part})
45 # Calculate the OLD checksum manually for this BomItem
46 old_hash = hashlib.md5(str(item.pk).encode())
47 old_hash.update(str(item.sub_part.pk).encode())
48 old_hash.update(str(full_name).encode())
49 old_hash.update(str(item.quantity).encode())
50 old_hash.update(str(item.note).encode())
51 old_hash.update(str(item.reference).encode())
52 old_hash.update(str(item.optional).encode())
53 old_hash.update(str(item.inherited).encode())
55 if item.consumable:
56 old_hash.update(str(item.consumable).encode())
58 if item.allow_variants:
59 old_hash.update(str(item.allow_variants).encode())
61 checksum = str(old_hash.digest())
63 # Now, update the 'validated' field based on whether the checksum is 'valid' or not
64 item.validated = item.checksum == checksum
66 """Next, we need to update the item with a "new" hash, with the following differences:
67 - Uses the PK of the 'part', not the BomItem itself,
68 - Does not use the 'full_name' of the linked 'sub_part'
69 - Does not use the 'note' field
70 """
72 if item.validated:
74 new_hash = hashlib.md5(''.encode())
76 components = [
77 item.part.pk,
78 item.sub_part.pk,
79 normalize(item.quantity),
80 item.reference,
81 item.optional,
82 item.inherited,
83 item.consumable,
84 item.allow_variants
85 ]
87 for component in components:
88 new_hash.update(str(component).encode())
90 item.checksum = str(new_hash.digest())
92 item.save()
94 logger.info(f"Updated 'validated' flag for {n} BomItem objects")
97class Migration(migrations.Migration):
99 dependencies = [
100 ('part', '0101_bomitem_validated'),
101 ]
103 operations = [
104 migrations.RunPython(
105 update_bom_item,
106 reverse_code=migrations.RunPython.noop
107 )
108 ]