Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Django post_save triggered before/after saving instance to database?

I have a website using Django. Each post is an object called Article. I want to retrieve the post's HTML after saving it so I wrote the following post_save hook:

@receiver(models.signals.post_save, sender=Article)
def _send_article_mentions(sender, instance, **kwargs):
    import requests
    from django.contrib.sites.models import Site
    from urlparse import urljoin
    from ParallelTransport.settings import ARTICLES_URL
    SITE_URL = 'http://'+Site.objects.get_current().domain
    article_url = urljoin( SITE_URL, instance.get_absolute_url() )
    import time
    time.sleep(20)
    r = requests.get(article_url)
    error_file = open(ARTICLES_URL+'/'+'error.txt','w')
    error_file.write('file started1\n')

    m = r.status_code
    error_file.write(str(m))
    error_file.close()

It basically waits for 20s (added as a test) then tries to retrieve the HTML of the post using its URL, and writes the request status code to a file for debugging.

The problem is I always get status = 404 on the first save, it does work on 2nd and subsequent saves. I thought the way Django works would be, in order:

  1. save instance to database using save(). At this point the post would get a URL
  2. send post_save signal

But then I should be able to retrieve the HTML in post_save. Am I understanding post_save incorrectly?

Added notes:

  1. Putting this code in save() method does not work. Nor should it. The post is added to database at the end of the save() method and so should not have any URL until save() ends.
  2. This is on a production site, not on the development server.
  3. I want to use the links in the HTML to send 'pingbacks' or actually webmention. But all my pingbacks are being rejected because the post does not have a URL yet. This is the bare minimum code that does not work.
like image 373
Kartik Prabhu Avatar asked Feb 14 '14 00:02

Kartik Prabhu


People also ask

What is pre save and post in Django?

To understand that, we have to know one VERY IMPORTANT property about the variable instance in post_save and pre_save: The post_save's instance has the attributes with values which are already saved in your model, but the pre_save's instance has the attributes with values which are yet to be saved in your model.

What is Post_save in Django?

django.db.models.signals. post_save. Like pre_save , but sent at the end of the save() method. Arguments sent with this signal: sender.

What is pre save signal in Django?

Django Signals - pre_save()To execute some code dealing with another part of your application before the object gets saved to the database, you have to use a pre_save signal. That way, the signal is sent at the beginning of a model's save() method.

How do Django signals work?

Django includes a “signal dispatcher” which helps decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.


1 Answers

Although this is a completely wrong approach(*), the problem is probably in database transactions. Current thread saves the article but within this uncommited transaction you are trying to get these data through another thread (through web server). In that case, this behaviour is fully correct. Either you need to commit before retrieving through another thread or get the HTML by another way.

(*) should be done asynchronously on the background (Celery or other more lightweight async queue app) or you can call the view directly if you want to get the HTML (depending on your view, you may have to forge the request; if too complicated, you can create a helper function that cherrypicks minimal code to render the template). If you only need to call a 3rd party API after you save something, you want to do it asynchronously. If you don't do it, the success of your "save() code" will depend on the availability of your connection or the 3rd party service and you will need to deal with transactions on place where you won't to deal with transactions ;)

like image 119
Bruce Avatar answered Sep 27 '22 19:09

Bruce