On Wed, Jul 3, 2019, at 4:47 AM, Simon King wrote: > 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?
oh, whoops sorry it does not :) just call configure_mappers() :) > > 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. > -- 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/c2e6d8b4-e508-464c-af2f-f58e983a01f7%40www.fastmail.com. For more options, visit https://groups.google.com/d/optout.