Como pode-se obter o conjunto de todas as classes com relacionamentos reversa para um modelo no Django?

votos
4

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)

Como pode um, dada apenas a classe Alimentação, obter um conjunto de todas as classes que tem relações Reverse para. Ou seja, dada a classe de Alimentação , como se pode obter a aulas gato , vaca e Humano .

Eu acho que é possível porque o alimento possui os três relações reversa: Food.cat_set , Food.cow_set e Food.human_set .

Ajuda do apreciado e obrigado!

Publicado 11/11/2008 em 02:42
fonte usuário
Em outras línguas...                            


2 respostas

votos
14

Algumas escavações no código fonte revelou:

django / db / modelos / options.py:

def get_all_related_objects(self, local_only=False):

def get_all_related_many_to_many_objects(self, local_only=False)

E, usando essas funções nos modelos de acima, você hipoteticamente obter:

>>> 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 : eu ouço Model._meta é 'instável', e talvez não deve ser invocado no post Django-1.0 mundo.

Obrigado pela leitura. :)

Respondeu 11/11/2008 em 03:00
fonte usuário

votos
7

Ou

A) Use herança de tabela múltipla e criar uma classe base "Eater", que gato, vaca e Human herdar.

B) Use uma relação genérica , onde o alimento pode estar ligada a qualquer outro modelo.

Essas são bem documentado e características oficialmente suportado, é melhor você ficar com eles para manter seu próprio código limpo, evitar soluções alternativas e ter certeza de que ainda vai ser suportado no futuro.

- EDIT (Aka "como ser uma prostituta reputação")

Então, aqui está uma receita para esse caso particular.

Vamos supor que você absolutamente deseja modelos separados para gato, vaca e Humano. Em uma aplicação no mundo real, você quer perguntar a si mesmo por um campo "categoria" não iria fazer o trabalho.

É mais fácil para chegar à classe "real" através de relações genéricas, então aqui é a implementação de B. Não podemos ter esse campo 'food' em pessoa, gato ou vaca, ou nós vamos correr para os mesmos problemas. Então vamos criar um modelo intermediário "FoodConsumer". Nós vamos ter que escrever testes de validação adicionais, se nós não queremos mais do que um alimento para uma instância.

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)    

Agora, para demonstrar que vamos apenas escrever este trabalho 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'

"""
Respondeu 11/11/2008 em 04:48
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more