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 [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to