了解Django GenericForeignKey和GenericRelation

时间:2016-10-20 07:57:25

标签: django django-models

我建立词汇并拥有以下模型:

class Word(Model):
 name = CharField(max_length=75)

class EnNoun(Model):
 word = OneToOneField(Word)

class FrNoun(Model):
 word = ForeignKey(Word)
 gender = CharField()

同一个字可以同时包含EnNounFrNoun。 是否可以使用最少数量的查询来获取EnNounFrNoun的给定单词的结果(对于语言和词性,将会有更多类似的类,如ItAdverb) ?

如何将翻译从一个lang存储到另一个lang(查询20多个表不是一个选项)。

GenericForeign键是否有用? 我一般如何使用它们?

谢谢。

更新

有以下翻译课程:

@python_2_unicode_compatible
class Translation(Model):
    from_content_type = ForeignKey(ContentType, related_name='from_word_content_type')
    from_object_id = UUIDField(default=uuid.uuid4)
    from_word = GenericForeignKey('from_content_type', 'from_object_id')

    to_content_type = ForeignKey(ContentType, related_name='to_word_content_type')
    to_object_id = UUIDField(default=uuid.uuid4)
    to_word = GenericForeignKey('to_content_type', 'to_object_id')

但它不起作用: 字段' to_word'不生成自动反向关系,因此不能用于反向查询。如果是GenericForeignKey,请考虑添加GenericRelation。

2 个答案:

答案 0 :(得分:2)

GenericForeignKey试图给你一个ForeignKey行为,但是它反对一种类型的对象,它们是为一组对象类型做的(这就是为什么它们用2列定义,1为了保持{{} 1}}和另一个保留primery_key)。

contenty_typeGenericRelation的反向关系,因为Django不会自动为GenericForeignKey创建反向关系(与GenericForeignKeys不同),您必须手动设置它们。< / p>

我对翻译/词汇工作人员的最佳实践不是很熟悉,但如果你想用ForeignKeysGenericRelations解决问题,一种方法是:< / p>

GenericForeignKeys

我们基本上创建了一个保持单词 - 名词关系的模型, 这个是以下

class Word(Model):
    name = CharField(max_length=75)
    nouns = GenericRelation('WordNoun', content_type_field='noun_ct', object_id_field='noun_id')

class WordNoun(Model):
    word = ForeignKey(Word)
    noun_ct = ForeignKey(ContentType,
        on_delete=models.CASCADE,
        #this is optional
        limit_choices_to = {"model__in": ('EnNoun', 'FrNoun')}
    )
    noun_id = PositiveIntegerField()
    noun = GenericForeignKey('noun_ct', 'noun_id')

class EnNoun(Model):
    word = OneToOneField(Word)

class FrNoun(Model):
    word = ForeignKey(Word)
    gender = CharField()

这种方法的问题在于,在获得# Having some word word = Word.objects.get(pk=1) # With 1 query you can get a list with # all WordNoun objects for this word. word_nouns = word.nouns.all() 列表后, 访问单个word_nouns实例将进行新查询。

noun

稍微优化一下的一种方法是使用for word_noun in word.nouns.all(): print word_noun.noun #this will make a query ,因此如果一个prefetch_related有3 word(假设1 word_nouns和2 EnNoun )。

然后,而不是4个查询 - FrNoun为1,每个word_nouns为3,我们将其优化为3个查询 - noun为1,每个{{1}为2个}(word_nounscontenty_type

EnNoun

不同之处在于查询数量现在取决于不同FrNoun的数量,而不是相关for word_noun in word.nouns.all().prefetch_related('noun'): print word_noun.noun #this will not make a query 个对象的数量。

如果您预取的列表中有一个ContentTypes有多个WordNoun,那么这会很好,但如果您有1个Nouns contenty_type`则没有任何区别。

我能想到的另一种方法是使用以下模型结构:

contenty_type

答案 1 :(得分:0)

可以向同一模型添加多个GenericForeignKey。这将允许您在具有通用类型的对象之间创建“链接”。后面我举一个例子。我修改了您的示例,以创建我认为更有用的东西。表Translation在法语和英语单词之间创建链接。单词存储在类EnVerbFrVerbEnNounFrNoun中。如果没有GenericForeignKey,则必须创建两个转换模型:TranslationVerbTranslationNoun。但是在下面的代码中,我展示了适用于动词和名词的通用翻译模型。我希望这是有道理的!

从技术上讲,您忘记添加GenericRelation字段。另外,他们必须在相关对象的content_type字段中指定字段object_idGenericRelation的新名称。

示例

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

Translation(models.Model):
    fr_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name="fr_content_type")
    fr_object_id = models.PositiveIntegerField()
    fr_word = GenericForeignKey('fr_content_type', 'fr_object_id')

    en_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name="en_content_type")
    en_object_id = models.PositiveIntegerField()
    en_word = GenericForeignKey('en_content_type', 'en_object_id')

class FrVerb(models.Model):
    name = models.CharField(max_length=75)
    translation = GenericRelation(Translation, content_type_field='fr_content_type', object_id_field='fr_object_id')

class FrNoun(models.Model):
    name = models.CharField(max_length=75)
    gender = models.CharField(max_length=75)
    translation = GenericRelation(Translation, content_type_field='fr_content_type', object_id_field='fr_object_id')

class EnVerb(models.Model):
    name = models.CharField(max_length=75)
    translation = GenericRelation(Translation, content_type_field='en_content_type', object_id_field='en_object_id')

class EnNoun(models.Model):
    name = models.CharField(max_length=75)
    translation = GenericRelation(Translation, content_type_field='en_content_type', object_id_field='en_object_id')

您可以使用它在模型之间创建通用翻译:

from polls.models import *      

EnNoun(name='tree').save()                                                                                                                       
FrNoun(name='arbre').save()                                                                                                                      
EnVerb(name='be').save()                                                                                                                         
FrVerb(name='etre').save()                                                                                                                       

trans1 = Translation(fr_word=FrNoun.objects.first(), en_word=EnNoun.objects.first())                                                              
trans2 = Translation(fr_word=FrVerb.objects.first(), en_word=EnVerb.objects.first())

现在trans1用于链接“树”和“ arbre”,trans2用于链接“ be”和“être”,而这些对象属于不同的模型!