Issue: a
Author: efake2002
State: closed
Created: 2019-02-24 04:05:30


I think that I have a solution to your problem. But I warn you, I'm quite a inexperienced programmer and this is monkey patching (I know, I know, shame on me, but it works!). So take this solution with a grain of salt.

I have implemented this months ago, so I don't really know what it is doing under the hood anymore. The trick is that I have implemented a helper called `metaclass_resolver` to get rid of the `metaclass conflict` and inherit from N metaclasses with no problems.

# The code

```
def metaclass_resolver(*classes):
    metaclass = tuple(set(type(cls) for cls in classes))
    metaclass = (
        metaclass[0]
        if len(metaclass) == 1
        else type("_".join(mcls.__name__ for mcls in metaclass), metaclass, {})
    )  # class M_C
    return metaclass("_".join(cls.__name__ for cls in classes), classes, {})
```

### Usage

```
Python 3.7.2 (default, Dec 26 2018, 08:50:25) 
[GCC 6.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from util.metaclass_resolver import metaclass_resolver
>>> from peewee import *
>>> 
>>> from abc import ABC
>>> 
>>> class dummy(metaclass_resolver(ABC, Model)): pass
...
>>> issubclass(dummy, ABC)
True
>>> issubclass(dummy, Model)
True
```

# Real Usage Case

To show that this (somewhat) works, I will share how this is working in my current system.

Every table `table_name` on my database has a sibling called `table_name_history` to keep track of all the changes on the table. To do this, I'm using a simple inheritance to create the columns:

```
class BaseModel(Model):
     ...

class BaseHistory(Model):
    ...

class Dummy(BaseModel):
    dummy_field = DummyPeeweeField()

class DummyHistory(Dummy, BaseHistory):
    dummy = ForeingKeyField(Dummy)
```

The problem was that I couldn't inherit unique fields, as they would need to be not unique in the history table.

I resorted to monkey patching a metaclass to remove the unique constraint when the class was beeing loaded.

```
class UniqueFieldFixMeta(type):
    def __init__(cls, name, bases, clsdict):
        if (
            name != "UniqueFieldFix"
            and name != "Model_UniqueFieldFix"
            and name != "BaseHistory"
        ):
            print("Fixing history unique columns for")
            print(cls)
            for key, value in cls._meta.fields.items():
                value.unique = False
        super(UniqueFieldFixMeta, cls).__init__(name, bases, clsdict)


class UniqueFieldFix(metaclass=UniqueFieldFixMeta):
    pass
```

Now, the `BaseHistory` looks like this:

```
class BaseHistory(metaclass_resolver(Model, UniqueFieldFix)):
    ...
```

And it works wonderfully.

### Please, More Salt Sir

Again, I don't really know if this is what you are looking for. If it is, you need to be comfortable with some monkey patching. I'm just a script kid.

a

What happened to the issue? @efake2002

The original comment:

-------------------------------

Hi.

First of all, thanks for this amazing library. It's helped and simplified our work a lot.

This is closely related to #1722 but not quite the same.

In #1722, @coleifer told us that he would not be implementing support for the abstract base class module that Python 3 provides built-in. I respect your decision to not implement it. It's your library and your personal choice.

Currently, if we try to inherit from both ABC and Peewee's Model class like so:

class AbstractModel(ABC, peewee.Model):
it throws this error:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Are there any work arounds for this on our side? My team really needs this functionality to define abstract models with the orm. We have a ton of models and we need some way to distinguish between the abstract and the concrete models we're creating. Therefore, is there any way to implement this on our side? What is the easiest way of doing this in relation to peewee?

I know that Django provides this functionality and I'd like to implement something similar as well. Something like https://docs.djangoproject.com/en/2.1/topics/db/models/#meta-inheritance would be amazing for us. Please let me know how we can accomplish this.

Some of my team members are considering moving to some other orm like Pony because of this issue, but we really love this library and would prefer to stay if there was some work around for this. Please let me know.

Thanks for your time and any help is much appreciated!

> Some of my team members are considering moving to some other orm like Pony because of this issue, but we really love this library and would prefer to stay if there was some work around for this. Please let me know.

I love it when people say that they're "thinking about switching to <other project>" to try and get my attention. By all means, have fun with a magical ORM that decompiles generator expression bytecode and is 99% magic!

(I know, everyone hates comments on closed issues, but)

If anyone gets directed here via Google, abstraction of peewee's Model class _is_ possible. You just have to use the `ABCMeta` class vs an `ABC` subclass.

So you can do something like this:

```
from abc import ABCMeta, abstractmethod
from peewee import Model

class BaseModel(Model):
    __metaclass__ = ABCMeta

    @abstractmethod
    def some_method(self):
        pass

     ...

class SecondBaseModel(BaseModel):
    __metaclass__ = ABCMeta

    @abstractmethod
    def some_other_method(self):
        pass 

    ...

# ^ etc

class Dummy(SecondBaseModel):
    dummy_field = DummyPeeweeField()

    def some_method(self):
        ...

    def some_other_method(self):
        ...

```

IDE's don't seem to like this (lol) and you may have to re-state the abstract functions down your sub classes. However, this definitely runs!

Edit: Formatting / grammar