Search code examples
pythonooppeeweeflask-peewee

is there a way to set peewee fields via a mixin?


I'm trying to set up a reusable set of data models which I can include in multiple apps, something like this (I'm using users as an example here, but the actual one is a peewee backend for the Authlib library):

# mixins.py

class UserMixin(peewee.Model):
    username = peewee.CharField()
    password = peewee.CharField()

    def set_password(self):
        # do stuff
    ...

Once that mixin's created, I should be able to import it like this, defining only the additional fields (the defaults will already be there from the mixin)

# models.py

db = peewee.SqliteDatabase(config.get('DATABASE_FILE'))

class BaseModel(peewee.model):
    class Meta:
        database = db

class User(BaseModel, UserMixin):
    email = peewee.CharField()
    ...

I've seen people do this with SQLAlchemy, but when I use this strategy with peewee it doesn't seem to save the fields properly:

  • if UserMixin inherits from peewee.Model, it says "unable to resolve import hierarchy" (probably since we're importing from peewee.Model multiple times)
  • if UserMixin is just an object, then peewee doesn't seem to handle its fields properly: they all end up as unbound instances and don't get saved in the database.

My question: is there an "official way" to create reusable model mixins with fields in peewee?

I've seen other projects (such as flask-login) use mixins, but those are generally additional functions like set_password in this example, and not ones that define the fields themselves.


I have a few potential alternate solutions, like

  • Define the models themselves, rather than mixins, in the shared file, and override their .Meta.database separately for each models.py entry
  • Define only the other functions in the mixin; let the fields be defined separately each time in models.py
  • Use the shared code as a file to copy-paste from rather than importing directly.

But there's probably some cleaner way of doing this?


Solution

  • Here's a simple example:

    from peewee import *
    
    db = SqliteDatabase(':memory:')
    
    class Base(Model):
        class Meta:
            database = db
    
    
    class UserModelMixin(Model):
        username = TextField()
    
    class User(UserModelMixin, Base):
        pass
    
    print(User._meta.fields)
    #{'id': <AutoField: User.id>, 'username': <TextField: User.username>}
    

    I think the problem was the ordering of your mixins.