Source code for file.models.named_file

from onegov.core.crypto import random_token
from onegov.file.models.file import File
from onegov.file.utils import as_fileintent


from typing import overload, IO, TypeVar, TYPE_CHECKING
if TYPE_CHECKING:
    from sqlalchemy.orm import relationship
    from typing import Protocol
    from typing import Self

[docs] class HasFiles(Protocol):
[docs] files: relationship[list[File]]
[docs] _F = TypeVar('_F', bound=File)
[docs] class NamedFile: """ Helper for managing files using static names together with AssociatedFiles. A named file can be added by assigning a tuple of a file-like content and a filename. Reading the named file will return a File object. Finally, named files can be deleted using the del-Operator. Example:: class MyClass(AssociatedFiles): pdf = NamedFile() obj = MyClass() with open('some.pdf', 'rb') as file: obj.pdf = (file.read(), 'some.pdf') obj.pdf.reference.file.read() del obj.pdf """ def __init__(self, cls: type[_F] | None = None):
[docs] self.cls = cls or File
[docs] def __set_name__(self, owner: type[object], name: str) -> None: self.name = name
@overload
[docs] def __get__( self, instance: None, owner: type['HasFiles'] | None = None ) -> 'Self': ...
@overload def __get__( self, instance: 'HasFiles', owner: type['HasFiles'] | None = None ) -> File | None: ... def __get__( self, instance: 'HasFiles | None', owner: type['HasFiles'] | None = None ) -> 'Self | File | None': if instance is None: return None for file in instance.files: if file.name == self.name: return file return None
[docs] def __set__( self, instance: 'HasFiles', value: tuple[bytes | IO[bytes], str | None] ) -> None: content, filename = value self.__delete__(instance) file = self.cls(id=random_token()) file.name = self.name file.reference = as_fileintent(content, filename) instance.files.append(file)
[docs] def __delete__(self, instance: 'HasFiles') -> None: for file in tuple(instance.files): if file.name == self.name: instance.files.remove(file)