From 442841d5545294c0f6ef965ceb7aecdb2691af6c Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 16 Oct 2026 15:55:34 +0200
Subject: [PATCH v14 04/12] Don't re-parse the stored badge image on every edit

BadgeForm includes imagedata, so ModelForm._post_clean() validated it
through full_clean() on every save, and ImageBinaryField.to_python()
parsed and decoded the stored image even when the edit did not touch it.
Now that the field only accepts exact 150x150 images that is a small
decode, but it is needless work on every badge edit, and it would be
expensive for any larger row stored earlier while auto_scale was on.

Exclude imagedata from model validation unless a new file was uploaded.
self.files holds only newly uploaded files, which is exactly the case
that needs validating. changed_data cannot be used for this: for a file
field has_changed() compares the stored bytes against the empty value,
so an unchanged file always counts as changed.

The value is still written back through construct_instance() /
save_form_data(), so edits that keep or clear the image behave as before.
---
 pgweb/contributors/forms.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/pgweb/contributors/forms.py b/pgweb/contributors/forms.py
index 5ce44557..e73c5294 100644
--- a/pgweb/contributors/forms.py
+++ b/pgweb/contributors/forms.py
@@ -57,6 +57,16 @@ class BadgeForm(forms.ModelForm):
             # remove the holders fieldset
             self.fieldsets = [fs for fs in self.fieldsets if fs['id'] != 'holders']
 
+    def _get_validation_exclusions(self):
+        exclude = super()._get_validation_exclusions()
+        # The stored image was already validated when it was uploaded, so
+        # don't re-parse and re-decode it on an edit that doesn't replace it.
+        # self.files holds only newly uploaded files, unlike changed_data
+        # (FileField.has_changed reports an unchanged file as changed).
+        if 'imagedata' not in self.files:
+            exclude.add('imagedata')
+        return exclude
+
     def clean_add_holder(self):
         if self.cleaned_data['add_holder']:
             for u in self.cleaned_data['add_holder'].split():
-- 
2.53.0

