How can one get the set of all classes with reverse relationships for a model in Django?

Given:

from django.db import models

class Food(models.Model):
     """Food, by name."""
     name = models.CharField(max_length=25)

class Cat(models.Model):
     """A cat eats one type of food"""
     food = models.ForeignKey(Food)

class Cow(models.Model):
     """A cow eats one type of food"""
     food = models.ForeignKey(Food)

class Human(models.Model):
     """A human may eat lots of types of food"""
     food = models.ManyToManyField(Food)

How can one, given only the class Food, get a set of all classes that it has "reverse relationships" to. I.e. given the class Food, how can one get the classes Cat, Cow and Human.

I would think it's possible because Food has the three "reverse relations": Food.cat_set, Food.cow_set, and Food.human_set.

Help's appreciated & thank you!


Asked by: Elise424 | Posted: 01-10-2021






Answer 1

Some digging in the source code revealed:

django/db/models/options.py:

def get_all_related_objects(self, local_only=False):

def get_all_related_many_to_many_objects(self, local_only=False)

And, using these functions on the models from above, you hypothetically get:

>>> Food._meta.get_all_related_objects()
[<RelatedObject: app_label:cow related to food>,
    <RelatedObject: app_label:cat related to food>,]

>>> Food._meta.get_all_related_many_to_many_objects()
[<RelatedObject: app_label:human related to food>,]

# and, per django/db/models/related.py
# you can retrieve the model with
>>> Food._meta.get_all_related_objects()[0].model
<class 'app_label.models.Cow'>

Note: I hear Model._meta is 'unstable', and perhaps ought not to be relied upon in the post Django-1.0 world.

Thanks for reading. :)

Answered by: Ada508 | Posted: 02-11-2021



Answer 2

Either

A) Use multiple table inheritance and create a "Eater" base class, that Cat, Cow and Human inherit from.

B) Use a Generic Relation, where Food could be linked to any other Model.

Those are well-documented and officially supported features, you'd better stick to them to keep your own code clean, avoid workarounds and be sure it'll be still supported in the future.

-- EDIT ( A.k.a. "how to be a reputation whore" )

So, here is a recipe for that particular case.

Let's assume you absolutely want separate models for Cat, Cow and Human. In a real-world application, you want to ask to yourself why a "category" field wouldn't do the job.

It's easier to get to the "real" class through generic relations, so here is the implementation for B. We can't have that 'food' field in Person, Cat or Cow, or we'll run into the same problems. So we'll create an intermediary "FoodConsumer" model. We'll have to write additional validation tests if we don't want more than one food for an instance.

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Food(models.Model):
     """Food, by name."""
     name = models.CharField(max_length=25)

# ConsumedFood has a foreign key to Food, and a "eaten_by" generic relation
class ConsumedFood(models.Model):
    food = models.ForeignKey(Food, related_name="eaters")
    content_type = models.ForeignKey(ContentType, null=True)
    object_id = models.PositiveIntegerField(null=True)
    eaten_by = generic.GenericForeignKey('content_type', 'object_id')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()
    address = models.CharField(max_length=100)
    city = models.CharField(max_length=50)
    foods = generic.GenericRelation(ConsumedFood)

class Cat(models.Model):
    name = models.CharField(max_length=50)
    foods = generic.GenericRelation(ConsumedFood)    

class Cow(models.Model):
    farmer = models.ForeignKey(Person)
    foods = generic.GenericRelation(ConsumedFood)    

Now, to demonstrate it let's just write this working doctest:

