Django: Override model methods from abstract base class models - django

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.

Related

correct use of class method in django

I had studied class methods in python but never really understood its application in Djnago. My coding life was going well without the use of it. But I came across a situation where class method will be useful.
My model:
class Example(models.Model):
post_count = models.IntegerField(default=0)
#classmethod
def total_counts(cls):
return cls.objects.values('post_views').annotate(sum logic)
In the above function, if I had used property decorator, i can only get a single object post_count because the object self is used. But if I use classmethod then I can count the post views of all the objects.
So my thought is that whenever we have to deal with all the objects instead of a self object we need class method. Is this correct?
Also, can we use this class method directly into our model serializer field just like we use property to serializer??

django: Downcasting automatically in multitable inheritance/polymorphic tables

I have something like this:
class Base(Model):
...
def downcast(self):
try:
return self.childa
except:
pass
try:
return self.childb
except:
pass
return self
class ChildA(Base):
....
class ChildB(Base):
....
class Foo(Model):
child = ForeignKey(Base)
Whenever I have a Foo object, the child foreignkey is always an instance of Base - my understanding is that's how Django works. For now I have added a downcast() method to Base (see above). I don't mind hardcoding the possible derived types.
What I would like is to somehow centralize that downcast automatically in Foo. I added this multi-table inheritance to existing code and I keep finding instances where the code really needs it downcast -- so I have to then manually downcast it locally in the code.
I was using the django-polymorphic package, but it is giving me some side effects I don't know how/nor want to deal with (like I can't delete rows - got some error about opts.pk being None deep in queryset code.)
So I've wondered -- would putting something in __init__() (after calling the base class init) be ok? Are there side effects I'm not thinking of? This seems like it could be a problem when creating new instances from scratch.
def __init__(*args, **kwargs):
super(Base, self).__init__(*arg, **kwargs)
self.child = self.child.downcast()
Should I just rename child?
class Foo(Model):
child_poly = ForeignKey(Base) # was child
#property
def child(self):
return self.child_poly.downcast()
This could be a problem when creating Foo() from scratch. I can't say Foo(child=c).
Is there a better approach? Not looking for a generic polymorphic solution/mixin -- not after trying to debug django and finding that removing django-polymorphic fixed the deletion issue.
In the end, I went back to django-polymorphic and haven't had the issue I was having before again.

reason to subclass your own resouce for tastypie

I've seen test codes where custom resources are further subclassed.
But it was for the test sake, not for any real use.
Why would anyone want to subclass a resource for obvious reason(but apparently not to me)?
An example would suffice.
I just ran into a situation where I need to subclass a resource.
I have a django model that has several subclasses like so (I've simplified it a lot):
class Information(models.Model):
name = models.CharField)
class Question(Information):
question = models.CharField)
class Link(Information):
url = models.CharField()
I want an API for accessing all of the Information objects, but still want to be able to access all the subclass specific attributes for each subclass. In this case I'd want to have 3 resources:
class InformationResource(ModelResource):
class Meta:
queryset = Information.objects.all()
def dehydrate(self, bundle):
'''Add some extra meta data here'''
class QuestionResource(InformationResource):
class Meta:
queryset = Question.objects.all()
class LinkResource(InformationResource):
class Meta:
queryset = Link.objects.all()
This way I have an api for returning all Information objects (as well as the attributes that are common to all of them), but if I need to access subclass specific attributes I can access them through their subclass resource APIs. Furthermore, these subclassed Resources will have access to all of the methods defined for their superclass. For instance, they can override the superclass' dehydrate method to add more fields.

How to go from a Model base to derived class in Django?

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)

Django - Introspect django Model in a method implemented in its abstract ancestor?

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?