Comment obtenir l'ensemble de toutes les classes avec des relations inverses pour un modèle dans Django?

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

Question

Donné:

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)

Comment peut-on, étant donné que la classe Food, obtenir un ensemble de toutes les classes qu’il a "relations inverses"? à. C'est à dire. compte tenu de la classe Alimentation , comment obtenir les classes Chat , Vache et Humain .

Je pense que c'est possible car Food possède les trois "relations inverses": Food.cat_set , Food.cow_set et Food.human_set .

L'aide est appréciée & amp; merci!

Était-ce utile?

La solution

Soit

A) Utilisez Héritage de tables multiples et créez un " Mangeur " classe de base, que le chat, la vache et l’homme héritent de.

B) Utilisez une Relation générique , où Les aliments pourraient être liés à n’importe quel autre modèle.

Ce sont des fonctionnalités bien documentées et officiellement prises en charge, vous devriez vous en tenir à garder votre propre code propre, éviter les solutions de contournement et vous assurer qu'il sera toujours pris en charge à l'avenir.

- EDIT (A.k.a. "comment être une putain de réputation")

Alors, voici une recette pour ce cas particulier.

Supposons que vous souhaitiez absolument des modèles distincts pour Chat, Vache et Humain. Dans une application réelle, vous voulez vous demander pourquoi une "catégorie" champ ne ferait pas le travail.

Il est plus facile d’arriver au " real " classe à travers les relations génériques, voici donc la mise en œuvre pour B. Nous ne pouvons pas avoir ce champ «nourriture» dans Person, Cat ou Cow, ou nous aurons les mêmes problèmes. Nous allons donc créer un intermédiaire " FoodConsumer " modèle. Nous devrons écrire des tests de validation supplémentaires si nous ne voulons pas plus d’un aliment par 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)    

Maintenant, pour le démontrer, écrivons ce 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'

"""

Autres conseils

Certaines recherches dans le code source ont révélé:

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)

Et, en utilisant ces fonctions sur les modèles d'en haut, vous obtenez hypothétiquement:

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

Remarque : J'apprends que Model._meta est "instable" et ne devrait peut-être pas être utilisé dans le monde postérieur à Django-1.0.

Merci d'avoir lu. :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top