Django 从继承字段序列化子模型
Django serialize child model from inheritance field
我正在使用 DRF,我的模型似乎存在设计问题:
背景:
我正在做一个财务管理的个人项目,所以我有 SavingsAccount
、CreditCard
、Incomes
的模型(这确实很简单,但我认为这足以看出问题所在)。
问题:
在收入中,我应该能够跟踪我将钱添加到哪个帐户,但这可能是储蓄帐户或信用卡,所以我制作了另一个具有两者共同属性的模型,称为 Account
。
我正在使用 InheritanceManager
以使其更容易一些。
from model_utils.managers import InheritanceManager
class Account(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=120)
balance = models.DecimalField(decimal_places=4, max_digits=12)
objects = InheritanceManager()
def get_child(self):
return Account.objects.get_subclass(pk=self.pk)
class SavingsAccount(Account):
bank = models.CharField(max_length=120)
is_investment = models.BooleanField(default=0)
class CreditCard(Account):
cut = models.IntegerField()
pay = models.IntegerField()
bank = models.CharField(max_length=120)
credit = models.DecimalField(decimal_places=4, max_digits=12)
@property
def used(self):
return self.credit - self.balance
class Income(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
amount = models.DecimalField(decimal_places=4, max_digits=12)
account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="incomes")
description = models.TextField()
date = models.DateField()
有了这个我就可以通过以下方式与帐户进行交互:income = Income.objects.select_related("account").first()
然后 income.account.get_child()
以检索 SavingsAccount 或 CreditCard 对象,问题出在序列化程序上,因为我想发送子对象而不是帐户对象。
我目前的(不完整的)解决方案:
使用 rest-framework-generic-relations 我得到了
class IncomeSerializer(serializers.ModelSerializer):
account = GenericRelatedField({
CreditCard: CreditCardSerializers(),
SavingsAccount: SavingsAccountSerializers(),
})
class Meta:
Model = Income
fields = ("id", "amount", "account", "description")
这失败了,因为序列化程序获取的帐户对象是 Account
类型,我怎样才能得到一个带有子帐户 类 而不是默认帐户的收入查询集?
这是个坏主意吗?我应该如何实施这样的事情?最好只发送帐户对象的 ID,然后对该对象执行另一个请求?
非常感谢,我尝试提供所有需要的信息,但请告诉我是否需要添加更多信息。
我一直避免使用 ContentType,因为我认为这里不需要它。
好吧,现在我是这样做的:
class AccountSerializer(serializers.ModelSerializer):
def to_representation(self, value):
child = value.get_child()
if isinstance(child, SavingsAccount):
serializer = SavingsAccountSerializer(child)
elif isinstance(child, Wallet):
serializer = WalletSerializer(child)
else:
raise Exception('Unexpected type of tagged object')
return serializer.data
class Meta:
model = Account
class WalletSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = ('__all__')
class SavingsAccountSerializer(serializers.ModelSerializer):
class Meta:
model = SavingsAccount
fields = ('__all__')
class IncomeSerializer(serializers.ModelSerializer):
account = AccountSerializer(read_only=True)
class Meta:
model = Income
fields = ("__all__")
我确信这不是最有效的方法,因为每个 object 中 get_child
方法使用的连接使用了 n
个查询(其中n 是收入中 object 的数量),我很确定你可以用 2 个查询来做到这一点(每个 child class 一个)但我只是在学习,希望更有经验的人会 post 给出更好的答案。
尽管如此,我还是让这个帮助像我一样在小项目中工作的人。
注意:
我只是用它来检索信息,我还没有测试它的检索信息。
我正在使用 DRF,我的模型似乎存在设计问题:
背景:
我正在做一个财务管理的个人项目,所以我有 SavingsAccount
、CreditCard
、Incomes
的模型(这确实很简单,但我认为这足以看出问题所在)。
问题:
在收入中,我应该能够跟踪我将钱添加到哪个帐户,但这可能是储蓄帐户或信用卡,所以我制作了另一个具有两者共同属性的模型,称为 Account
。
我正在使用 InheritanceManager
以使其更容易一些。
from model_utils.managers import InheritanceManager
class Account(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=120)
balance = models.DecimalField(decimal_places=4, max_digits=12)
objects = InheritanceManager()
def get_child(self):
return Account.objects.get_subclass(pk=self.pk)
class SavingsAccount(Account):
bank = models.CharField(max_length=120)
is_investment = models.BooleanField(default=0)
class CreditCard(Account):
cut = models.IntegerField()
pay = models.IntegerField()
bank = models.CharField(max_length=120)
credit = models.DecimalField(decimal_places=4, max_digits=12)
@property
def used(self):
return self.credit - self.balance
class Income(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
amount = models.DecimalField(decimal_places=4, max_digits=12)
account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="incomes")
description = models.TextField()
date = models.DateField()
有了这个我就可以通过以下方式与帐户进行交互:income = Income.objects.select_related("account").first()
然后 income.account.get_child()
以检索 SavingsAccount 或 CreditCard 对象,问题出在序列化程序上,因为我想发送子对象而不是帐户对象。
我目前的(不完整的)解决方案: 使用 rest-framework-generic-relations 我得到了
class IncomeSerializer(serializers.ModelSerializer):
account = GenericRelatedField({
CreditCard: CreditCardSerializers(),
SavingsAccount: SavingsAccountSerializers(),
})
class Meta:
Model = Income
fields = ("id", "amount", "account", "description")
这失败了,因为序列化程序获取的帐户对象是 Account
类型,我怎样才能得到一个带有子帐户 类 而不是默认帐户的收入查询集?
这是个坏主意吗?我应该如何实施这样的事情?最好只发送帐户对象的 ID,然后对该对象执行另一个请求?
非常感谢,我尝试提供所有需要的信息,但请告诉我是否需要添加更多信息。
我一直避免使用 ContentType,因为我认为这里不需要它。
好吧,现在我是这样做的:
class AccountSerializer(serializers.ModelSerializer):
def to_representation(self, value):
child = value.get_child()
if isinstance(child, SavingsAccount):
serializer = SavingsAccountSerializer(child)
elif isinstance(child, Wallet):
serializer = WalletSerializer(child)
else:
raise Exception('Unexpected type of tagged object')
return serializer.data
class Meta:
model = Account
class WalletSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = ('__all__')
class SavingsAccountSerializer(serializers.ModelSerializer):
class Meta:
model = SavingsAccount
fields = ('__all__')
class IncomeSerializer(serializers.ModelSerializer):
account = AccountSerializer(read_only=True)
class Meta:
model = Income
fields = ("__all__")
我确信这不是最有效的方法,因为每个 object 中 get_child
方法使用的连接使用了 n
个查询(其中n 是收入中 object 的数量),我很确定你可以用 2 个查询来做到这一点(每个 child class 一个)但我只是在学习,希望更有经验的人会 post 给出更好的答案。
尽管如此,我还是让这个帮助像我一样在小项目中工作的人。
注意: 我只是用它来检索信息,我还没有测试它的检索信息。