理解 Django GenericForeignKey 和 GenericRelation [英] Understanding Django GenericForeignKey and GenericRelation
问题描述
我正在建立词汇表并有以下模型:
I'm building vocabulary and have following models:
class Word(Model):
name = CharField(max_length=75)
class EnNoun(Model):
word = OneToOneField(Word)
class FrNoun(Model):
word = ForeignKey(Word)
gender = CharField()
EnNoun
和 FrNoun
中可以出现同一个词.是否可以使用最少的查询次数为 EnNoun
和 FrNoun
获取给定单词的结果(语言和词性会有更多类似的类,例如ItAdverb
)?
Same word can be in both EnNoun
and FrNoun
. Is it possible to fetch result for given word for both EnNoun
and FrNoun
, using the least number of queries (there will be more similar classes for language and part of speech, like ItAdverb
)?
如何存储从一种语言到另一种语言的翻译(查询 20 多个表不是一种选择).
How to store translation from one lang to another (querying 20+ tables is not an option).
GenericForeign
键有什么用吗?我一般如何使用它们?
Are GenericForeign
keys of any use? How can I use them in general?
我有以下翻译课:
@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')
但它不起作用:
Field 'to_word' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
推荐答案
GenericForeignKey 尝试给你一个 ForeignKey
行为,而不是针对一种类型的对象,他们为一组对象做类型(这就是为什么它们被定义为 2 列,1 列保留 primary_key
,另一个保留 contenty_type
).
GenericForeignKey try to give you a ForeignKey
behavior but instead to be against one type of object, they do it for a set of object types (thats why they are defined with 2 columns, 1 to keep the primary_key
and another to keep the contenty_type
).
GenericRelation
是 GenericForeignKey
的反向关系,因为 Django 不会自动为 GenericForeignKeys
创建反向关系(与 ForeignKeys
) 你必须手动设置它们.
GenericRelation
is the reverse relation of a GenericForeignKey
, because Django do not automatically create reverse relations for GenericForeignKeys
(unlike ForeignKeys
) you have to setup them manually.
我不太熟悉翻译/词汇工作人员的最佳实践,但是如果您想使用 GenericRelations
和 GenericForeignKeys
来解决问题,一种方法是这样做会是:
I'm not very familiar with the best-practices in translations/vocabulary staffs, but if you want to approach the problem with GenericRelations
and GenericForeignKeys
, one way to do it would be:
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()
我们基本上是在创建一个保持词-名词关系的模型,这是以下内容
We are basically creating a model keeping word-noun relations, this give is the following
# 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
列表后,访问单个 名词
实例将进行新查询.
The problem with this approach is that after you get the word_nouns
list,
accessing a single noun
instance will make a new query.
for word_noun in word.nouns.all():
print word_noun.noun #this will make a query
稍微优化的一种方法是使用 prefetch_related
,所以如果单个 word
有 3 个 word_nouns
(假设有 1 个 EnNoun
和 2 FrNoun
).
One way to slightly optimize this is to use prefetch_related
, so if a single word
has 3 word_nouns
(lets say 1 EnNoun
and 2 FrNoun
).
然后,我们将其优化为 3 个查询,而不是 4 个查询 - word_nouns
1 个,每个 名词
3 个,我们将其优化为 3 个查询 - word_nouns
1 个> 和每个 contenty_type
(EnNoun
和 FrNoun
)
Then instead of 4 queries - 1 for the word_nouns
and 3 for each noun
, we optimize it to 3 queries - 1 for the word_nouns
and 2 for each contenty_type
(EnNoun
and FrNoun
)
for word_noun in word.nouns.all().prefetch_related('noun'):
print word_noun.noun #this will not make a query
区别在于查询的数量现在取决于不同ContentTypes
的数量,而不是相关的WordNoun
对象的数量.
The difference is that the number of queries will now depends on the number of different ContentTypes
, rather than the number of related WordNoun
objects.
当您在要预取的列表中有多个来自一个 contenty_type
的 Nouns
时,这会很好地扩展,但是如果您有 1 个 Noun per
contenty_type`.
This scales nice when you have several Nouns
from one contenty_type
in the list you are prefetching, but will make no difference if you have 1 Noun per
contenty_type`.
我能想到的另一种方法是使用以下模型结构:
Another approach which I can think of is to use the following model structure:
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()
这篇关于理解 Django GenericForeignKey 和 GenericRelation的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!