首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Dajngo模型嵌入多个抽象模型

Dajngo模型嵌入多个抽象模型
EN

Stack Overflow用户
提问于 2020-12-18 20:37:16
回答 2查看 36关注 0票数 0

假设我有一个用于在我的站点上存储客户订单的Order模型。将有两个地址字段:shipping_addressbilling_address。我可以写成这样:

代码语言:javascript
复制
class Order(models.Model):
  order_number = models.CharField()
  time_created = models.DateTimeField()
  ...
  shipping_name = models.CharField()
  shipping_city = models.CharField()
  shipping_street = models.CharField()
  shipping_building = models.CharField()
  billing_name = models.CharField()
  billing_city = models.CharField()
  billing_street = models.CharField()
  billing_building = models.CharField()
  ...

然后通过order.shipping_nameorder.billing_name访问字段,但是当每个地址都需要包含15-20个字段时,这会变得非常麻烦。

我尝试了一对一的关系,但是应用程序的其他地方的代码变得非常复杂。

如果只有一个地址字段,我可以使用抽象模型来使其变得更清晰:

代码语言:javascript
复制
class Address(models.Model):
  class Meta:
    abstract = True
  name = models.CharField()
  city = models.CharField()
  street = models.CharField()
  building = models.CharField()
  ...

class Order(Address, models.Model):
  order_number = models.CharField()
  time_created = models.DateTimeField()

但是这样,我只能拥有一组地址数据,Address类中的字段名将与来自Order的字段发生冲突。

是否有一种方法将具有给定前缀的字段注入或嵌入到给定模型中?我正在寻找类似于Spring模型中使用的@Embedded装饰器的东西。

我会想象它看起来是这样的:

代码语言:javascript
复制
class Address(models.Model):
  class Meta:
    abstract = True
  name = models.CharField()
  city = models.CharField()
  street = models.CharField()
  building = models.CharField()
  ...

class Order(models.Model):
  order_number = models.CharField()
  time_created = models.DateTimeField()
  shipping_address = Embedded(Address, prefix='shipping')
  billing_address = Embedded(Address, prefix='billing')

然后通过order.shipping_address.city访问字段,或者不破坏其他地方的代码,也许order.shipping_address_city也能工作。

编辑:使用JSONField或HStoreField可以实现类似的功能吗?

EN

回答 2

Stack Overflow用户

发布于 2022-07-27 08:32:28

经过大量的尝试和错误之后,使用来自https://stackoverflow.com/a/55051990/345811的模型工厂的方法似乎对我来说是最好的。

下面是示例models.py实现:

代码语言:javascript
复制
from django.db import models
from django.utils.translation import gettext_lazy as _

from app.countries import COUNTRIES  # A list of country code/name tuples

ADDRESS_FIELDS = (
    ('first_name', dict(verbose_name=_('First name'), max_length=256)),
    ('last_name', dict(verbose_name=_('Last name'), max_length=256)),
    ('company', dict(verbose_name=_('Company'), max_length=256, required=False)),
    ('street', dict(verbose_name=_('Street address'), max_length=256)),
    ('street2', dict(verbose_name=_('Street address line 2'), max_length=256, required=False)),
    ('street3', dict(verbose_name=_('Street address line 3'), max_length=256, required=False)),
    ('postal_code', dict(verbose_name=_('Postal code'), max_length=16)),
    ('city', dict(verbose_name=_('City'), max_length=64)),
    ('province', dict(verbose_name=_('Province'), max_length=64, required=False)),
    ('country', dict(verbose_name=_('Country'), choices=COUNTRIES, max_length=2)),
)


