2008-11-11 5 views
4

Étant donné:Comment peut-on obtenir l'ensemble de toutes les classes avec des relations inverses pour un modèle dans Django?

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 nourriture de classe, obtenir un ensemble de toutes les classes qu'il a « relations inverses » à. C'est à dire. donné la classe Alimentation, comment peut-on obtenir les classes Cat, vache et humaine.

Je pense qu'il est possible parce que la nourriture a trois "relations inverses": Food.cat_set, Food.cow_set et Food.human_set.

Aide appréciée & merci!

Répondre

7

Soit

A) Utilisez multiple table inheritance et créer une classe de base "Eater", ce chat, vache et humain hériter de. B) Utiliser un Generic Relation, où la nourriture pourrait être liée à n'importe quel autre modèle. Il s'agit de fonctionnalités bien documentées et officiellement prises en charge. Il est préférable de vous y tenir pour garder votre propre code propre, éviter les solutions de contournement et vous assurer qu'il sera toujours pris en charge à l'avenir.

- EDIT (alias « comment être une putain de réputation »)

Donc, voici une recette pour ce cas particulier. Supposons que vous voulez absolument des modèles distincts pour les chats, les vaches et les humains. Dans une application du monde réel, vous voulez vous demander pourquoi un champ "catégorie" ne ferait pas le travail. Il est plus facile d'accéder à la "vraie" classe par des relations génériques, alors voici la mise en œuvre pour B. Nous ne pouvons pas avoir ce champ "food" en Personne, Chat ou Vache, ou nous allons rencontrer le mêmes problèmes. Nous allons donc créer un modèle "FoodConsumer" intermédiaire. Nous devrons écrire des tests de validation supplémentaires si nous ne voulons pas plus d'un aliment pour une 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 démontrer que nous allons écrire ce travail 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' 

""" 
+0

Salut Vincent, merci pour la réponse. Des exemples de comment utiliser exactement MTI ou GR pour répondre à la question seraient très appréciés :). –

+0

Brilliant, btw. :) Merci! –

14

Certains creuser dans le code source a révélé:

django/db/modèles/options.py:

def get_all_related_objects(self, local_only=False): 

def get_all_related_many_to_many_objects(self, local_only=False) 

Et, l'utilisation de ces fonctions sur les modèles de ci-dessus, 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'entends Model._meta est « instable », et devrait peut-être pas être invoqué dans le monde post-Django 1.0.

Merci d'avoir lu. :)

Questions connexes