emmett-framework / emmett

The web framework for inventors
BSD 3-Clause "New" or "Revised" License
1.08k stars 72 forks source link

BLOB type is always decoded to string #461

Open robinvandernoord opened 1 year ago

robinvandernoord commented 1 year ago

I'm having an issue with blobs in the database. According to the documentation, this field type is mapped to Python str. When looking at the code, this is done with .decode('utf-8'). However, in our database we have some raw images, which of course can't be decoded as utf-8. image When looking through the adapter code (emmett/orm/adapters.py 229: def parse), I saw an option blob_decode. However, I can't seem to find where to set that option. Is this an argument to app.config.db or adapter_args? I have monkey patched the emmett.orm.adapters.parse method to forcefully set blob_decode to False, but that's very hacky and I've already seen some selects where my patch is not applied.

Summary: how do I get raw bytes from my bytea/blob database fields?

gi0baro commented 1 year ago

BLOB encoding/decoding is a pyDAL feature kept in place for retro-compatibility reasons.

In order to use raw BLOB values with postgres, you should define your own parser/representer, eg:

from emmett.orm.engines import adapters
from emmett.orm.engines.postgres import (
    PostgresAdapter, 
    JSONBPostgreParser,
    JSONBPostgreRepresenter,
    parse_type,
    repr_type
)

class PlainBlobParser(JSONBPostgreParser):
    @parse_type('blob')
    def _blob(self, value):
        return value

class PlainBlobRepresenter(JSONBPostgreRepresenter):
    @repr_type('blob')
    def _blob(self, value):
        return value

@adapters.register('postgres')
class PlainBlobPostgresAdapter(PostgresAdapter):
    def _load_dependencies(self):
        super()._load_dependencies()
        self.parser = PlainBlobParser(self)
        self.representer = PlainBlobRepresenter(self)

I know it's not super fancy, eventually for 2.6 I came up with a setting in Database config to do this.

robinvandernoord commented 1 year ago

Thanks for the solution, I'll try this soon!