w1nter
w1nter

Reputation: 47

SQLAlchemy - mask values in objects on the fly

I have the following SQLAlchemy class defined:

Base = sqlalchemy.ext.declarative.declarative_base()
class NSASecrets(Base):
  __tablename__ = 'nsasecrets';
  id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True);
  text = sqlalchemy.Column(sqlalchemy.String);
  author = sqlalchemy.Column(sqlalchemy.String);

Now what I want to do is to be able to mask "author" field depending on some logic, something like:

if (allowed):
  nsasecrets = session.query(NSASecrets,**mask=False**);
else:
  nsasecrets = session.query(NSASecrets,**mask=True**);
for nsasecret in nsasecrets:
  print '{0} {1}'.format(author, text);

So depending on this "mask" parameter I would like output to be "John Smith" in False case - output not masked, or "J*** **h" when output is masked. Now obviously I could do it in this very print, but problem is that prints are scattered around the code and the only way I see to do this in controlled centralized manner is to create SQLAlchemy objects with already masked values. So is there any well known solution to this? Or should I just create my own session manager that would overload "query" interface or am I missing some other possible solutions to this?

Thanks

Upvotes: 2

Views: 1175

Answers (1)

zzzeek
zzzeek

Reputation: 75217

this is typically the kind of thing in Python we do with something called descriptors. A simple way to combine descriptors with SQLAlchemy mapped columns is to use the synonym, though synonym is a bit dated at this point, in favor of a less "magic" system called hybrids. Either can be used here, below is an example of a hybrid:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, synonym_for
from sqlalchemy.ext.hybrid import hybrid_property

Base = declarative_base()

class NSASecrets(Base):
    __tablename__ = 'nsasecrets'

    id = Column(Integer, primary_key=True)
    _text = Column("text", String)
    _author = Column("author", String)

    def _obfuscate(self, value):
        return "%s%s" % (value[0], ("*" * (len(value) - 2)))

    @hybrid_property
    def text(self):
        return self._obfuscate(self._text)

    @text.setter
    def text(self, value):
        self._text = value

    @text.expression
    def text(cls):
        return cls._text

    @hybrid_property
    def author(self):
        return self._obfuscate(self._author)

    @author.setter
    def author(self, value):
        self._author = value

    @author.expression
    def author(cls):
        return cls._author

n1 = NSASecrets(text='some text', author="some author")

print n1.text
print n1.author

note that this doesn't have much to do with querying. The idea of formatting the data as it arrives in a rowset is a different way to go, and there's some ways to make that happen too, though if you're only concerned about print statements that refer to "text" and "author", it's likely more convenient to keep that as a python access pattern.

Upvotes: 2

Related Questions