Created
December 3, 2013 22:26
-
-
Save gavinwahl/7778717 to your computer and use it in GitHub Desktop.
Abstract (PEP 3119) Django models.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABCMeta, abstractmethod | |
class AbstractModelMeta(ABCMeta, type(models.Model)): | |
pass | |
class ABCModel(models.Model): | |
__metaclass__ = AbstractModelMeta | |
class Meta: | |
abstract = True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Foo(ABCModel): | |
class Meta: | |
abstract = True | |
@abstractmethod | |
def foo(self): | |
return 1 | |
class Bar(Foo): | |
def foo(self): | |
return 2 | |
# >>> Foo() | |
# Traceback (most recent call last): | |
# File "<console>", line 1, in <module> | |
# TypeError: Can't instantiate abstract class Foo with abstract methods foo | |
# >>> Bar() | |
# <Bar: Bar object> |
Hello Thanks for your post.
I am not a specialist of metaclasses but I have to define abstract Django Models as AbstractModelMeta. And I also need to modifiy some fields for each model subclasses.
I tried to use init_subclass but there is a bug in django which make this solution unusable https://code.djangoproject.com/ticket/34555 .
SO I guess the solution is to redefine new but I have been turning into circles for several days and did not find any solution.
If I overwrite new in ABCModel then I get builtins.TypeError: object.new() takes exactly one argument (the type to instantiate).
When I check the mro of the ABCModel the base.Model is second in the list.
Any help would me most welcome!....
Here is a test
import unittest
import os
from unittest.mock import patch
from abc import abstractmethod, ABCMeta
from django.db.models.base import Model
import django.db.models as models
from django.apps.registry import Apps
class AbstractModelMeta(ABCMeta, type(models.Model)):
""" a Django model and python abstract class"""
pass
#def __new__(cls, name, bases, attrs, **kwargs):
#clas = super().__new__(cls, name, bases, attrs, **kwargs)
#return clas
class Test_AbstractImplementation (unittest.TestCase):
@classmethod
def setUpClass(cls):
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoLudd21.settings'
cls.patcher = patch.object(Apps, 'check_apps_ready')
cls.patcher.start()
class AbsImplementation (models.Model,
metaclass=AbstractModelMeta):
class Meta:
app_label = 'fakeapp'
abstract = True
field1 = models.CharField(
max_length=20)
field2 = models.IntegerField(
default=5)
@staticmethod
def change_field():
return 10
@abstractmethod
def absmethod(self):
pass
def __new__(cls, name, bases, attrs, **kwargs):
field1 = models.CharField(
max_length=cls.change_field(),
)
attrs.update({'field1': field1})
machine = kwargs.pop('machine', None)
if not machine:
raise ValueError('implementation must have a machine attribute')
clas = super().__new__(cls, name, bases, attrs, **kwargs)
setattr(clas, 'machine', machine)
return clas
cls.absimp = AbsImplementation
def test(self):
class Imp (metaclass=self.absimp,
machine='machine'):
class Meta:
app_label = 'fakeapp'
abstract = False
def absmethod(self):
pass
self.assertTrue(issubclass(Imp, models.Model))
self.assertTrue(issubclass(Imp, self.absimp))
self.assertEqual(Imp.machine, 'machine')
field1 = Imp._meta.get_field('field1')
field2 = Imp._meta.get_field('field2')
self.assertEqual(field1.max_length, 10)
self.assertEqual(field2.default, 5)
@unittest.skip
def test_missing_method(self):
with self.assertRaises(TypeError) as e:
class Imp1 (self.absimp,
machine='machine'):
pass
class Meta:
app_label = 'fakeapp'
abstract = False
ex = e.exception
self.assertTrue(isinstance(ex, TypeError))
self.assertTrue('absmethod' in ex.args[0])
@classmethod
def tearDownClass(cls):
cls.patcher.stop()
```
`
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks! This solution passing the DRY principle, and I can refactor a lot of repeated code lines.