【问题标题】:Deletion objects that is used as foreign key删除用作外键的对象
【发布时间】:2014-12-26 20:23:45
【问题描述】:

我有下一个模型:

class Target(models.Model):
    name = models.CharField(max_length=100, blank=False)


class SubTarget(models.Model):
    target = models.ForeignKey(Target, related_name='sub_targets')
    name = models.CharField(max_length=100, blank=True, null=True, default='')

例如,我运行下一段代码:

target = Target(name='test-target')
target.save()
sub_target = SubTarget(name='test-sub-target, target=target)
sub_target.save()

所以现在我有了带有外键的 sub_target 对象。

我的目标序列化器如下所示:

class TargetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Target
        fields = ('id', 'name', 'sub_targets')
        depth = 1
        read_only_fields = ('sub_targets',)

和适当的观点:

class TargetDetail(generics.RetrieveUpdateDestroyAPIView):
    model = Target
    serializer_class = TargetSerializer

所以,没有什么能阻止我删除只是 target 使用外键创建的对象。而且,这个操作还删除了相关的sub_target对象。我怎样才能避免这种行为?

【问题讨论】:

    标签: django rest django-rest-framework


    【解决方案1】:

    我不确定,但我认为您在问如何在删除 Target 对象时防止 SubTarget 对象被删除。默认情况下,Django 模拟 ON DELETE CASCADE。您可以使用 on_delete 关键字控制此行为。

    所以:

    class Target(models.Model):
        name = models.CharField(max_length=100, blank=False)
    
    
    class SubTarget(models.Model):
        target = models.ForeignKey(Target, related_name='sub_targets', 
                                   null=True, on_delete=models.SET_NULL)
        name = models.CharField(max_length=100, blank=True, null=True, default='')
    

    Documentation

    【讨论】:

      【解决方案2】:

      延迟回复,但也可以在 ForeignKey 上使用 model.PROTECT 来避免这种情况

      target = models.ForeignKey( Target, related_name='sub_targets', on_delete=models.PROTECT )

      class BaseModel(models.Model):
      
          def can_delete(self):
              # get all the related object to be deleted
              for related in get_candidate_relations_to_delete(self._meta):
                  field = related.field
                  if field.remote_field.on_delete == models.PROTECT:
                      # check for relationship with at least one related object
                      related = related.related_model.objects.filter(**{related.field.name: self})
                      if related.exists():
                          return False, related
              return True, None
      
          class Meta:
              abstract = True
      

      【讨论】:

        【解决方案3】:

        在寻找一种方法来检查是否可以在 django 中删除模型实例后,我遇到了许多示例,但没有按预期工作。希望这个解决方案能有所帮助。

        让我们从创建一个可以被其他模型继承的抽象模型类开始

        class ModelIsDeletable(models.Model):
            name = models.CharField(max_length=200, blank=True, null=True, unique=True)
            description = models.CharField(max_length=200, blank=True, null=True)
            date_modified = models.DateTimeField(auto_now_add=True)
        
            def is_deletable(self):
                # get all the related object
                for rel in self._meta.get_fields():
                    try:
                        # check if there is a relationship with at least one related object
                        related = rel.related_model.objects.filter(**{rel.field.name: self})
                        if related.exists():
                            # if there is return a Tuple of flag = False the related_model object
                            return False, related
                    except AttributeError:  # an attribute error for field occurs when checking for AutoField
                        pass  # just pass as we dont need to check for AutoField
                return True, None
        
             class Meta:
                abstract = True
        

        示例

        假设我们有三个模型 Organization and Department 和 StaffType 一个组织中可以有这么多部门 并且一个组织有一个特定的 StaffType

        class StaffType(ModelIsDeletable):
            pensionable = models.BooleanField(default=False)
        
        class Organization(ModelIsDeletable):
            staff_type = models.ForeignKey(to=StaffType)
        
        
        class Department(ModelIsDeletable):
            organization = models.ForeignKey(to=Organization, to_field="id")
        

        所以在添加一些信息后说你想删除一个组织模型实例 已经绑定到一个部门

        例如我们有 组织表 =>(名称 = 工程,pk = 1) 部门表 => (name=Developer, organization_fk=1, pk=1)

        现在,当您尝试使用 pk 获取组织后删除组织时

        a_org = Organization.objects.get(pk=1)
        

        有了这个,你可以检查它是否可以删除

        deletable, related_obj = a_org.is_deletable()
        
        if not deletable:
            # do some stuff with the related_obj list
        
        else:
            # call the delete function
            a_org.delete()
        

        【讨论】:

          【解决方案4】:

          也许你可以尝试另一种方式,我刚刚为我的项目得到它,使用 Django 1.8

          instance = get_object_or_404(MyModel, pk=pk)
          eliminate = True
          for robject in instance._meta.get_all_related_objects():
              if robject is not None:
                  if robject.related_model.objects.filter(**{robject.field.name: instance}).exists() and eliminate:
                      eliminate = False
          
          if eliminate:
              instance.delete()
              # additional code
          else:
              # additional code
              pass
          

          【讨论】:

            【解决方案5】:

            您可以覆盖模型的删除操作,例如:

            class Target(models.Model):
                name = models.CharField(max_length=100, blank=False)
            
                def delete(self, *args, **kwargs):
            
                    for robject in self._meta.get_all_related_objects():
                        if robject is not None:
                            q = Q(**{"%s__id" % robject.field.name: self.id})
                            if robject.model.objects.filter(q).exists():
                                raise Exception("Item has active reference.")
            
                    try:
                        with transaction.atomic():
                            super(Target, self).delete(*args, **kwargs)
                    except Exception, exp:
                        raise exp
            

            请注意,如果相关对象与另一个对象没有反向关系,则它不起作用。

            【讨论】:

              猜你喜欢
              • 2012-09-24
              • 1970-01-01
              • 2019-04-12
              • 1970-01-01
              • 2017-04-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-04-20
              相关资源
              最近更新 更多