如何更改 Django 模型子类中的字段参数?

发布于 12-12 06:46 字数 362 浏览 0 评论 0原文

假设我有一些 Django 模型,它是一个抽象基类:

class Foo(models.Model):
    value=models.IntegerField()

    class Meta:
        abstract = True

它有两个派生类,我希望每个子类的字段默认值都不同。我不能简单地覆盖该字段

class Bar(Foo):
    value=models.IntegerField(default=9)

,因为 Django 不允许您覆盖子类中的字段。我看过有关尝试更改可用选项的帖子,但在这种情况下,我最关心的是更改默认值。有什么建议吗?

Let's say I have some Django model that is an abstract base class:

class Foo(models.Model):
    value=models.IntegerField()

    class Meta:
        abstract = True

and it has two derived classes, where I'd like the default value of the field to be different for each child classes. I can't simply override the field

class Bar(Foo):
    value=models.IntegerField(default=9)

because Django won't let you override fields in subclasses. I've seen posts about trying to changes available choices, but in this case I care mostly about changing the default value. Any advice?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

奶气2024-12-19 06:46:28

按照另一个答案中的建议重新定义保存方法的问题是,在保存模型对象之前,不会设置您的值。
不存在此问题的另一个选择是在子类(Bar 类)中重新定义 __init__ :

def __init__(self, *args, **kwargs):
    if 'value' not in kwargs:
        kwargs['value'] = 9
    super(Bar, self).__init__(*args, **kwargs)

The problem with redefining the save method as suggested in the other answer is that your value will not be set until you save your model object.
Another option that doesn't have this problem is to redefine the __init__ in the child class (Bar class):

def __init__(self, *args, **kwargs):
    if 'value' not in kwargs:
        kwargs['value'] = 9
    super(Bar, self).__init__(*args, **kwargs)
烟雨凡馨2024-12-19 06:46:28

请参阅 https://stackoverflow.com/a/6379556/15690

您实际上可以按如下方式执行此操作:

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

BaseMessage._meta.get_field('is_public').default = True

class Message(BaseMessage):
    # some fields...

See https://stackoverflow.com/a/6379556/15690:

You can actually do this as follows:

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

BaseMessage._meta.get_field('is_public').default = True

class Message(BaseMessage):
    # some fields...
无尽的现实2024-12-19 06:46:28

我认为你想要做的事情是不可能的,至少在 Django 中是不可能的。您必须将 Django 中的继承视为超类的外键(几乎就是这样),并且您无法更改 FK 关系中属性的默认值。

因此,您能做的最好的事情就是重新定义 save() 方法。它会类似于:

def save(self, *args, **kwargs):
    if not self.value:
        self.value = 9
    super(Bar, self).save(*args, **kwargs)

And, in Foo (super-class):

    value = models.IntegerField(blank=True)

以避免数据库出现 NOT NULL 问题。

I think that what you're trying to do is not possible, at least not in Django. You have to see inheritance in Django as a ForeignKey to the super-class (that's pretty much it), and you can't change the default value of an attribute in a FK relation.

So, the best thing you could do is to redefine the save() method. It would be something like:

def save(self, *args, **kwargs):
    if not self.value:
        self.value = 9
    super(Bar, self).save(*args, **kwargs)

And, in Foo (super-class):

    value = models.IntegerField(blank=True)

to avoid NOT NULL problems with the database.

莫相离2024-12-19 06:46:28

这定义了一个元类(和一个基类),为所有子类提供给定类型的字段,默认值可以在子类中设置,但不一定是:

from django.db import models
class DefaultTextFieldMetaclass(models.base.ModelBase):
    DEFAULT_TEXT = 'this is the metaclass default'

    def __new__(mcs, name, parents, _dict):
        if not (('Meta' in _dict) and hasattr(_dict['Meta'], 'abstract') and _dict['Meta'].abstract):
            # class is concrete
            if 'DEFAULT_TEXT' in _dict:
                default = _dict['DEFAULT_TEXT']
            else:       # Use inherited DEFAULT_TEXT if available
                default_set = False
                for cls in parents:
                    if hasattr(cls, 'DEFAULT_TEXT'):
                        default = cls.DEFAULT_TEXT
                        default_set = True
                if not default_set:
                    default = mcs.DEFAULT_TEXT
            _dict['modeltext'] = models.TextField(default=default)
        return super(DefaultTextFieldMetaclass, mcs).__new__(mcs, name, parents, _dict)


class BaseTextFieldClass(models.Model):
    class Meta(object):
        abstract = True
    __metaclass__ = DefaultTextFieldMetaclass
    DEFAULT_TEXT = 'superclass default'


class TextA(BaseTextFieldClass):
    DEFAULT_TEXT = 'A default for TextA'

class TextB(BaseTextFieldClass):
    DEFAULT_TEXT = 'The default for TextB'
    number = models.IntegerField(default=43)

class TextC(BaseTextFieldClass):
    othertext = models.TextField(default='some other field')

除非你有一堆子类和/或多个与 BaseTextFieldClass 相关的方法/属性和假设,这可能有点矫枉过正......但它应该按照OP的要求去做。

This defines a metaclass (and a base class) that provides all subclasses a field of a given type, with defaults that can be set in the subclass, but don't have to be:

from django.db import models
class DefaultTextFieldMetaclass(models.base.ModelBase):
    DEFAULT_TEXT = 'this is the metaclass default'

    def __new__(mcs, name, parents, _dict):
        if not (('Meta' in _dict) and hasattr(_dict['Meta'], 'abstract') and _dict['Meta'].abstract):
            # class is concrete
            if 'DEFAULT_TEXT' in _dict:
                default = _dict['DEFAULT_TEXT']
            else:       # Use inherited DEFAULT_TEXT if available
                default_set = False
                for cls in parents:
                    if hasattr(cls, 'DEFAULT_TEXT'):
                        default = cls.DEFAULT_TEXT
                        default_set = True
                if not default_set:
                    default = mcs.DEFAULT_TEXT
            _dict['modeltext'] = models.TextField(default=default)
        return super(DefaultTextFieldMetaclass, mcs).__new__(mcs, name, parents, _dict)


class BaseTextFieldClass(models.Model):
    class Meta(object):
        abstract = True
    __metaclass__ = DefaultTextFieldMetaclass
    DEFAULT_TEXT = 'superclass default'


class TextA(BaseTextFieldClass):
    DEFAULT_TEXT = 'A default for TextA'

class TextB(BaseTextFieldClass):
    DEFAULT_TEXT = 'The default for TextB'
    number = models.IntegerField(default=43)

class TextC(BaseTextFieldClass):
    othertext = models.TextField(default='some other field')

Unless you have a bunch of subclasses and/or multiple methods/attributes and assumptions that tie into the BaseTextFieldClass, this is probably overkill... but it should do what OP requested.

撧情箌佬2024-12-19 06:46:28

这可能是一个较新的功能,但在 Django 4.2.4 中,我可以重新定义该字段,并且 Django 会在迁移文件中正确地重新创建它。

class Foo(models.Model):
    value=models.IntegerField()

    class Meta:
        abstract = True

class Bar(Foo):
    value=models.IntegerField(default=9)

It is likely a newer feature, but in Django 4.2.4, I am able to just redefine the field and Django properly recreates it in the migration file.

class Foo(models.Model):
    value=models.IntegerField()

    class Meta:
        abstract = True

class Bar(Foo):
    value=models.IntegerField(default=9)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文