Just FTR i was thinking about this Q today and I usually would opt to make a Service object stateless, and things like "request" and User would be passed into individual methods explicitly. I usually start application with a pattern that might be more verbose to start with, but once you write half a dozen cases front to back, the most appropriate pattern usually makes itself apparent and you then refactor.
e.g. don't overplan ahead, do it stupidly simple then expect to refactor a few times. but also don't create state where it isnt needed, if that makes sense. On Sat, Mar 23, 2019 at 11:24 AM Andrew Martin <agmar...@gmail.com> wrote: > > I just realized that almost everything about how I'm using mixins here is > pretty much wrong. This is probably a garbage question and can be deleted, > but I don't want to do it myself in case someone is writing a response to > tell me that. > > On Saturday, March 23, 2019 at 9:00:49 AM UTC-5, Andrew Martin wrote: >> >> I like to keep my models separate from actions on it, so I only use them for >> defining tables, relationships, and indexes. To perform actions on a model I >> use a service that inherits from the model and provides . . . well. >> services. It's an interface pattern. I'm making these more generic, and >> separating out repeated code into a mixin. It works fine, but I kind of hate >> the implementation because it feels wrong and fragile to me. I was wondering >> if anyone had some suggestions to improve how I'm doing this. >> >> Here are some examples. >> >> >> class User(Base): >> __tablename__ = 'users' >> __table_args__ = {'sqlite_autoincrement': True} >> >> # postgres implementation for later >> # user_id_seq = Sequence('user_id_seq', metadata=Base.metadata) >> # id = Column(BigInteger, user_id_seq, >> server_default=user_id_seq.next_value(), primary_key=True) >> id = Column(Integer, primary_key=True) >> resource_uid = Column(Text, nullable=False) >> username = Column(Text, nullable=False, unique=True) >> hashed_password = Column(Text, nullable=True) >> is_enabled = Column(Integer, default=1, nullable=False) >> >> >> class CRUDMixIn: >> def __init__(self): >> # super().__init__() >> print('initing crud mixin') >> # This assumes that there are only two MixIns used in the service in >> this order >> # e.g.: class XService(FormMixIn, CRUDMixIn, User): >> self.model = self.__class__.mro()[3] >> >> def get_one_by_id(self, id): >> one_row = >> self.request.dbsession.query(self.model).filter(self.model.id == id).first() >> return one >> >> def get_all(self): >> all_rows = self.request.dbsession.query(self.model).all() >> return all_rows >> >> >> class UserService(FormMixIn, CRUDMixIn, User): >> def __init__(self, request: Request): >> super().__init__() >> self.request = request >> >> # other user related methods and business logic >> >> >> >> What is obviously really gross about this is getting the class for the >> MixIn. Relying on the MRO means that anyone using it has to keep the same >> order, and that feels wrong. But it doesn't feel as wrong repeating a bunch >> of boilerplate CRUD code. I've looked at more than a few web/CRUD >> frameworks, and I don't see people doing things like this. Most often what I >> see is people putting generic CRUD functions in the Declarative Base, and I >> really don't like that coupling there. I'd much prefer to have the model >> layer separated from its actions. I had thought about setting the model in >> the UserService like this: >> >> class UserService(FormMixIn, CRUDMixIn, User): >> def __init__(self, request: Request): >> super().__init__() >> self.request = request >> self.model = User >> >> >> But that returns a <class 'sqlalchemy.ext.declarative.api.DeclarativeMeta'> >> instead of <class models.User>, so I still have to get to the MRO there to >> get the user model to query and it ends up being just as ugly. Although, I >> guess that's more stable than what I'm doing now because the model MRO isn't >> going to change often (or ever, maybe?). >> >> Anyway, I'm curious if anyone has thoughts about how I can make this better >> or less fragile. >> >> thanks! >> >> > -- > 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. > 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. For more options, visit https://groups.google.com/d/optout.