【发布时间】:2019-12-30 19:53:55
【问题描述】:
我有一个使用 Django 2.2.4 的项目。
我有一个名为 Company 的 Django 模型。
我使用 post_save 信号来确保在创建新公司后立即创建一个名为“Billing”的新模型实例,该实例与该公司相关联。这包含公司的帐单信息。这很好用。
由于我的 Billing 对象与 Company 关联,并且我使用 on_delete=models.CASCADE,因此一旦删除 Company,与该公司关联的 Billing 对象也会自动删除。这也很好用。
由于每个公司的 Billing 对象现在与 Company 一起自动创建和删除,因此使用 Django Admin Web 界面的管理员无需手动创建或删除 Billing 对象。我想对他们隐藏这个功能。
通常,阻止 Django Admin 允许某人添加或删除对象的常用方法是在 admin.py 中将其添加到该模型的 ModelAdmin:
class BillingAdmin(admin.ModelAdmin):
...
# Prevent deletion from admin portal
def has_delete_permission(self, request, obj=None):
return False
# Prevent adding from admin portal
def has_add_permission(self, request, obj=None):
return False
这可行,并且确实隐藏了管理员手动创建或删除 Billing 对象实例的能力。然而,它确实有一个负面影响:Django Admin 用户不能再删除公司。删除公司时,Django 会查找所有需要删除的关联对象,通知用户不允许删除关联的 Billing 对象,并阻止用户删除公司。
虽然我不希望 Django Admin 用户能够手动创建或删除 Billing 模型的实例,但我仍然希望他们能够删除整个 Company,这将导致删除与该公司关联的计费模式。
就我而言,阻止用户删除 Billing 模型的实例与其说是一项安全功能,不如说是一项安全功能,因为它旨在防止混淆,不让数据库最终处于存在公司的状态,但是它不存在计费对象。 Django 显然不会有这个问题,但它会让用户感到困惑。
有解决办法吗?
更新:
使用has_delete_permission 设置,如果您尝试通过 Django Admin 删除公司,您会得到:
不抛出异常。至少没有一个未被捕获并出现在 Django 日志中。
我的模型如下所示:
class Company(Group):
...
class Billing(models.Model):
company = AutoOneToOneField('Company', on_delete=models.CASCADE, blank=False, null=False, related_name="billing")
monthly_rate = models.DecimalField(max_digits=10, decimal_places=2, default=0, blank=False, null=False)
# Create billing object for a company when it is first created
@receiver(post_save, sender=Company)
def create_billing_for_company(sender, instance, created, *args, **kwargs):
if created:
Billing.objects.create(company=instance)
AutoOneToOneField 是 django-annoying 的一部分。它确保如果您运行 MyCompany.billing,并且关联的计费对象尚不存在,则会自动创建一个,而不是引发异常。这里可能不需要,因为我在创建公司时会自动创建对象,但这不会造成伤害,并确保我的代码永远不必担心关联的对象不存在。
另外请注意,我没有覆盖我的计费模型的 delete 函数。
【问题讨论】:
-
意见:你不应该删除任何东西。我的意思是最好的方式,但最终用户不擅长不把事情搞砸。你为什么要让他们删除一个值然后级联它???在模型上添加状态/活动字段并通过将其设置为 0 来“软删除”它。删除保留给 DBA 或最终用户以外的人。
-
@dfundako 我们不是在谈论用户。我们正在谈论管理员。 Django Admin Web 界面的用户。由于 Django Admin Web 界面本质上是一个进入 DB 的界面,因此这些用户或多或少是 DBA。而
people are bad at not messing things up正是我试图阻止他们删除 Billing 对象的原因,该对象应该在公司存在时存在,而在公司被删除时不存在。 -
恕我直言:我认为@dfundako 是在谈论任何类型的用户,管理员是一种用户,我同意避免删除是最佳做法,管理员的不当行为可能会导致大问题在您的系统中。通过布尔值(活动/非活动)更改删除是最好的方法,如果您的问题是存储限制,您可以构建一个脚本来删除每个月的非活动记录。
-
我没有无限的时间来处理这个项目。我还有 10 个其他项目等着我。我不会花一周时间开发一个复杂的系统来防止管理员删除公司,因为 Django Admin 中已经有警告告诉管理员确切将删除什么,并在删除任何内容之前强制他们确认。很有可能,在 6 到 12 个月后投入生产时,我将成为使用 Django Admin 的人。除非我确定,否则我知道不要删除公司,但我可能不记得计费模型的详细信息,或者我是否应该添加/删除它。
-
您想要的只是从模板中完全隐藏“删除”选项,您可以通过复制默认模板并从中删除删除按钮来覆盖
Billing模型的“add form template”。