Django model inheritance - accessing the child attributes from the parent - python

I am trying to set-up some model inheritance in Django. I have three classes:
class Parent(Model):
name = TextField()
class Child1(Parent):
foo = FloatField()
class Child2(Parent):
bar = BoolField()
class RandomClass(Model):
myLink = ForignKey(Parent)
The problem is in my other models like RandomClass I can get instances of Parent but I cant access the variables specific to the child classes.
I could, of course, just have multiple nullable foreign keys in RandomClass then write a property 'wrapper' which works out which ForeignKey is non-null and returns that...but that's not very nice.
Is there a better way to do this?
Cheers,
Jack

Related

Class inheritance python

In the case creating a model, for example
class Student(models.Model)
name=models.charfield(),roll=models.integerfield()
similarly,
In the case creating a form, class newform(forms.Form)
name=forms.charfield(),roll=forms.integerfield()
similarly,
In the case creating a serializer, class serial(serializers.Serializer)
name=serializers.charfield(),roll=serializers.integerfield()
I understood that in each classes,a base class is inherited but i am confused that if different objects of different classes are created inside a class in each scenario then what is the meaning of inheriting models.model, forms.Form,serializers.Serializer what these inherited classes do?
Django uses inheritance as well as object composition which are techniques of OOP for reusability.
Let us take your first class as example (I have only kept one field for simplicity):
Student(models.Model):
name = models.CharField(max_length=100)
Inheritance:
The first line Student(model.Model): does inheritance by inheriting from Model class using which you are getting methods like save(), delete(), clean_fields e.t.c. Now your Student class can reuse those methods.
Composition
The second line name = models.CharField(max_length=100) does object composition by creating object namely name of class CharField using which you get methods like check, get_internal_type e.t.c.
All of those Inbuilt classes (Model, CharField e.t.c) are defined in file namely models.py so when you do models.Model you are getting Model class from file models.py and models.CharField gives you CharField class from same file.
By inheriting from other classes, you have access to their methods;
Class A(object):
def _print(self):
print('Class A')
Class B(A):
def other_print(self):
print('Class B')
if __name__ == "__main__":
a, b = A(), B()
a._print()
b._print()
b.other_print()
When inheriting from model, forms, etc... You inherit from an object that is already integrated in the framework and thus has specific methods to work with the framework. For example the model will be registered to the database, the form 'knows' how to render properly, etc...
When you inherit from these classes, you already have an pre-built object with all these methods.

Model subclass: override CharField max_length

I have a supplied database schema for which I want to create a Django application. Many of the tables in the schema share a common set of columns, such as name and date_created. That prompted me to create an abstract Standard_model class containing those columns, and subclass the relevant models from it.
Unfortunately, some of the tables have a name column with a different max_length. I'm trying to come up with a way for the subclassed model to pass the max_length value to the abstract base class, but I'm drawing a blank.
Any ideas?
class Standard_model(models.Model):
name = models.CharField(max_length=50)
date_created = models.DateTimeField()
class Meta:
abstract = True
class MyModel(Standard_model):
name = models.CharField(max_length=80) # Can't do this.
No, you cannot override the name field definition:
In normal Python class inheritance, it is permissible for a child
class to override any attribute from the parent class. In Django, this
is not permitted for attributes that are Field instances (at least,
not at the moment). If a base class has a field called author, you
cannot create another model field called author in any class that
inherits from that base class.
See also:
In Django - Model Inheritance - Does it allow you to override a parent model's attribute?
And, FYI, according to the model naming convention, it should be called StandardModel.

Django: store common fields in a parent model

