Remote objects that are used as a foreign key

I have the following models:

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='') 

For example, I run the following code:

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

So now I have a sub_target object with a foreign key.

My serializer for the purpose is as follows:

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

and corresponding view:

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

So, nothing prevents me from deleting only the target created object with a foreign key. In addition, this operation also removes the associated sub_target object. How can I avoid this behavior?

+7
rest django django-rest-framework
source share
5 answers

I'm not sure, but I think you are asking how to prevent the removal of SubTarget objects when deleting Target objects. By default, Django emulates ON DELETE CASCADE. You can control this behavior with the on_delete keyword.

So:

 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

+7
source share

After finding a way to check if it is possible to delete the model instance in django, I came across many examples, but did not work properly. Hope this solution can help.

Let's start by creating an abstract model class that can be inherited by another model.

 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 

Example

So, let's say we have three models Organization and Department and StaffType So many Departments can be in the Organization And the Organization has a certain 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") 

so let's say by adding some information that you want to delete an instance of an organization model that is already tied to the Department

For example, we have Organization Table => (name = Engineering, pk = 1) Department table => (name = Developer, organization_fk = 1, pk = 1)

Now when you try to delete the organization after receiving it with pk

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

With this you can check if it is deleted

 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() 
+2
source share

You can override the delete operation of the model, for example:

 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 

Note that it does not work if the related object is not inversely related to another.

0
source share

Maybe you can try it differently, I just got it for my project using 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 
0
source share

Late answer, but this can also be avoided by using model.PROTECT in ForeignKey

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 
0
source share

All Articles