"""
>>> from models import *

Create some food records

>>> weed = Food(name="weed")
>>> weed.save()

>>> burger = Food(name="burger")
>>> burger.save()

>>> pet_food = Food(name="Pet food")
>>> pet_food.save()

John the farmer likes burgers

>>> john = Person(first_name="John", last_name="Farmer", birth_date="1960-10-12")
>>> john.save()
>>> john.foods.create(food=burger)
<ConsumedFood: ConsumedFood object>

Wilma the cow eats weed

>>> wilma = Cow(farmer=john)
>>> wilma.save()
>>> wilma.foods.create(food=weed)
<ConsumedFood: ConsumedFood object>

Felix the cat likes pet food

>>> felix = Cat(name="felix")
>>> felix.save()
>>> pet_food.eaters.create(eaten_by=felix)
<ConsumedFood: ConsumedFood object>

What food john likes again ?
>>> john.foods.all()[0].food.name
u'burger'

Who's getting pet food ?
>>> living_thing = pet_food.eaters.all()[0].eaten_by
>>> isinstance(living_thing,Cow)
False
>>> isinstance(living_thing,Cat)
True

John's farm is in fire ! He looses his cow.
>>> wilma.delete()

John is a lot poorer right now
>>> john.foods.clear()
>>> john.foods.create(food=pet_food)
<ConsumedFood: ConsumedFood object>

Who's eating pet food now ?
>>> for consumed_food in pet_food.eaters.all():
...    consumed_food.eaten_by
<Cat: Cat object>
<Person: Person object>

Get the second pet food eater
>>> living_thing = pet_food.eaters.all()[1].eaten_by

Try to find if it's a person and reveal his name
>>> if isinstance(living_thing,Person): living_thing.first_name
u'John'

"""

Answered by: Lenny541 | Posted: 02-11-2021



Similar questions

python - In Django, how do you retrieve data from extra fields on many-to-many relationships without an explicit query for it?

Given a situation in Django 1.0 where you have extra data on a Many-to-Many relationship: class Player(models.Model): name = models.CharField(max_length=80) class Team(models.Model): name = models.CharField(max_length=40) players = models.ManyToManyField(Player, through='TeamPlayer', related_...


python - What is the best way to fetch/render one-to-many relationships?

I have 2 models which look like that: class Entry(models.Model): user = models.ForeignKey(User) dataname = models.TextField() datadesc = models.TextField() timestamp = models.DateTimeField(auto_now=True) class EntryFile(models.Model): entry = models.ForeignKey(Entry) datafile = models.FileField(upload_to="uploads/%Y/%m/%d/%H-%M-%S") I want to render all the entries with their rela...


python - Generic many-to-many relationships

I'm trying to create a messaging system where a message's sender and recipients can be generic entities. This seems fine for the sender, where there is only object to reference (GenericForeignKey) but I can't figure out how to go about this for the recipients (GenericManyToManyKey ??) Below is a simplified example. PersonClient and CompanyClient inherit attributes from Client but have their own specific details. Th...


python - Loading and saving data from m2m relationships in Textarea widgets with ModelForm

I have a Model that looks something like this: class Business(models.Model): name = models.CharField('business name', max_length=100) # ... some other fields emails = models.ManyToManyField(Email, null=True) phone_numbers = models.ManyToManyField(PhoneNumber, null=True) urls = models.ManyToManyField(URL, null=True) and a corresponding ModelForm: class Bu...


Python OOP - Class relationships

Assuming I have a system of three Classes. The GameClass creates instances of both other classes upon initialization. class FieldClass: def __init__( self ): return def AnswerAQuestion( self ): return 42 class PlayerClass: def __init__( self ): return def DoMagicHere( self ): # Access "AnswerAQuestion" located in the "FieldClass" instance in ...


python - How to output huge dependency relationships diagram of Plone with Graphviz?

I wrote a tool for find dependency relationships behind a Python project. It is Gluttony. I run it on Plone, the result is impressive. I output the diagram with Networkx, and it looks like this:


uml - What options are there for visualising class relationships in a Python program

Closed. This question does not meet Stack Overflow guid...


python - how do simple SQLAlchemy relationships work?

I'm no database expert -- I just know the basics, really. I've picked up SQLAlchemy for a small project, and I'm using the declarative base configuration rather than the "normal" way. This way seems a lot simpler. However, while setting up my database schema, I realized I don't understand some database relationship concepts. If I had a many-to-one relationship before, for example, articles by authors (where...


python - Foreign key relationships missing when reflecting db in SqlAlchemy

I am attempting to use SqlAlchemy (0.5.8) to interface with a legacy database declaratively and using reflection. My test code looks like this: from sqlalchemy import * from sqlalchemy.orm import create_session from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() engine = create_engine('oracle://schemaname:pwd@SID') meta = MetaData(bind=engine) class CONSTRUCT(Base): __tab...


python - Elegant way to count frequency and correlation of many-to-many relationships with Django ORM?

I have a Pizza model and a Topping model, with a many-to-many relationship between the two. Can you recommend an elegant way to extract: the popularity (frequency) of each topping the correlation between toppings (i.e. which sets of toppings are most frequent) Thanks






Still can't find your answer? Check out these communities...



PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python



top