I've got some models:
class Place(models.Model):
name = models.CharField(unique=True)
class Bar(Place):
drinks = models.ManyToManyField('Drink')
class Restaurant(Place):
meals = models.ManyToManyField('Meals')
That's a multi-table inherited structure where each bar serves drinks only, and each restaurant serves meals only. I, though, need a name of each place to be unique across all the places - hence the parent Place model.
Now, multi-table inheritance presumes a parent and a child are separate entities. That means when I want to create a new Bar, I should go like this:
>> parent = Place(name='Myplace')
>> parent.save()
>> child = Bar(place=parent, drinks=mydrinklist)
>> child.save()
But in my case, Place is not a separate entity: it should not exists by itself. It's just a shared storage with some restrictions. I'd like to have something like this:
>> child = Bar(name='Myplace', drinks=mydrinklist)
>> child.save()
Where name attribute is automatically passed to the underlying parent model and a Place model is silently created when save() is called. SQLAlchemy can do that via its multi-table inheritance. Is there a way to achieve the same in Django?
Django's abstract base classes solve the problem of sharing common fields between models:
class Place(models.Model):
name = models.CharField(unique=True)
class Meta:
abstract = True
Edit: Having said that, as Daniel mentioned in the comments, the solution you propose should work just fine. Here's more on Django's multi-table inheritance

Django model multi-table inheritance: create super instance of existing instance (cast)

I have the following class hierarchy in Django, using multi-table inheritance:
class Vehicle(models.Model):
name = models.CharField(blank=True)
class Car(Vehicle):
color = models.CharField(blank=True)
As I use multi-table inheritance, at the database level, there two database tables, one for Vehicle and another for Car. There is also a OneToOne relationship between them.
I have some Car objects on my database. I want to cast these objects to Vehicle, and delete the "Car" part of them. I want that none of these "ex-Cars" appear if I do Car.objects.all().
To obtain the superclass instance, I can do:
car = Car()
vehicle = car.vehicle
But how can I safely delete the "car" object without deleting the "vehicle" object?
I have found some answers related to downcasting (creating a child class instance from a superclass instance and storing it on db, see: Django model inheritance: create sub-instance of existing instance (downcast)?), but I have not found an answer yet about this case.

Django - how to make required to implement field?

I'm buildibg some abstract model for about 10 models. I need to make, somehow, that 1 field is not declared in abstract model, but MUST be declared in inheriting models.
How to do that? Is there any way to use NotImplementedError?
I am afraid there isn't an easy way to achieve that, if possible at all, without digging deep into Django.
The main reason is that Field name "hiding" is not permitted in Django. What this means is that if you want to declare an abstract attribute in the base abstract class that is a Field instance, you will not be able to rewrite it in the child classes contrary to the normal Python class inheritance paradigm. To quote from the doc:
In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this is not permitted for attributes that are Field instances (at least, not at the moment). If a base class has a field called author, you cannot create another model field called author in any class that inherits from that base class.
Overriding fields in a parent model leads to difficulties in areas such as initializing new instances (specifying which field is being initialized in Model.init) and serialization. These are features which normal Python class inheritance doesn't have to deal with in quite the same way, so the difference between Django model inheritance and Python class inheritance isn't arbitrary.
This restriction only applies to attributes which are Field instances. Normal Python attributes can be overridden if you wish. It also only applies to the name of the attribute as Python sees it: if you are manually specifying the database column name, you can have the same column name appearing in both a child and an ancestor model for multi-table inheritance (they are columns in two different database tables).
Django will raise a FieldError if you override any model field in any ancestor model.
However, if the attribute is not a Field instance (very unlikely though), you will be able to achieve exactly what you want by using using #property decorator. Something like this should work:
class Person(models.Model):
def __init__(self, *args, **kwargs):
super(Person, self).__init__(*args, **kwargs)
self.last_name
first_name = models.CharField(max_length=30)
#property
def last_name(self):
raise NotImplementedError
class Meta:
abstract = True
class Student(Person):
home_group = models.CharField(max_length=5)
last_name = "Doe" # "models.CharField()" will not work!
class BadStudent(Person):
home_group = models.CharField(max_length=5)
# "NotImplmentedError" will be raised when instantiating BadStudent()
You may also want to take a look at abc.abstractproperty. I am not sure how it would work with Django's model inheritance though.
Why would you want to do it?? Which are the reasons the common field cannot be declared in the AbstractModel??
If you really want to do it, use the instructions here:
add methods in subclasses within the super class constructor

Categories