dyv*_*yve 14 python django django-signals django-models
我有这样的设置(简化为这个问题):
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
employees = models.ManyToManyField(Employee)
Run Code Online (Sandbox Code Playgroud)
当员工即将被删除时,我想检查他是否已连接到任何项目.如果是这样,删除应该是不可能的.
我知道信号以及如何使用它们.我可以连接到pre_delete信号,并使它像一个例外ValidationError.这可以防止删除但是表单等不能正常处理.
这似乎是其他人会遇到的情况.我希望有人可以指出一个更优雅的解决方案.
Elw*_*win 21
我正在寻找这个问题的答案,无法找到一个好的,这对两个models.Model.delete()和QuerySet.delete()都有效.我一起去实施Steve K的解决方案.我使用此解决方案确保无法以任何方式从数据库中删除对象(此示例中的Employee),但将其设置为非活动状态.
这是一个迟到的答案..只是为了其他人看起来我在这里提出我的解决方案.
这是代码:
class CustomQuerySet(QuerySet):
def delete(self):
self.update(active=False)
class ActiveManager(models.Manager):
def active(self):
return self.model.objects.filter(active=True)
def get_queryset(self):
return CustomQuerySet(self.model, using=self._db)
class Employee(models.Model):
name = models.CharField(name, unique=True)
active = models.BooleanField(default=True, editable=False)
objects = ActiveManager()
def delete(self):
self.active = False
self.save()
Run Code Online (Sandbox Code Playgroud)
用法:
Employee.objects.active() # use it just like you would .all()
Run Code Online (Sandbox Code Playgroud)
或在管理员:
class Employee(admin.ModelAdmin):
def queryset(self, request):
return super(Employee, self).queryset(request).filter(active=True)
Run Code Online (Sandbox Code Playgroud)
Col*_*cks 14
对于那些在ForeignKey关系中遇到相同问题的人来说,正确的答案是on_delete=models.PROTECT在ForeignKey关系上使用 Djago 的字段。这将防止删除任何具有外键链接的对象。这不适用于ManyToManyField关系(如本问题中所述),但适用于ForeignKey字段。
因此,如果模型是这样的,这将有助于防止删除任何Employee与一个或多个Project对象相关联的对象:
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
employees = models.ForeignKey(Employee, on_delete=models.PROTECT)
Run Code Online (Sandbox Code Playgroud)
文档可以在这里找到。
如果您知道永远不会有任何大规模员工删除尝试,您可以覆盖delete您的模型,只有super在合法操作时才会调用.
不幸的是,任何可能调用的东西都queryset.delete()将直接用于SQL:http:
//docs.djangoproject.com/en/dev/topics/db/queries/#deleting-objects
但我不认为这是一个很大的问题,因为你是编写这个代码的人,并且可以确保从来没有任何queryset.delete()员工.delete()手动呼叫.
我希望删除员工是比较少见的.
def delete(self, *args, **kwargs):
if not self.related_query.all():
super(MyModel, self).delete(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
小智 5
这将从我的应用程序中的实现中总结解决方案。LWN的答案就是一些代码。
在四种情况下,您的数据将被删除:
delete()Model实例:project.delete()delete()QuerySet实例:Project.objects.all().delete()尽管第一种情况没有什么可做的,但其他三种可以精细控制。一个建议是,在大多数情况下,您永远不要删除数据本身,因为这些数据反映了我们应用程序的历史和使用情况。active首选在布尔字段上设置。
为了防止出现delete()在Model实例上,请delete()在Model声明中使用子类:
def delete(self):
self.active = False
self.save(update_fields=('active',))
Run Code Online (Sandbox Code Playgroud)
虽然delete()上查询集实例需要一点点的设置与自定义对象管理器在LWN的答案。
将其包装为可重用的实现:
class ActiveQuerySet(models.QuerySet):
def delete(self):
self.save(update_fields=('active',))
class ActiveManager(models.Manager):
def active(self):
return self.model.objects.filter(active=True)
def get_queryset(self):
return ActiveQuerySet(self.model, using=self._db)
class ActiveModel(models.Model):
""" Use `active` state of model instead of delete it
"""
active = models.BooleanField(default=True, editable=False)
class Meta:
abstract = True
def delete(self):
self.active = False
self.save()
objects = ActiveManager()
Run Code Online (Sandbox Code Playgroud)
用法,只是子ActiveModel类:
class Project(ActiveModel):
...
Run Code Online (Sandbox Code Playgroud)
如果我们的对象的任何ForeignKey字段被删除,仍然可以删除:
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager')
>>> manager.delete() # this would cause `project` deleted as well
Run Code Online (Sandbox Code Playgroud)
这可以通过添加“模型”字段的on_delete参数来防止:
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager',
on_delete=models.PROTECT)
Run Code Online (Sandbox Code Playgroud)
的默认on_delete值为CASCADE,这将导致您的实例被删除,方法是使用PROTECT代替,它将引发ProtectedError(的子类IntegrityError)。这样做的另一个目的是,应保留数据的ForeignKey作为参考。
| 归档时间: |
|
| 查看次数: |
12647 次 |
| 最近记录: |