避免在Django中创建父模型类

时间:2017-07-04 20:13:58

标签: django

我有以下数据模型:

class PaymentGateway(models.Model):
    GATEWAYS = Choices(
        ('stripe', _('Stripe')),
        ('paybox', _('PayBox')),
        ('generic', _('Generic')),
    )

    tenant = models.ForeignKey(Tenant, 
related_name="payment_gateways")
    name = models.CharField(
        max_length=50,
        verbose_name=_('Name'),
        choices=GATEWAYS,
        default=GATEWAYS.generic
    )

class StripeGateway(PaymentGateway):
    stripe_public_key = models.CharField(max_length=255)
    stripe_key = models.CharField(max_length=255)

    class Meta:
        verbose_name = u'Stripe Gateway'

    def __init__(self, *args, **kwargs):
        super(StripeGateway, self).__init__(*args, **kwargs)
        self.name = PaymentGateway.GATEWAYS.stripe

    def __unicode__(self):
        return "Stripe data for %(school_name)s" % {"school_name": self.school.name}


class EtransactionGateway(PaymentGateway):
    pbx_site = models.IntegerField()
    pbx_rang = models.IntegerField()
    pbx_identifiant = models.IntegerField()
    pbx_devise = models.IntegerField()
    pbx_secret = models.CharField(max_length=255)

    class Meta:
        verbose_name = u'Etransaction Gateway'

    def __init__(self, *args, **kwargs):
        super(EtransactionGateway, self).__init__(*args, **kwargs)
        self.name = PaymentGateway.GATEWAYS.paybox

    def __unicode__(self):
        return "E-transaction data for %(school_name)s" % {"school_name": self.school.name}

这很好,但是当我创建StripeGateway或EtransactionGateway的实例时,我可以看到另一个PaymentGateway实例。

我已尝试按[建议] [https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name-aEDIT创建PaymentGateway摘要:我已根据评论尝试将抽象添加到父类,然后我运行与runnd相关的查询-name)但是当我这样做并运行迁移时(我已经使用当前场景运行了第一次迁移)然后我收到错误,说eTransactionGateway中的id字段与来自paymentGateway的那个冲突(抱歉,我可以&#t; t现在找到踪迹。)

现在这没关系,但我真的不喜欢PaymentGateway的实例,或者至少有backref payment_gateways返回子类,因为现在这样的事情:

tenant = Tenant.objects.get(name='tenant_1')
gws = tenant.payment_gateways.all()

返回对我没有任何价值的父实例。

有什么建议吗?

编辑:我已根据评论尝试将抽象添加到父类,然后运行

所以类现在看起来像这样:

class PaymentGateway(models.Model):
    GATEWAYS = Choices(
        ('stripe', _('Stripe')),
        ('paybox', _('PayBox')),
        ('generic', _('Generic')),
    )

    tenant = models.ForeignKey(Tenant, related_name = "%(app_label)s_%(class)s_related")

    name = models.CharField(
        max_length=50,
        verbose_name=_('Name'),
        choices=GATEWAYS,
        default=GATEWAYS.generic
    )

    class Meta:
        abstract = True


class StripeGateway(PaymentGateway):
    stripe_public_key = models.CharField(max_length=255)
    stripe_key = models.CharField(max_length=255)

    class Meta:
        verbose_name = u'Stripe Gateway'

    def __init__(self, *args, **kwargs):
        super(StripeGateway, self).__init__(*args, **kwargs)
        self.name = PaymentGateway.GATEWAYS.stripe

    def __unicode__(self):
        return "Stripe data for %(school_name)s" % {"school_name": self.school.name}


class EtransactionGateway(PaymentGateway):
    pbx_site = models.IntegerField()
    pbx_rang = models.IntegerField()
    pbx_identifiant = models.IntegerField()
    pbx_devise = models.IntegerField()
    pbx_secret = models.CharField(max_length=255)


    class Meta:
        verbose_name = u'Etransaction Gateway'

    def __init__(self, *args, **kwargs):
        super(EtransactionGateway, self).__init__(*args, **kwargs)
        self.name = PaymentGateway.GATEWAYS.paybox

    def __unicode__(self):
        return "E-transaction data for %(school_name)s" % {"school_name": self.school.name}

我先重置数据库

python manage.py reset_db

然后

python manage.py makemigrations

得到这样的消息:

You are trying to add a non-nullable field 'id' to etransactiongateway without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Select an option: 


You are trying to add a non-nullable field 'tenant' to stripegateway without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:

1)现在提供一次性默认值(将在所有现有行上设置)    2)退出,让我在models.py

中添加默认值

我选择1,因为我甚至没有在我的子类中使用id字段(这是由Django内部管理),对于第二条消息,我使用了一个租户的id我已经知道存在于id为1的db中

之后获取此输出:

Migrations for 'payment':
0010_auto_20170704_2305.py:
  - RemovEDIT: I've tried, based on comments, to add abstract to parent class and then I rune field tenat from paymentgateway
  - Remove field paymentgateway_ptr from etransactiongateway
  - Remove field paymentgateway_ptr from stripegateway
  - Add field id to etransactiongateway
  - Add field name to etransactiongateway
  - Add field tenant to etransactiongateway
  - Add field id to stripegateway
  - Add field name to stripegateway
  - Add field tenant to stripegateway
  - Delete model PaymentGateway

此迁移对我来说很奇怪,但无论如何,让我们尝试迁移,这是输出:

  Applying payment.0010_auto_20170704_2305...Traceback (most recent call last):
  File "manage.py", line 25, in <module>
execute_from_command_line(sys.argv)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
utility.execute()
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 345, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/core/management/base.py", line 348, in run_from_argv
self.execute(*args, **cmd_options)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/core/management/base.py", line 399, in execute
output = self.handle(*args, **options)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 200, in handle
executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 92, in migrate
self._migrate_all_forwards(plan, full_plan, fake=fake, fake_initial=fake_initial)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 121, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 198, in apply_migration
state = migration.apply(state, schema_editor)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/migration.py", line 115, in apply
operation.state_forwards(self.app_label, project_state)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/operations/fields.py", line 51, in state_forwards
state.reload_model(app_label, self.model_name_lower)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 148, in reload_model
self.apps.render_multiple(states_to_be_rendered)
 File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 296, in render_multiple
model.render(self)
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 585, in render
body,EDIT: I've tried, based on comments, to add abstract to parent class and then I run
  File "/home/acme/python_envs/acme/local/lib/python2.7/site-packages/django/db/models/base.py", line 223, in __new__
'base class %r' % (field.name, name, base.__name__)
django.core.exceptions.FieldError: Local field u'id' in class 'EtransactionGateway' clashes with field of similar name from base class 'PaymentGateway'

1 个答案:

答案 0 :(得分:0)

如果您不想在数据库中为父模型创建表,可以在模型的元类中添加abstract=True

class PaymentGateway(models.Model):
    ...
    class Meta:
        abstract = True

来源:Django abstract base classes