DoesNotExist on ForeignKey Access

36 views
Skip to first unread message

TheBeardedTemplar

unread,
Aug 12, 2016, 2:00:58 PM8/12/16
to Django users
Hey all,

I'm quite stumped with a problem that recently started showing up in my code. I have a model called a Widget that can have an associated Blob object. When a Widget is deleted, I want to make sure that the Blob associated with it isn't used elsewhere, and if it's not, delete it. I do that via a postDelete hook, as follows:

def postDeleteWidget(sender, **kwargs):
   
"""
    Delete the associated blob when the widgetis deleted.
    """

    widget
= kwargs['instance']


   
#Make sure this blob isn't used elsewhere
   
if widget.blob is not None and Widget.objects.filter(blob=widget.blob).count() == 0:
        widget
.blob.delete()

This come is failing when it first looks up widget.blob in these Django files:

File "C:\Python27\Lib\site-packages\django\db\models\fields\related.py", line 617, in __get__
  rel_obj
= qs.get()
File "C:\Python27\Lib\site-packages\django\db\models\query.py", line 334, in get
 
self.model._meta.object_name

myproject
.apps.blobs.models.DoesNotExist: Blob matching query does not exist.

Now here's the weird part. If I insert "sleep(1)" after getting the widget, the code works fine. It deletes the associated blob as expected. I'm really at a loss in terms of how to explain this. I'd appreciate any advice. Even printing 'widget.blob' before the if statement usually resolves it. Is this some kind of race condition involved in accessing a related key on a postDelete hook.

Thanks,
-Keilan

Todor Velichkov

unread,
Aug 12, 2016, 3:19:17 PM8/12/16
to Django users
Some more code will be helpful, for example the model structure, how do you define the the `ForeignKey`? How do you subscribe for the signal?
You are saying that with sleep(1) the code works fine, is this mean that you can reproduce the problem at any time?
However test it without fetching the blob object, this would probably fix the problem:

if widget.blob_id is not None and Widget.objects.filter(blob_id=widget.blob_id).count() == 0:
   
Blob.objects.filter(id=widget.blob_id).delete()

TheBeardedTemplar

unread,
Aug 12, 2016, 4:10:13 PM8/12/16
to Django users


On Friday, August 12, 2016 at 1:19:17 PM UTC-6, Todor Velichkov wrote:
Some more code will be helpful, for example the model structure, how do you define the the `ForeignKey`? How do you subscribe for the signal?
You are saying that with sleep(1) the code works fine, is this mean that you can reproduce the problem at any time?
However test it without fetching the blob object, this would probably fix the problem:

if widget.blob_id is not None and Widget.objects.filter(blob_id=widget.blob_id).count() == 0:
   
Blob.objects.filter(id=widget.blob_id).delete()


Hello Todor,

Your solution worked like a charm. It was fetching the blob object causing the failure, thank you very much! For the sake of completion, here is the code you asked for:

class Widget(models.Model):
    client
= models.ForeignKey(Client, related_name="layers")
    label
= models.TextField()
    blob
= models.ForeignKey(Blob, blank=True, null=True)
    date_created
= models.DateTimeField(auto_now_add=True, blank=True, null=True)

And the connection is made using signals.post_delete.connect(postDeleteWidget, sender=Widget).
 

Constantine Covtushenko

unread,
Aug 12, 2016, 4:32:22 PM8/12/16
to django...@googlegroups.com
HI guis,

I need to mention one point that works well for me.
If you check blob existence with:
  hasattr(widget, 'blob')
then django responds False in the case Blob Does Not exist an True otherwise.

At the same time DB request is performed but DoesNotExist exception is not raised.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/ea379e82-a7e5-455e-9018-007af3c7c435%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Todor Velichkov

unread,
Aug 12, 2016, 6:08:47 PM8/12/16
to Django users, constantine...@gmail.com
@TheBeardedTemplar,
I'm happy this solves the problem, but its more like a workaround, I'm interested to understand how this raise condition is achieved. ;)
One thing that I was thinking that can cause this raise condition is that by default Django emulates CASCADE DELETE behavior on ForeignKeys, which means that if you delete a Blob object all its widget will also get deleted. This means than when you delete a Blob with more than 1 widget, its, your post_delete method will be called for each widget, the first one can delete the Blob, and then there will be no Blob object for the rest, thus DoesNotExists error will be raised. (I'm not sure if this is what really is happening, its just a hypothesis).

@Covtushenko,
yes this is a nice trick.

On Friday, August 12, 2016 at 11:32:22 PM UTC+3, Constantine Covtushenko wrote:
HI guis,

I need to mention one point that works well for me.
If you check blob existence with:
  hasattr(widget, 'blob')
then django responds False in the case Blob Does Not exist an True otherwise.

At the same time DB request is performed but DoesNotExist exception is not raised.
On Fri, Aug 12, 2016 at 11:10 PM, TheBeardedTemplar <thebeard...@gmail.com> wrote:


On Friday, August 12, 2016 at 1:19:17 PM UTC-6, Todor Velichkov wrote:
Some more code will be helpful, for example the model structure, how do you define the the `ForeignKey`? How do you subscribe for the signal?
You are saying that with sleep(1) the code works fine, is this mean that you can reproduce the problem at any time?
However test it without fetching the blob object, this would probably fix the problem:

if widget.blob_id is not None and Widget.objects.filter(blob_id=widget.blob_id).count() == 0:
   
Blob.objects.filter(id=widget.blob_id).delete()


Hello Todor,

Your solution worked like a charm. It was fetching the blob object causing the failure, thank you very much! For the sake of completion, here is the code you asked for:

class Widget(models.Model):
    client
= models.ForeignKey(Client, related_name="layers")
    label
= models.TextField()
    blob
= models.ForeignKey(Blob, blank=True, null=True)
    date_created
= models.DateTimeField(auto_now_add=True, blank=True, null=True)

And the connection is made using signals.post_delete.connect(postDeleteWidget, sender=Widget).
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
Reply all
Reply to author
Forward
0 new messages