I am trying to use an abstract base class model in Django to avoid writing some duplicate code and encountering some unexpected behavior.
Here is simplified version of my abstract base class:
class AbstractDocument(models.Model):
notes = models.CharField(max_length=255)
document = models.FileField(upload_to=document_file_path)
def document_file_path(instance, filename):
pass
class Meta:
abstract = True
I need to define the method document_file_path or the code generates error. I want to define different behavior in the subclasses for the document_file_path. Below is an example:
class BookDocument(AbstractDocument):
book = models.ForeignKey(Book, on_delete=models.CASCADE)
def document_file_path(instance, filename):
return f'books/{filename}'
It does not appear that the child method is overriding the parent method because I get an error that document_file_path returned NoneType when I run the code above. I tried making the method in AbstractDocument return an actual path, but the child method doesn't override the parent in that scenario either.
Is there some reason why what I'm trying to do is not possible? Is there something I'm missing in the implementation? Is there another or better way to accomplish this?
Field is a class attribute, not instance, but you're trying to link it to an instance's method which is not really possible/correct.
So try marking your method as a #staticmethod or switch to a solution similar to proposed in the second answer here: make a "simple" function calling instance's (which is the first argument in upload_to) method, which can be inherited and overridden.
I'm trying to create classmethod constructors for a child class, but I cannot initialize the instance properly.
I have read many blogs and answers on this site and even tried exactly what some other people have posted, still to no avail. Hopefully I'm missing something really simple. Basic example of what I'm trying:
class A(object):
def __init__(self, foo):
self.foo = foo
class B(A):
#classmethod
def make_new(cls):
super(B, cls).__init__('bar')
foobar = B.make_new()
I keep getting unbound method error:
TypeError: unbound method __init__() must be called with B instance as first argument (got str instance instead)
The __init__ method is a regular instance method, not a class method. It needs the instance it is going to initialize to already have been created when it is called. Your current code is failing in exactly the same way that A.__init__("foo") would fail (it's not the fault of super).
I suspect you wanted to be calling __new__ rather than __init__. The __new__ method is the actual "constructor" method that is responsible for creating the instance (usually by delegating the actual creation step to object.__new__). You don't need to use super either, since you've not overridden the __new__ method that you inherited (from object, since A doesn't override it either).
But you don't actually need to do that either. You can just call the cls argument your classmethod been passed. Calling a class is the normal way to construct instances:
class B(A):
#classmethod
def make_new(cls):
return cls("bar") # note that you also need to `return` the instance you create!
If the purpose of the class method is to avoid running B.__init__, you might want something like:
class B(A):
#classmethod
def make_new(cls):
self = cls.__new__(cls)
super(B, self).__init__('bar')
return self
I'm relatively new to python and django and I'm just trying to get my head around method overrides and how that effects the way methods work in the parent class.
So I have this parent class a lá:
class Parent():
def save(self):
if not self.slug:
self.slug = self.get_slug()
def get_slug(self):
return slugify(self)
and the child class in which I'm going to override the get_slug method as I want to use slugify with its own unicode method (am I right that by calling slugify(self) that he automatically uses the unicode method?).
class Child(Parent):
def __unicode__(self):
return u'%s %s' %(self.title, self.season)
def get_slug(self):
return slugify(self)
What I'm not sure about is how the save method works for a instance of child.
After I created a new child instance (for example in the admin area) does the inherited save method just call the get_slug method in the parent class?
I thought that save would call my overriden get_slug method instead of the original one.
Perhaps it's totally obvious but I don't seem to understand it.
So when I use child.slug in my template I get the outcome of the parent get_slug method.
Only if I use child.get_slug I can get the right outcome.
Could someone explain me how inherited methods interact with overriden methods in a child class.
Thanks in advance
Assuming a simple set of inherited Model classes, like this:
class BaseObject(models.Model):
some_field = models.SomeField(...)
class AwesomeObject(BaseObject):
awesome_field = models.AwesomeField(...)
class ExcellentObject(BaseObject):
excellent_field = models.ExcellentField(...)
and a query that looks like this:
found_objects = BaseObject.objects.filter(some_field='bogus')
What's the best way to take each found object and turn it back into it's derived class? The code I'm using now is like this:
for found in found_objects:
if hasattr(found, 'awesomeobject'):
ProcessAwesome(found.awesomeobject)
elif hasattr(found, 'excellentobject'):
ProcessExcellent(found.excellentobject):
But, it feels like this is an abuse of "hasattr". Is there a better way to do this without creating an explicit "type" field on the base class?
For this specific problem, there is django-polymorphic. It works by using the content type framework in Django to store the model ID which the derived table points to. When you evaluate the queryset, it will upcast all models their specific type.
You'll get:
>>> BaseProject.objects.all()
[ <AwesomeObject>, <ExcellentObject>, <BaseObject>, <AwesomeObject> ]
That's the best way that I know of. Unfortunately, inheritance is a little clunky in this regard. Multiple table inheritance is basically just a one-to-one relationship between the parent model and the extra fields the child adds, which is why that hasattr trick works. You can think of each of those as a OneToOneField attribute on your parent model. When you think of it that way, Django has no way of knowing which child to return or even if to return a child, so you have to handle that logic yourself:
I tend to create a method on the parent such as get_child, which simply cycles through the attributes and returns the one that pops:
class BaseObject(models.Model):
some_field = models.SomeField(...)
def get_child(self):
if hasattr(self, 'awesomeobject'):
return ProcessAwesome(found.awesomeobject)
elif hasattr(self, 'excellentobject'):
return ProcessExcellent(found.excellentobject):
else:
return None
At least then, you can just call found.get_child(), and maybe forget about the hackery that gets you there.
Going from a base class to a derived class is generally a sign of bad design in a program. The method you propose, using hasattr, can be a serious problem. I'll show you:
# defined in some open source library
class MyObject(object):
def what_is_derived(self):
if hasattr(self, 'derived1'):
return 'derived1'
elif hasattr(self, 'derived2'):
return 'derived2'
else:
return 'base'
Let's pretend that classes Derived1 and Derived2 are defined in that same library. Now, you want to use the features of MyObject, so you derive from it in your own code.
# defined in your own code
class MyBetterObject(MyObject):
pass
better_object = MyBetterObject()
better_object.what_is_derived() # prints 'base'
The whole point of polymorphism is that you can have many derived classes without the base class having to change. By making the base class aware of all of it's derived classes, you severely reduce the usefulness of such a class. You can't create a derived class without changing the base class.
Either you want to work with a derived class, or you don't care what the specific class is and all you need are the properties/methods of the base class. It is the same in all OOP languages. There are facilities for finding out what the derived class is, but usually it's a bad idea.
From a django models perspective, I usually use inheritance in such a way:
class Address(models.Model):
# fields...
class Person(Address):
# fields...
class Business(Address):
# fields...
Address.objects.all() # find all addresses for whatever reason
Person.objects.all() # im only interested in people
Business.objects.all() # need to work with businesses
# need to show all addresses in a postcode, and what type of address they are?
businesses = Business.objects.filter(postcode='90210')
people = Person.objects.filter(postcode='90210')
# use the address properties on both
Deeply nested inheritance chains with django models are awkward. They are also pretty unnecessary in most cases. Instead of polluting your base class with hasattr checks, define a helper method which is capable of querying the required derived classes if such a thing is called for. Just don't define it on the Base class.
I use introspection ;
class Base(models.Model):
[ we have some unique 'key' attribute ]
class_name = models.CharField(..., editable=False)
def get_base(self):
if self.__class__ == Base:
return self
# if we are not an instance of Base we 'go up'
return Base.objects.get(key=self.key)
def get_specific(self):
if self.__class__ != Base:
return self
# if we are an instance of Base we find the specific class
class_type = getattr(sys.modules["project.app.models"],
self.class_name)
return class_type.objects.get(key=self.key)
You need some factory to create the specific classes so you are sure to correctly save str(self.class) in class_name
You can also use InheritanceQuerySet from django-model-utils in case you want to explicitly state which queries to affect, like this:
from model_utils.managers import InheritanceQuerySet
class UserManager([...]):
def get_queryset(self):
return InheritanceQuerySet(self.model).select_subclasses()
(code from https://stackoverflow.com/a/25108201)
In django, I would like to reference the class whose method is being called, where the method itself is implemented in its abstract ancestor.
class AbstractFather(models.Model):
class Meta:
abstract = True
def my_method(self):
# >>> Here <<<
class Child(AbstractFather):
pass
I'm looking to do something like:
isinstance(instance, Child):
Of course I can't know within my_method which child Model was called a priori.
Trivial and works:
class AbstractFather(models.Model):
class Meta:
abstract = True
def my_method(self,some_instance):
print isinstance(some_instance,self.__class__)
class Child(AbstractFather):
pass
Why do you say that? You absolutely can. AbstractFather is an abstract model, so it will never be instantiated, so you can always be sure that whatever's calling my_method is an instance of a subclass. The syntax you give should work.
Edit So what exactly are you trying to compare against? self in my_method will always be the relevant instance, and its class will always be the specific subclass of AbstractFather. What do you need to check?