How to go from a Model base to derived class in Django? - 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)

Related

Django: Override model methods from abstract base class models

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.

Converting attributes of class to a django models subclass

I have a bunch of classes that I'm now trying to incorporate into django.
For example, I have a Base class that all my other classes derive from:
class Base:
def __init__(self, label: str = 'Base'):
self.label = label
An example of a sublcass would be a Person class:
from typing import Any, Dict
class Person(Base):
def __init__(self, name: str, attributes_to_options: Dict[str, Any], **kwargs):
super().__init__(**kwargs)
self.name = name
self.attributes_to_options = attributes_to_options
I would use this as:
alex = Person(name='Alex', attributes_to_options={'age': 10, 'is_happy': True}, label='Person:Alex')
My question is, how do I incorporate such a class into django? Is it as simple as inheritting from models.Model? e.g.
from django.db import models
class Person(Base, models.Model):
def __init__(self, name: str, attributes_to_options: Dict[str, Any], **kwargs):
super().__init__(**kwargs)
self.name = name
self.attributes_to_options = attributes_to_options
But then how do I specify the models.CharField for the two attributes name and attributes_to_options?
Thanks for any help here.
Bear in mind that in general, any Django Model subclass corresponds to a database table. Inheriting from such a class ("concrete inheritance") means that another database table will be created with a one-to-one linkage between rows, and that every query will implicitly perform a join in the DB. This is bad for performance. But for tables with not very many rows or for tables queried infrequently, you possibly don't need to care.
Django provides two special cases that can be defined via the Meta class in the Model subclass. The first is an "Abstract Base Class" which allow you to define a bunch of stuff which will be present in any derived model. IN the case of fields, they are "copied" into the class that inherits them, rather than having their own DB table. The second is a "Proxy" class, which allows you to place a new set of methods on top of an existing database table, and which goes some way towards allowing polymorphic models. Careful reading of the Django doc. is a good idea.
I have experimentally established that one can also use mix-in classes in the same way as one uses them with class-based views. In other words,
class ExtraMethodsMixin( object): # NB must inherit from object
# NB no model field definitions allowed here
#property
def something_or_other(self):
return something_based_on_model_fields_defined_elsewhere
#etc.
and then
class Foo( ExtraMethodsMixin, models.Model): # NB mixin goes first
# define names and fields that the ExtraMethodsMixin uses
# (and anything else that a Foo needs)
...
The one snag I have found is that migrations do remember the dependency of Foo on ExtraMethodsMixin and so it's a PITA should you desire to completely remove the mixin at a later date. However, you can stub it out to a single pass statement without any problems, so this is probably not a significant worry. My other worry is that this usage is completely undocumented (other than as standard Python), so it's just about possible that it's trampling on Django internals in some very subtle way I've not yet spotted. So I'm definitely a bit short of recommending this technique.

Is this a valid approach to sharing a function between Python classes?

My goal is to use the same function from multiple classes in Python.
I've seen discussion about mixins and inheritance etc but they all seem to come with caveats and cautions about doing things just right.
So I wondered if I could just call another plain old function that lives outsides the classes. It seems to work, but maybe I'm failing to understand something important?
So my question is - is this a valid approach to sharing a function between Python classes?
def clean_reference_url(self):
if not self.cleaned_data['reference_url']:
return ''
try:
if 'illegaltext' in self.cleaned_data['reference_url']:
raise Exception
except:
raise forms.ValidationError('You cannot put illegaltext in this field.')
return self.cleaned_data['reference_url']
class ContactForm(forms.ModelForm):
def clean_reference_url(self):
return clean_reference_url(self)
class TripForm(forms.ModelForm):
def clean_reference_url(self):
return clean_reference_url(self)
It's valid, but it's unnecessary to have the extra layer of wrapping. The mix-in approach is the simplest, but yes, it has some caveats (largely related to metaclasses), so if you want to avoid that, you can still set a method in multiple classes by just setting during the definition of each class. Keep the function definition the same, and change the classes to:
class ContactForm(forms.ModelForm):
clean_reference_url = clean_reference_url
class TripForm(forms.ModelForm):
clean_reference_url = clean_reference_url
Again, a mixin is even cleaner, e.g.:
class CleanableUrl: # Change name as appropriate
def clean_reference_url(self):
# ...
class ContactForm(CleanableUrl, forms.ModelForm):
# No need to talk about clean_reference_url at all
class TripForm(CleanableUrl, forms.ModelForm):
# No need to talk about clean_reference_url at all
and it's usually the most Pythonic approach, assuming it works for your scenario (no conflicting metaclasses on the base types).

Transient use of Django Model instances

I have a use case where a particular class can either be transient or persistent. Transient instances are build from a JSON payload on a PUT call, and may either be persisted to the database or used during the server call and then either returned or discarded. What is best practice for this case? My options seem to be:
Write two classes, one of which is a models.Model subclass, and the other of which isn't, and make them implement the same API, or
Use the Model subclass, but be careful not to call save().
Is either of these preferable, according to conventional use of Django models?
You'll need both:
abstract = True is useful if inheritants still should be concrete models, so that no table should be created just for the parent class. It allows you to opt out of multi-table inheritance, and instead have the shared attributes duplicated to inheritants tables instead (abstract base inheritance).
managed = False is useful if the inheriting class should never be persisted at all. Django migrations and fixtures won't generate any database table for this.
class TransientModel(models.Model):
"""Inherit from this class to use django constructors and serialization but no database management"""
def save(*args, **kwargs):
pass # avoid exceptions if called
class Meta:
abstract = True # no table for this class
managed = False # no database management
class Brutto(TransientModel):
"""This is not persisted. No table app_brutto"""
#do more things here
pass
In order to remain as DRY as possible, you could have an abstract mock class deriving your model:
class A(models.Model):
# fields'n'stuff
class TransientA(A):
def save(*args, **kwargs):
pass # avoid exceptions if called
class Meta:
abstract = True # no table created
Now, even if you call save on it anywhere (even in methods inherited from A), you'll be shooting blanks.

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?