Wie kann man die Menge aller Klassen mit Reverse-Beziehungen für ein Modell in Django bekommen?

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

Frage

Gegeben:

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)

Wie kann man nur die Klasse Lebensmittel gegeben, eine Menge aller Klassen erhalten, die es zu „reverse Beziehungen“ hat. D. h angesichts der Klasse Essen , wie kann man die Klassen bekommen Katze , Cow und Mensch .

Ich würde denken, dass es möglich ist, weil Essen die drei "reverse Beziehungen" hat: Food.cat_set Food.cow_set und Food.human_set .

Hilfe der geschätzt und danke!

War es hilfreich?

Lösung

Entweder

A) Verwenden Sie Vererbung mehrere Tabellen und erstellen Sie eine „Eater“ Basisklasse, die Katze, Kuh und Mensch beerben.

B) Verwenden Sie einen Generika Relation , wo Essen könnte zu jedem anderen Modell verknüpft werden.

Das ist gut dokumentiert und offiziell Features unterstützt, sollten Sie besser bei ihnen bleiben würden Ihren eigenen Code sauber zu halten, Umgehungen zu vermeiden und sicher sein, es wird auch in Zukunft unterstützt werden.

- EDIT (auch bekannt als "wie ein Ruf Hure sein")

So, hier ist ein Rezept für diesen speziellen Fall.

Nehmen wir an, Sie wollen unbedingt separate Modelle für Katze, Kuh und Mensch. In einer realen Anwendung, möchten Sie sich fragen, warum eine „Kategorie“ Feld würde den Job nicht machen.

Es ist einfacher, die „echte“ Klasse durch generische Beziehungen zu bekommen, so ist hier die Umsetzung von B. Wir können nicht haben, dass ‚Lebensmittel‘ Feld in Person, Katze oder Kuh, oder wir werden in die gleichen laufen Probleme. Also werden wir einen Vermittler „FoodConsumer“ Modell erstellen. Wir müssen zusätzliche Validierungstests schreiben, wenn wir für eine Instanz nicht mehr als ein Lebensmittel wollen.

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)    

Nun, zu zeigen, es wir schreiben gerade diese Arbeit 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'

"""

Andere Tipps

Einige graben im Quellcode offenbart:

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)

Und diese Funktionen auf den Modellen von oben verwenden, können Sie hypothetisch erhalten:

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

Hinweis . Ich höre Model._meta ist 'instabil', und vielleicht sollte nicht auf in der Post Django-1.0 Welt verlassen wird

Vielen Dank für das Lesen. :)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top