GenericForeignKey tries to give you a ForeignKey
behavior but instead to be against one type of object, they do it for a set of object types (that's why they are defined with 2 columns, 1 to keep the primary_key
and another to keep the content_type
).
GenericRelation
is the reverse relation of a GenericForeignKey
, because Django does not automatically create reverse relations for GenericForeignKeys
(unlike ForeignKeys
) you have to setup them manually.
I'm not very familiar with the best-practices in translations/vocabulary staff, 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()
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
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
).
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
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.
This scales nicely when you have several Nouns
from one contenty_type
in the list you are prefetching, but it will make no difference if you have 1 Noun per
contenty_type`.
Another approach that 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 be as easy as:
word_nouns = word.wordnoun_set.all()