I just wanted to give you a shout-out. Your comment really made something click for me. What I was doing in my first post sort of gets me a few steps towards what I'm trying to do. (Side note, I'm not really building a specific app at the moment. I'm building a fairly opinionated framework for making data science/analytics apps. So the over-planning trap is real, but also expected).
When I thought about your comment, I realized that I don't need or want a UserService and a CommentService and a GroupService, etc. etc. with each getting CRUD and Form functionality from mixins. The mixin approach is basically just shifting my veggies around the plate. It surfaces and separates the operations on models . . . a little. The methods are still really tightly coupled to classes, and for no very good reason at all. What I really want is a generic CRUD service, a Form service, a Login (or maybe it's Auth) service, etc. Basically, the service doesn't care about the model until you tell it to. And because services are stateless as you said, you can call them from one another. Like the CRUD service can use the Form service for populating data. I really like where this is going. So, thanks again for popping in with your thought. It really nudged me in a good direction. On Saturday, March 23, 2019 at 1:55:34 PM UTC-5, Mike Bayer wrote: > > 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 <agma...@gmail.com > <javascript:>> 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+...@googlegroups.com <javascript:>. > > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > > 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.