Question

I am trying to create mixins that would represent commonly used fields within a database. In this example, it is extremely common for a database table to track when the record was created, who created it, when it was edited, and who edited it. To do this I have created the following mixins:

[project]/libs/mixins/CreationAuditMixin.py:

from django.db import models

class CreationAuditMixin(models.Model):
    """ CreationAuditMixin documentation
    """
    created_at = models.DateTimeField(auto_now_add=True)
    created_by = models.CharField(max_length=255, blank=True, editable=False)

    class Meta:
        abstract = True

[project]/libs/mixins/ModificationAuditMixin.py:

from django.db import models

class ModificationAuditMixin(models.Model):
    """ ModificationAuditMixin documentation
    """
    modified_at = models.DateTimeField(auto_now=True)
    modified_by = models.CharField(max_length=255, blank=True, editable=False)

    class Meta:
        abstract = True

[project]/libs/mixins/FullAuditMixin.py (just for convenience):

from django.db import models

from libs.mixins.audit import CreationAuditMixin, ModificationAuditMixin

class FullAuditMixin(models.Model, CreationAuditMixin, ModificationAuditMixin):

    class Meta:
        abstract = True

[project]/libs/models/questions/Context.py:

from django.db import models

from libs.mixins.audit import FullAuditMixin, CreationAuditMixin, ModificationAuditMixin

_app_label = 'questions'
_db_table = '\".\"'.join((_app_label, 'context'))

class Context(models.Model, FullAuditMixin):
    """ Model representing a Context
    """
    _format_string = "{}: {}, {} - {}"

    name = models.CharField(max_length=255)
    description = models.CharField(max_length=2000)
    publish_start = models.DateTimeField()
    publish_end = models.DateTimeField()

    def __unicode__(self):
        """ Returns a unicode representation of the model
        """
        result = self._format_string.format(self.name, self.description, self.publish_start, self.publish_end)
        return result

    class Meta:
        app_label = _app_label
        db_table = _db_table

I may move the models into their specific app file structure at a later time but for now there is already manual configuration around schema/app_label/db_table so this structure does not contribute additional configuration.

When attempting anything that imports Context (most manage commands, in this case, attempting a migration) I receive the following error:

./manage.py schemamigration questions --initial
Traceback (most recent call last):
  File "./manage.py", line 20, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 392, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 242, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 284, in execute
    self.validate()
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 310, in validate
    num_errors = get_validation_errors(s, app)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/validation.py", line 34, in get_validation_errors
    for (app_name, error) in get_app_errors().items():
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 196, in get_app_errors
    self._populate()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 75, in _populate
    self.load_app(app_name, True)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 99, in load_app
    models = import_module('%s.models' % app_name)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/importlib.py", line 40, in import_module
    __import__(name)
  File "./apps/questions/models.py", line 1, in <module>
    from libs.models.questions import Context
  File "./libs/models/questions/Context.py", line 3, in <module>
    from libs.mixins.audit import FullAuditMixin, CreationAuditMixin, EditAuditMixin
  File "./libs/mixins/audit/FullAuditMixin.py", line 5, in <module>
    class FullAuditMixin(models.Model, CreationAuditMixin, EditAuditMixin):
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 79, in __new__
    new_class = super_new(cls, name, bases, {'__module__': module})
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

When I built these mixins, I had already consulted Django: Creating a Mixin for Reusable Model Fields and http://www.djangocurrent.com/2011/05/django-aspect-oriented-models.html, my approach was based on reading these. Any ideas on how I can properly structure these mixins and the cause of this error would be appreciated.

Was it helpful?

Solution

Context should not inherit from models.Model, as it already has that in the inheritance chain from the mixins themselves. You may also need to remove models.Model from FullAuditMixin, for the same reason.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top