Django inheritance and subclass attributes - python

With a configuration such as this:
class A (models.Model):
common_attribute = models.IntegerField()
class B (A):
subclass_attribute = models.IntegerField()
class C (models.Model)
a = ForeignKey(A)
... if an instance of C contains an instance of B, Django seems to treat this as an instance of A and I can't access c.a.subclass_attribute. Is there a way around this? I don't want to use abstract inheritance because of the difficulties with ForeignKeys - I want C to support any subclass of A.

You can circumvent the difficulties with ForeignKey by using django-polymorphic.
Django Polymorphic allows you to query the base class objects but retrieves the child class instances:
>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
>>> Project.objects.all()
[ <Project: id 1, topic "Department Party">,
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
To use django polymorphic you only need to declare your models with Polymorphic Model as base class:
from django.db import models
from polymorphic import PolymorphicModel
class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
Foreign keys will also return the child class instances, which I think is exactly what you want.
# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model
>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))
>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Take into account that these queries will be slightly less performant.

Related

Django model filter elements

I have four models as follows:
class modelA(models.Model):
name = models.CharField(...)
class modelB(models.Model):
date = models.DateTimeField(...)
A = models.ForeignKey(modelA, ...)
class modelC(models.Model):
email = models.CharField(...)
B = models.ForeignKey(modelB, ...)
class modelD(models.Model):
uid = models.CharField(...)
C = models.ForeignKey(modelC)
Given modelA element id, I have to filter modelD elements based on that id. But I am not sure about how to do that.
I appreciate any ideas!
modalD.objects.filter(C__B__A__name ='name')
when you use double underscore you filter the related Inheritance modal

Django changing model value based on relationship with other model

I'm working on a project that has three models, one of which has a value that is dependent on if the other two are linked to it by a foreign key. the value can also switch if the links change. I was wondering how I would go about that.
For example:
general/models.py
class Person(models.Model):
DEFAULT = 'D'
A = 'A'
B = 'B'
BOTH = 'AB'
TYPES = [
(DEFAULT,'Default'),
(A,'Type A'),
(B,'Type B'),
(BOTH,'Type A & B')
]
# type is default if Person is not linked to A or B
# A if Person is linked to A
# B if Person is linked to B
# BOTH if Person is linked to both
type = models.CharField(max_length=2, choices=TYPES, default=DEFAULT)
A/models.py
class A(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)
B/models.py
class B(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)
You should override the save method of models A and B in order to modify the ForeignKey like this:
class A(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)
def save(self, *args, **kwargs):
if self.person.type == Person.B:
self.person.type=Person.AB
self.person.save()
# elif...

Differences in using a "through" table without or with a related name for many-to-many relationship

When it comes to Many-To-Many fields in Django, what is the difference between using a "through" table like the following...
class A(models.Model):
things = models.ManyToManyField("B", through=ThroughModel)
...
class B(models.Model):
text = models.TextField()
...
class ThroughModel(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
compared to just specifying a related name in the intermediary table like so?
class A(models.Model):
...
class B(models.Model):
text = models.TextField()
...
class ThroughModel(models.Model):
a = models.ForeignKey(A, related_name="things")
b = models.ForeignKey(B)
Either way you should be able to reference the relationship like
a_instance.things
right? Is there some behind the scenes difference between how this is implemented in the database?
The difference lies in how you reference a_instance from b_instance:
b_instance.a_set
in the first case and
b_instance.things
in the second case

How to exclude fields from form created via PolymorphicChildModelAdmin

Playing a little around with Polymorphic and additional plugins I'm wondering how I can prevent some of the base class fields from being showed inside form for child admin interface. Having this adminy.py for my child class:
from django.contrib import admin
from .models import *
from partsmanagement.models import Part
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
admin.site.register(Book)
class BookAdmin(PolymorphicChildModelAdmin):
base_model = Part
and this admin.py for the base model:
# -*- coding: utf-8 -*-
from django.contrib import admin
from .models import *
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from bookcollection.models import Book
from bookcollection.admin import BookAdmin
admin.site.register(Part)
class PartAdmin(PolymorphicParentModelAdmin):
base_model = 'Part'
child_models = (
(Book, BookAdmin),
)
Now the form inside admin shows all fileds of base and child class. I tried to add exclude = list() for child class but this didn't work (no change).
Filtering for classes (equivalent to python's isinstance() ):
>>> ModelA.objects.instance_of(ModelB)
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
In general, including or excluding parts of the inheritance tree:
ModelA.objects.instance_of(ModelB [, ModelC ...])
ModelA.objects.not_instance_of(ModelB [, ModelC ...])
You can also use this feature in Q-objects (with the same result as above):
>>> ModelA.objects.filter( Q(instance_of=ModelB) )
Polymorphic filtering (for fields in derived classes)
For example, cherrypicking objects from multiple derived classes anywhere in the inheritance tree, using Q objects (with the syntax: exact model name + three _ + field name):
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Combining Querysets
Querysets could now be regarded as object containers that allow the aggregation of different object types, very similar to python lists - as long as the objects are accessed through the manager of a common base class:
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
[ <ModelX: id 1, field_x (CharField)>,
<ModelY: id 2, field_y (CharField)> ]

Connect one model's ManyToManyField with another model's ForeignKey

let I have a model with ForeignKey to another:
class Photo(models.Model):
name = models.CharField(max_length=64)
album = models.ForeignKey('Album')
And that 'Album' model must to connect this photo's objects to its ManyToManyField automatically:
class Album(models.Model):
name = models.CharField(max_length=64)
photoItems = models.ManyToManyField(Photo)
And the question is how can I do this?
You could do it the following way, and Django should automatically add a reverse relation. Hence, no need to add album as foreign key to your Photo model. Doing this you get access to "albums" from your Photo model.
class Photo(models.Model):
name = models.CharField(max_length=64)
class Album(models.Model):
name = models.CharField(max_length=64)
photoItems = models.ManyToManyField(Photo, related_name='albums')
Shell:
>>> photo1=Photo(name="photo number 1")
>>> photo2=Photo(name="photo number 2")
>>> photo1.save()
>>> photo2.save()
>>> album=Album(name="album1")
>>> album.save()
>>> album.photoItems.add(photo1,photo2)
>>> photo1.albums.get().name
'album1'

Categories