让我们看看我是否可以解释自己,我有这个模型:
class BillHeader(models.Model):
number = models.CharField(_('Bill number'), max_length=10, unique=True, \
default=__number)
client = models.ForeignKey(ClienteYProveedor, verbose_name=_('Client'))
date = models.DateTimeField(_('Date'), default=datetime.now)
def __unicode__(self):
return str(self.number)
class Meta:
abstract = True
class BillFooter(models.Model):
base_import = models.DecimalField(_('Base import'), max_digits=12, \
decimal_places=2)
class Meta:
abstract = True
class BillBody(models.Model):
description = models.CharField(_('Description'), max_length=200)
amount = models.DecimalField(_('Amount'), max_digits=6, decimal_places=2)
discount = models.DecimalField(_('Discount'), max_digits=4, \
decimal_places=2)
price = models.DecimalField(_('Price'), max_digits=12, decimal_places=2)
unitaryprice = models.DecimalField(_('Unitary Price'), max_digits=12, \
decimal_places=2)
def __unicode__(self):
return self.description
class Meta:
abstract = True
class EmittedBill(BillHeader, BillBody, BillFooter):
pass
class ReceivedBill(BillHeader, BillBody, BillFooter):
pass
当用户添加Emmited或Received帐单时,我需要将BillHeader
显示为普通字段集,但BillBody
和BillFooter
必须为TabularInline
。
如果我将这些作为TabularInline放在admin.py中,则会出现错误,表示他们需要ForeignKey
相关模型。当然,我不能把那些外键,因为它们是在底部声明的。我想你们都称之为“倒退的外键”。
我的问题是:我怎么能这样做才能在管理员中显示TabularInlines而不会弄得一团糟?我可以在没有抽象基类的情况下做到这一点,但随后又出现了另一个问题,它显示了TabularInline中的其他ForeignKey(如果你在EmmitedBills上它显示了TabularInline中的FK到ReceivedBills,反之亦然)我无法排除它们。
很抱歉,如果这是一个愚蠢的问题,但我不是程序员(这只是一个爱好)而且我真的让自己陷入数据模型混乱。
我会更好地解释:
我有两种类型的帐单Emitted
和Received
,它们都显示在管理员家中(这就是为什么我没有使用BooleanField
来标记它们)。这两种类型都有相同的字段,除了一个,即帐号,在Emmitted
中将自动生成。每个账单包括1个标题,包括数字,客户和日期,1个或多个主体内联条目,包含描述,金额,价格等,以及1个内联页脚,显示不含税的总价,应用税等。
更新
我已经完成了所有工作,但我有一个问题,在我的新模型中,BillBody有两个FK(EmmitedBill和ReceivedBill),它们出现在TabularInline中。如何隐藏它们?field.exclude()给出错误。
答案 0 :(得分:2)
我不完全理解你的问题,但你可以使用
ForeignKey('ModelName')
而不是
ForeignKey(ModelName)
如果尚未声明ModelName
模型。也许这解决了你的问题。
内联管理员(例如TabularInline
)仅在您具有一对多关系时使用,该关系由许多侧的ForeignKey
创建。如果您没有这样的外键,则无法使用内联管理员。继承绝对不同于ForeignKey
。
但是,我认为您的数据模型是错误的。看起来你确实想存钱。有两种类型的账单,emitted
和received
账单。 emitted
和received
帐单都有相同的字段。此外,您希望每个帐单包含一个带有数字,客户和日期,一个或多个正文条目的标题,其中每个条目都存储您存储在BillBody
中的信息以及一个或多个小数base_number
。
可能更适合您的数据模型
class Bill(models.Model):
number = models.CharField(_('Bill number'), max_length=10, unique=True, default=__number)
client = models.ForeignKey(ClienteYProveedor, verbose_name=_('Client'))
date = models.DateTimeField(_('Date'), default=datetime.now)
def get_total_price(self):
return sum([entry.price for entry in self.entries])
class BillEntry(models.Model):
bill = models.ForeignKey(Bill, related_name='entries')
description = models.CharField(_('Description'), max_length=200)
amount = models.DecimalField(_('Amount'), max_digits=6, decimal_places=2)
discount = models.DecimalField(_('Discount'), max_digits=4, decimal_places=2)
price = models.DecimalField(_('Price'), max_digits=12, decimal_places=2)
unitaryprice = models.DecimalField(_('Unitary Price'), max_digits=12, decimal_places=2)
我遗漏了__unicode__
方法。
现在您有一个从BillEntry
到Bill
的外键,您可以使用表格内联。我不明白你对base_import
的使用情况,所以我把它留了出来。
价格计算
如果您的price
应该总是等于amount*unitaryprice - discount
或amount*(unitaryprice-discount)
,那么您不应该将其放在字段中,而是在需要时计算它,无论是在Python中还是在数据库。如果要在Python中执行此操作,可以使用类似于get_total_price
的方法。如果你想在查询数据库时计算它们,那么让它与Django一起使用会有点困难。
在最后一种情况下,您可以查看SQL视图,但我认为这对于初学者来说有点太难了。另一种选择是使用自定义SQL表达式:
BillEntry.objects.extra(select={'price': 'amount*unitaryprice-discount'})
这将计算选择期间所有条目的价格。
<强>更新强>
如果为已发出和已接收的帐单添加两个子类并使用multi table inheritance,则可以使用从BillEntry
到Bill
的一个外键。
class EmittedBill(Bill):
pass
class ReceivedBill(Bill):
pass
您可能还需要考虑Django生成的数据库模型。通常,您只想在数据库中存储基本数据,而不是计算数据(就像您想要在页脚中一样)。因此,如果使用某个公式计算价格并使用unitaryprice
,amount
等,则不应存储此公式的结果,而是在必要时重新计算(最终缓存以避免重新计算) 。如果你不这样做,可能会在某些时候更新某些内容(例如amount
)并忘记更新计算值(price
),这会导致数据库中的不一致(因此你的应用程序中的错误)。一个好的数据库确实有约束,因此在不破坏至少一个约束的情况下不可能存储不一致的数据库。
我也不明白为什么你需要为每个账单单独的页眉和页脚。模型不是真正的账单,它存储账单的信息。如果您想要一个可见的页眉和页脚,那么您应该在视图层(模板)而不是模型本身中执行此操作。