Describe the bug
ValidatedModelSerializer.validate() only applies incoming data when there is no
instance (creation); on PUT/PATCH it runs full_clean() on the instance as
loaded. So Template.clean() never sees the change being saved, and its
type-scoped corrections are applied one write late — the offending write stores
an inconsistent row, and some later unrelated write happens to fix it.
TemplateSerializer.validate_vpn does not catch this: it only fires when both
type and vpn are present in initial_data, so a type-only PATCH bypasses it.
Steps To Reproduce
- Create a template with
type="vpn" and a VPN attached.
PATCH /api/v1/controller/template/<pk>/ with
{"type": "generic", "config": {"interfaces": []}} → 200.
- Re-read the row:
type is generic but vpn is still set, contradicting
clean()'s "clears VPN specific fields if type is not VPN".
PATCH anything else, e.g. {"name": "renamed"} → only now is vpn set to
None.
Same shape for required/default: PATCH {"required": true} stores
required=True, default=False, and default becomes True only on a later
write.
Expected behavior
The type change itself should clear vpn/auto_cert, and required should
force default on the same write.
Depends on openwisp/openwisp-utils#760 — and note that if that
lands as copy-based validation, Template.clean() will need to stop correcting
by assignment, since those corrections would no longer be saved at all.
Screenshots
N/A.
System Informatioon:
- OS: Pop!_OS 24.04 LTS
- Python Version: Python 3.13.5
- Django Version: Django 5.2.16
- Browser and Browser Version (if applicable): N/A (REST API)
Also: openwisp-controller 1.2.3, openwisp-utils 1.2.2, djangorestframework
3.16.1.
Describe the bug
ValidatedModelSerializer.validate()only applies incoming data when there is noinstance (creation); on PUT/PATCH it runs
full_clean()on the instance asloaded. So
Template.clean()never sees the change being saved, and itstype-scoped corrections are applied one write late — the offending write stores
an inconsistent row, and some later unrelated write happens to fix it.
TemplateSerializer.validate_vpndoes not catch this: it only fires when bothtypeandvpnare present ininitial_data, so a type-only PATCH bypasses it.Steps To Reproduce
type="vpn"and a VPN attached.PATCH /api/v1/controller/template/<pk>/with{"type": "generic", "config": {"interfaces": []}}→200.typeisgenericbutvpnis still set, contradictingclean()'s "clears VPN specific fields if type is not VPN".PATCHanything else, e.g.{"name": "renamed"}→ only now isvpnset toNone.Same shape for
required/default:PATCH {"required": true}storesrequired=True, default=False, anddefaultbecomesTrueonly on a laterwrite.
Expected behavior
The type change itself should clear
vpn/auto_cert, andrequiredshouldforce
defaulton the same write.Depends on openwisp/openwisp-utils#760 — and note that if that
lands as copy-based validation,
Template.clean()will need to stop correctingby assignment, since those corrections would no longer be saved at all.
Screenshots
N/A.
System Informatioon:
Also: openwisp-controller 1.2.3, openwisp-utils 1.2.2, djangorestframework
3.16.1.