¿Cómo se puede obtener el conjunto de todas las clases con relaciones inversas para un modelo en Django?

StackOverflow https://stackoverflow.com/questions/279782

Pregunta

Dado:

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)

¿Cómo puede uno, dada solo la clase Food, obtener un conjunto de todas las clases que tiene "relaciones inversas"? a. Es decir. dada la clase Food , ¿cómo se pueden obtener las clases Cat , Cow y Human .

Creo que es posible porque Food tiene las tres "relaciones inversas": Food.cat_set , Food.cow_set y Food.human_set .

La ayuda es apreciada & amp; gracias!

¿Fue útil?

Solución

Cualquiera

A) Use herencia de múltiples tablas y crea un " Eater " clase base, de la que heredan Cat, Cow y Human.

B) Use una Relación genérica , donde Los alimentos pueden estar vinculados a cualquier otro modelo.

Esas son características bien documentadas y compatibles oficialmente, es mejor que se adhiera a ellas para mantener su propio código limpio, evitar soluciones alternativas y asegurarse de que seguirá siendo compatible en el futuro.

- EDITAR (A.k.a. " cómo ser una prostituta de reputación ")

Entonces, aquí hay una receta para ese caso en particular.

Supongamos que quiere absolutamente modelos separados para Cat, Cow y Human. En una aplicación del mundo real, debe preguntarse por qué una " categoría " campo no haría el trabajo.

Es más fácil llegar al " real " clase a través de relaciones genéricas, así que aquí está la implementación para B. No podemos tener ese campo de 'comida' en Persona, Gato o Vaca, o nos encontraremos con los mismos problemas. Entonces crearemos un intermediario '' FoodConsumer '' modelo. Tendremos que escribir pruebas de validación adicionales si no queremos más de un alimento para una instancia.

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)    

Ahora, para demostrarlo, escribamos esto 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'

"""

Otros consejos

Se revelaron algunas excavaciones en el código fuente:

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)

Y, usando estas funciones en los modelos de arriba, hipotéticamente obtienes:

>>> 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'>

Nota : Escuché que Model._meta es 'inestable', y tal vez no se debe confiar en el mundo posterior a Django-1.0.

Gracias por leer. :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top