On Wed, Jul 3, 2019 at 3:46 AM gamcil <clmgilchr...@gmail.com> wrote: > > > Awesome thanks for the extensive reply. > > This is the first time I've played with descriptor classes, so I'm sorry if > this is really basic stuff. > > I set up my view class: > > class View: > def __init__(self, gene, attrs): > self.gene = gene > self.attrs = attrs > > def keys(self): > return iter(self.attrs) > > def items(self): > return ((attr, getattr(self.gene, attr)) for attr in self.attrs) > > def __getitem__(self, key): > return getattr(self.gene, key) > > def __setitem__(self, key, value): > setattr(self.gene, key, value) > > and a descriptor class: > class Descriptor: > def __init__(self, *attrs): > self.attrs = attrs > > def __get__(self, instance, owner): > return View(instance, self.attrs) > > def __set__(self, instance, value): > self.attrs = value > > then the Gene class using __declare_first__ becomes: > class Gene(Base): > __tablename__ = 'Gene' > id = Column(Integer, primary_key=True) > identifiers = Descriptor('locus', 'protein') > features = Descriptor('gene', 'mRNA', 'CDS') > > def __init__(self, **kwargs): > for dic in kwargs.values(): > for key, value in dic.items(): > setattr(self, key, value) > > @classmethod > def __declare_first__(cls): > for value in list(cls.__dict__.values()).copy(): > if isinstance(value, Descriptor): > for attr in value.attrs: > setattr(cls, key, Column(attr, String)) > > This seems to give me close to the interface I would like. A couple of > questions: > 1) So I guess this is still just directly mapping columns to Class > attributes, accessable by directly getting an attribute, i.e. > gene.features['mRNA'] is equivalent to gene.mRNA. Would you then mask the > class attributes by e.g. prepending with underscore?
That's entirely up to you. It's not *necessary* (no harm will be done if you access the data through both mechanisms, since the value is only stored in a single place), so it's just an aesthetic choice. > > 2) Currently, every time a method is called on the descriptor, a new instance > of the View class is returned. Is that by design, or is there a way of > persisting the one View per attribute (identifiers, features, ...) for each > Gene instance? Is that even an overhead I should be worried about? It seems > like the equivalent of creating a new dictionary every time I want to access > attributes on the Gene object. This is probably a case of "don't bother optimising until you know it's a problem", but in this case the optimisation is simple. You could change your Descriptor.__get__ method to store the View instance on the object itself, perhaps under a name derived from the list of attributes. For example: def __get__(self, instance, owner): cachename = '_descriptor_' + '_'.join(self.attrs) result = getattr(instance, cachename, None) if result is None: result = View(instance, self.attrs) setattr(instance, cachename, result) return result > > 3) When I call Base.metadata.create_all(bind=engine) without first creating a > Gene instance, the generated SQL is: > CREATE TABLE "gene" ( > id INTEGER NOT NULL, > PRIMARY KEY (id) > ) > > missing all of the other mappings. After calling e.g. gene = Gene(), it's as > expected: > CREATE TABLE "gene" ( > id INTEGER NOT NULL, > locus VARCHAR, > protein VARCHAR, > gene VARCHAR, > "mRNA" VARCHAR, > "CDS" VARCHAR, > PRIMARY KEY (id) > ) > This seems like a bug; I would have expected the __declare_first__ method to be called automatically when you call create_all. As a workaround, does it make any difference if you add a call to sqlalchemy.orm.configure_mappers() before your call to create_all? Simon -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. To view this discussion on the web visit https://groups.google.com/d/msgid/sqlalchemy/CAFHwexdP95%2BWy5-8OjqhoQcYcs8jRT9RFp-G-SCY9fwwO5UoHg%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.