了解Django GenericForeignKey和GenericRelation

And*_*unt 4 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()
Run Code Online (Sandbox Code Playgroud)

EnNoun和中可以有相同的单词FrNoun。是否可以使用最少数量的查询(和语言和词性类似的类更多,例如)来为EnNoun和两者获取给定单词的结果?FrNounItAdverb

如何存储从一种语言到另一种语言的翻译(查询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')
Run Code Online (Sandbox Code Playgroud)

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

Tod*_*dor 6

GenericForeignKey尝试给你一个ForeignKey行为,而是要针对一个类型的对象,他们是为了一组对象类型的(这就是为什么它们与2列,1保持定义primary_key,另一个保留contenty_type)。

GenericRelation是的反向关系GenericForeignKey,因为Django不会自动为您创建反向关系GenericForeignKeys(不同于ForeignKeys),您必须手动设置它们。

我对翻译/词汇人员的最佳做法不是很熟悉,但是如果您想使用GenericRelations和解决问题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()
Run Code Online (Sandbox Code Playgroud)

我们基本上是在建立一个保持单词-名词关系的模型,这给出了以下内容

# 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()
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于,获取word_nouns列表后,访问单个noun实例将进行新查询。

for word_noun in word.nouns.all():
    print word_noun.noun #this will make a query
Run Code Online (Sandbox Code Playgroud)

一种稍微优化此方法的方法是使用prefetch_related,因此,如果一个word具有3个word_nouns(让我们说1 EnNoun和2 FrNoun)。

然后,而不是4项的查询- 1为word_nouns和3对每个noun,我们把它优化至3项的查询- 1为word_nouns和2的每个contenty_typeEnNounFrNoun

for word_noun in word.nouns.all().prefetch_related('noun'):
    print word_noun.noun #this will not make a query
Run Code Online (Sandbox Code Playgroud)

区别在于查询的数量现在将取决于不同的数量ContentTypes,而不是相关WordNoun对象的数量。

这种秤不错,当你有几个Nouns从一个contenty_type在你预取列表中,但将没有什么区别,如果你有1个Noun percontenty_type`。

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

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

class WordNoun(Model):
    LANG_CHOICES = (
        ('en', 'English'),
        ('fr', 'French'),
    )
    word = ForeignKey(Word)
    lang = models.CharField(max_length=2, choices=LANG_CHOICES)
    gender = CharField(max_length=2, blank=True, default='')


#Now accessing all word_nouns would as easy as:
word_nouns = word.wordnoun_set.all()
Run Code Online (Sandbox Code Playgroud)