def address_model_factory(field_prefix, force_optional=False):
    """
    Usage:
    class SomeModel(address_model_factory(''), address_model_factory('billing', True))
    """
    class AbstractAddressModel(models.Model):
        class Meta:
            abstract = True

    for field_name, field_def in ADDRESS_FIELDS:
        required = field_def.get('required', True)
        field_kwargs = {k: v for k, v in field_def.items() if k != 'required'}
        if force_optional or not required:
            field_kwargs.update(blank=True, default="")
        AbstractAddressModel.add_to_class(field_name if not field_prefix else f'{field_prefix}_{field_name}',
                                          models.CharField(**field_kwargs)
                                          )

    return AbstractAddressModel


class Shipment(address_model_factory(''), address_model_factory('billing', True)):
    status = models.Charfield(_('Shipment status'), max_length=20)

会产生这样的迁移:

代码语言:javascript
复制
class Migration(migrations.Migration):

    initial = True

    operations = [
        migrations.CreateModel(
            name='Shipment',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('first_name', models.CharField(max_length=256, verbose_name='First name')),
                ('last_name', models.CharField(max_length=256, verbose_name='Last name')),
                ('company', models.CharField(blank=True, default='', max_length=256, verbose_name='Company')),
                ('street', models.CharField(max_length=256, verbose_name='Street address')),
                ('street2', models.CharField(blank=True, default='', max_length=256, verbose_name='Street address line 2')),
                ('street3', models.CharField(blank=True, default='', max_length=256, verbose_name='Street address line 3')),
                ('postal_code', models.CharField(max_length=16, verbose_name='Postal code')),
                ('city', models.CharField(max_length=64, verbose_name='City')),
                ('province', models.CharField(blank=True, default='', max_length=64, verbose_name='Province')),
                ('country', models.CharField(choices=[('DE', 'Germany'), ('GB', 'United Kingdom')], max_length=2, verbose_name='Country')),
                ('billing_first_name', models.CharField(blank=True, default='', max_length=256, verbose_name='First name')),
                ('billing_last_name', models.CharField(blank=True, default='', max_length=256, verbose_name='Last name')),
                ('billing_company', models.CharField(blank=True, default='', max_length=256, verbose_name='Company')),
                ('billing_street', models.CharField(blank=True, default='', max_length=256, verbose_name='Street address')),
                ('billing_street2', models.CharField(blank=True, default='', max_length=256, verbose_name='Street address line 2')),
                ('billing_street3', models.CharField(blank=True, default='', max_length=256, verbose_name='Street address line 3')),
                ('billing_postal_code', models.CharField(blank=True, default='', max_length=16, verbose_name='Postal code')),
                ('billing_city', models.CharField(blank=True, default='', max_length=64, verbose_name='City')),
                ('billing_province', models.CharField(blank=True, default='', max_length=64, verbose_name='Province')),
                ('billing_country', models.CharField(blank=True, choices=choices=[('DE', 'Germany'), ('GB', 'United Kingdom')], default='', max_length=2, verbose_name='Country')),
                ('status', models.CharField(max_length=20, verbose_name='Shipment status')),
            ],
            options={},
        ),
    ]
票数 0
EN

Stack Overflow用户

发布于 2020-12-18 21:24:53

嵌入另一个模型的一部分作为引用可能是不可能的,而且,它肯定是非标准的方式。唯一推荐的解决方案是使用两种不同的模型:OrderAddress。然后按顺序将地址指定为ForeignKey

代码语言:javascript
复制
class Order(models.Model):
    ...
    shipping_address = models.ForeignKey(
        Address, on_delete=models.SET_NULL, related_name="shipped_orders",
        blank=True, null=True
    )
    invoice_address = models.ForeignKey(
        Address, on_delete=models.SET_NULL, related_name="invoiced_orders",
        blank=True, null=True
    )

它将显示为一个选择框在管理,您可以选择发货和发票地址。为了使它更简单,只需在管理中使用字段即可。

如果您需要在管理页面中进行一些验证,同时添加一个订单,您可以设置一个将在管理页面中使用的模型,文档的这一部分描述了如何做到这一点。在您的自定义订单表单中,您可以验证像def clean_shipping_address(self)这样的字段,更详细地描述这里

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65363610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档