gpt4 book ai didi

python - 如何安全地继承 django models.Model 类?

转载 作者:行者123 更新时间:2023-12-01 06:35:36 25 4
gpt4 key购买 nike

我有以下数据库结构:

class Word(models.Model):
original = models.CharField(max_length=40)
translation = models.CharField(max_length=40)

class Verb(Word):
group = models.IntegerField(default=1)

在我看来,我需要先创建一个 Word 对象,然后确定其组(取决于 Word.original)后,创建一个 Verb 对象,并保存它。

Word 类继承并将对象保存为 Verb 的最佳方法是什么?

我尝试过几种解决方案:

1) 修改Verb中的__init__方法:

class Verb(Word):
group = models.IntegerField(default=1)

def __init__(self, base_word):
self.original = base_word.original
self.translation = base_word.translation

这会导致很多错误,因为我重写了 django 的内置 __init__ 方法。

2) 使用super().__init__():

class Verb(Word):
group = models.IntegerField(default=1)

def __init__(self, base_word):
super().__init__()
self.original = base_word.original
self.translation = base_word.translation

显然,这效果很好:

base_word = Word()
new_verb = Verb(base_word)
new_verb.save()

但是有两个问题:

  1. 尝试查看 django 管理页面中的对象时会导致错误:
__init__() takes 2 positional arguments but 9 were given
  • 这代码还是太多了,感觉不太对劲。我还需要写这个:
  • self.original = base_word.original
    self.translation = base_word.translation

    在每个子类中。这只是一个例子。在实际项目中,我有更多的领域。我想有一个更优雅的解决方案。

    最佳答案

    重写__init__不是执行此操作的正确方法。 Django 模型执行大量幕后工作,这些工作可能会与重写 __init__ 发生冲突,除非您遵循以下规则以安全的方式执行此操作:

    • 不要更改 __init__ 的签名 - 这意味着您不应更改该方法接受的参数。
    • 调用 super().__init__(*args, **kwargs) 方法后,执行自定义 __init__ 逻辑。
    • >

    在这种特殊情况下,您可以使用 django's proxy model inheritance features .

    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.
    """

    关于python - 如何安全地继承 django models.Model 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59686446/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com