Describe the bug
When subnets and IP addresses are provisioned by a SubnetDivisionRule (either during the initial provisioning or when updating number_of_ips), the foreign keys (subnet_id and ip_id) on the corresponding SubnetDivisionIndex records are saved as NULL in the database.
This breaks two important pieces of functionality:
-
Django Admin protections
In subnet_division/admin.py, the change permission logic checks whether a subnet was automatically generated using:
SubnetDivisionIndex.objects.filter(subnet_id=obj.id).exists()
Since subnet_id is stored as NULL, this query always returns False. As a result, automatically generated subnets and IP addresses are treated as normal objects and can be edited or deleted manually from the Django Admin, leading to inconsistent or corrupted IPAM data.
-
REST API queries and filters
Several queries rely on the SubnetDivisionIndex relationships to resolve related configurations/devices or identify automatically generated resources. Since these foreign keys are missing, those queries fail to return the expected results.
Steps To Reproduce
- Create a
SubnetDivisionRule (for example, a VPN Subnet Division Rule).
- Attach a device configuration or apply a template so provisioning is triggered.
- After provisioning completes, inspect the
subnet_division_subnetdivisionindex table or query it from the Django shell.
- Observe that the generated records have
subnet_id = NULL and/or ip_id = NULL.
- Open Django Admin → IPAM → Subnets.
- Open one of the automatically generated subnets.
- Notice that the subnet is editable, even though it was generated automatically and should be read-only.
Expected behavior
Generated SubnetDivisionIndex records should have valid subnet_id and ip_id foreign keys pointing to their corresponding generated objects.
Consequently:
- Automatically generated subnets should be detected correctly by the Django Admin.
- Automatically generated IP addresses should also be protected.
- API queries relying on these relationships should work correctly.
Root Cause Analysis
The issue occurs because the code copies the primary key values (subnet.id / ip.id) before the objects are saved.
At that point, those IDs are still None.
Later, bulk_create() updates the model instances with their generated primary keys, but it does not update primitive values that were copied earlier.
tasks.py
generated_indexes.append(
SubnetDivisionIndex(
keyword=f"{division_rule.label}_subnet{subnet.id}_ip{ip_id}",
subnet_id=subnet.id,
ip_id=ip.id, # ip.id is None here
rule_id=division_rule.id,
config_id=index.config_id,
)
)
Here, ip.id is copied before bulk_create() assigns an ID, so ip_id remains NULL.
rule_types/base.py
generated_indexes.append(
SubnetDivisionIndex(
keyword=f"{division_rule.label}_subnet{subnet_id}",
subnet_id=subnet_obj.id, # subnet_obj.id is None here
rule_id=division_rule.id,
config=config,
)
)
Similarly, subnet_obj.id is copied before the subnet has been saved, resulting in subnet_id = NULL.
Why this happens
Django's bulk_create() populates the primary keys on the model instances themselves after insertion.
For example:
subnet.id # becomes populated after bulk_create()
ip.id # becomes populated after bulk_create()
However, values that were copied earlier are not updated:
SubnetDivisionIndex(
subnet_id=subnet.id, # Copies None
)
The copied value remains None permanently unless explicitly updated afterward.
Minimal Reproduction
# Before bulk_create
ip.id == None
index = SubnetDivisionIndex(ip_id=ip.id)
# bulk_create assigns:
ip.id == 42
# But index.ip_id is still:
None
Proof of Bug
[Buggy Way] After bulk_create, index_buggy.ip_id is: None
Expected: 42
Actual: None
[Fixed Way] After bulk_create and save, index_fixed.ip_id is: 42
Expected: 42
Actual: 42
Impact
This issue causes:
- Django Admin protections to fail for generated subnets/IPs.
- Automatically generated objects to become editable and deletable.
- Broken REST API filtering and relationship resolution.
- Missing referential integrity between
SubnetDivisionIndex and generated IPAM objects.
System Information
- OS:
- Windows 11
- Ubuntu 24.04 LTS
- Python: 3.12
- Django:
Describe the bug
When subnets and IP addresses are provisioned by a
SubnetDivisionRule(either during the initial provisioning or when updatingnumber_of_ips), the foreign keys (subnet_idandip_id) on the correspondingSubnetDivisionIndexrecords are saved asNULLin the database.This breaks two important pieces of functionality:
Django Admin protections
In
subnet_division/admin.py, the change permission logic checks whether a subnet was automatically generated using:Since
subnet_idis stored asNULL, this query always returnsFalse. As a result, automatically generated subnets and IP addresses are treated as normal objects and can be edited or deleted manually from the Django Admin, leading to inconsistent or corrupted IPAM data.REST API queries and filters
Several queries rely on the
SubnetDivisionIndexrelationships to resolve related configurations/devices or identify automatically generated resources. Since these foreign keys are missing, those queries fail to return the expected results.Steps To Reproduce
SubnetDivisionRule(for example, a VPN Subnet Division Rule).subnet_division_subnetdivisionindextable or query it from the Django shell.subnet_id = NULLand/orip_id = NULL.Expected behavior
Generated
SubnetDivisionIndexrecords should have validsubnet_idandip_idforeign keys pointing to their corresponding generated objects.Consequently:
Root Cause Analysis
The issue occurs because the code copies the primary key values (
subnet.id/ip.id) before the objects are saved.At that point, those IDs are still
None.Later,
bulk_create()updates the model instances with their generated primary keys, but it does not update primitive values that were copied earlier.tasks.pyHere,
ip.idis copied beforebulk_create()assigns an ID, soip_idremainsNULL.rule_types/base.pySimilarly,
subnet_obj.idis copied before the subnet has been saved, resulting insubnet_id = NULL.Why this happens
Django's
bulk_create()populates the primary keys on the model instances themselves after insertion.For example:
However, values that were copied earlier are not updated:
The copied value remains
Nonepermanently unless explicitly updated afterward.Minimal Reproduction
Proof of Bug
Impact
This issue causes:
SubnetDivisionIndexand generated IPAM objects.System Information