Переопределение __init__
- неправильный способ сделать это. Django модели выполняют большую часть закулисной работы, с которой может конфликтовать переопределение __init__
, если вы не делаете это безопасным способом, следуя следующим правилам:
- Не изменяйте подпись из
__init__
- это означает, что вы не должны изменять аргументы, которые принимает метод. - Выполните пользовательский
__init__
logi c после , вызвав метод super().__init__(*args, **kwargs)
.
В этом конкретном случае вы можете использовать django функции наследования прокси-модели .
VERB = "V"
NOUN = "N"
# ...
WORD_TYPE_CHOICES = (
(VERB, "Verb"),
(NOUN, "Noun"),
# ...
)
class Word(models.Model):
original = models.CharField(max_length=40)
translation = models.CharField(max_length=40)
WORD_TYPE = "" # This is overridden in subclasses
word_type = models.CharField(
max_length=1,
blank=True,
editable=False, # So that the word type isn't editable through the admin.
choices=WORD_TYPE_CHOICES,
default=WORD_TYPE, # Defaults to an empty string
)
def __init__(self, *args, **kwargs):
# NOTE: I'm not 100% positive that this is required, but since we're not
# altering the signature of the __init__ method, performing the
# assignment of the word_type field is safe.
super().__init__(*args, **kwargs)
self.word_type = self.WORD_TYPE
def __str__(self):
return self.original
def save(self, *args, **kwargs):
# In the save method, we can force the subclasses to self-assign
# their word types.
if not self.word_type:
self.word_type = self.WORD_TYPE
super().save(*args, **kwargs)
class WordTypeManager(models.Manager):
""" This manager class filters the model's queryset so that only the
specific word_type is returned.
"""
def __init__(self, word_type, *args, **kwargs):
""" The manager is initialized with the `word_type` for the proxy model. """
self._word_type = word_type
super().__init__(*args, **kwargs)
def get_queryset(self):
return super().get_queryset().filter(word_type=self._word_type)
class Verb(Word):
# Here we can force the word_type for this proxy model, and set the default
# manager to filter for verbs only.
WORD_TYPE = VERB
objects = WordTypeManager(WORD_TYPE)
class Meta:
proxy = True
class Noun(Word):
WORD_TYPE = NOUN
objects = WordTypeManager(WORD_TYPE)
class Meta:
proxy = True
Теперь мы можем рассматривать различные типы слов как если они были отдельными моделями, или обращались ко всем вместе через модель Word
.
>>> noun = Noun.objects.create(original="name", translation="nombre")
>>> verb = Verb(original="write", translation="escribir")
>>> verb.save()
# Select all Words regardless of their word_type
>>> Word.objects.values_list("word_type", "original")
<QuerySet [('N', 'name'), ('V', 'write')]>
# Select the word_type based on the model class used
>>> Noun.objects.all()
<QuerySet [<Noun: name>]>
>>> Verb.objects.all()
<QuerySet [<Verb: write>]>
Это также работает с admin.ModelAdmin
классами.
@admin.register(Word)
class WordAdmin(admin.ModelAdmin):
""" This will show all words, regardless of their `word_type`. """
list_display = ["word_type", "original", "translation"]
@admin.register(Noun)
class NounAdmin(WordAdmin):
""" This will only show `Noun` instances, and inherit any other config from
WordAdmin.
"""