From 90cc7d381333c05333feb041ad4eb2a22af651d0 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 1 Aug 2022 19:28:39 +0200 Subject: [PATCH 1/2] add a binary file field (#66533) --- passerelle/utils/forms.py | 76 ++++++++++++++++++++++++++++++++++++++ passerelle/utils/models.py | 33 +++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 passerelle/utils/forms.py create mode 100644 passerelle/utils/models.py diff --git a/passerelle/utils/forms.py b/passerelle/utils/forms.py new file mode 100644 index 00000000..cbb7b86d --- /dev/null +++ b/passerelle/utils/forms.py @@ -0,0 +1,76 @@ +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2022 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django import forms + + +class BinaryFileInput(forms.ClearableFileInput): + def is_initial(self, value): + """ + Return whether value is considered to be initial value. + """ + return bool(value) + + def format_value(self, value): + """Format the size of the value in the db. + + We can't render it's name or url, but we'd like to give some information + as to wether this file is not empty/corrupt. + """ + if self.is_initial(value): + return f'{len(value)} bytes' + + def value_from_datadict(self, data, files, name): + """Return the file contents so they can be put in the db.""" + upload = super().value_from_datadict(data, files, name) + if upload: + return upload.read() + return upload + + +class BinaryFileField(forms.FileField): + widget = BinaryFileInput + + def __init__(self, *, max_length=None, **kwargs): + self.max_length = max_length + super().__init__(**kwargs) + + def to_python(self, value): + return value + + def clean(self, data, initial=None): + # False means the field value should be cleared; further validation is + # not needed. + if data is False: + if not self.required: + return None + # If the field is required, clearing is not possible (the widget + # shouldn't return False data in that case anyway). False is not + # in self.empty_value; if a False value makes it this far + # it should be validated from here on out as None (so it will be + # caught by the required check). + data = None + if not data and initial: + return initial + return super(forms.FileField, self).clean(data) + + def bound_data(self, data, initial): + if data is None: + return initial + return data + + def has_changed(self, initial, data): + return not self.disabled and data is not None diff --git a/passerelle/utils/models.py b/passerelle/utils/models.py new file mode 100644 index 00000000..ab6500e0 --- /dev/null +++ b/passerelle/utils/models.py @@ -0,0 +1,33 @@ +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2022 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import models + + +class BinaryFileField(models.BinaryField): + def __init__(self, *args, **kwargs): + kwargs.setdefault('editable', True) + super().__init__(*args, **kwargs) + + def formfield(self, **kwargs): + from .forms import BinaryFileField + + return super().formfield( + **{ + 'form_class': BinaryFileField, + **kwargs, + } + ) -- 2.36.1