3

I'm trying to do something that seems relatively simple, but I'm completely stumped. I have a model, and I want to send an e-mail out once an instance is created for the first time. I can do this easily enough using this:

@receiver(models.signals.post_save, sender=MyModel)
def execute_after_save(sender, instance, created, *args, **kwargs):
    if created:
        send_mail(mail_type='MyModel_created', instance=instance)

However, the problem is that I need to send a link in the e-mail to the change view of the model in the admin panel. I've been using code like this (in other cases) to construct the link inside the send_mail() function mentioned above:

mymodel_admin_url = request.build_absolute_uri(reverse('admin:mymodels_mymodel_change', args=(instance.id,)))

But you'll notice that this relies on having a request object, which isn't passed through the post_save signal, nor even the save method. What's the best way to get the link I need in this case?

Rebuilding and overriding everything (i.e. the save method, the base_save method, the post_save signal, etc.) to pass requests through seems like a nightmare. However, I can't get the link I need using the sites framework either (sometimes I use the site on subdomains, or for example when using manage.py runserver for testing, and calling on the sites framework doesn't build the correct links).

Gravity Grave
  • 2,802
  • 1
  • 27
  • 39
  • 1
    This seems like a classic use case for the `sites` framework. Can you expand on why that won't work for you? There's no way for Django to know what domain names your web server will respond to, so one way or another you're going to have to tell it what domain name to use to build the links. That could be in code, but the `sites` framework is usually more flexible. – Kevin Christopher Henry Sep 13 '14 at 18:08
  • @KevinChristopherHenry sure -- my use of the sites framework was something along the lines of calling on SITE_ID from settings.py, then using this to access the site I had setup in the database (e.g. SITE_ID = 1). Edit: I believe I used domain=Site.objects.get_current(). This let me call up "example.com" when making my links, but this wasn't at all useful when using my dev server ("dev.example.com") or the manage.py runserver ("127.0.0.1:8000"). Is there a way to use the sites framework to return the true absolute URL, like request.build_absolute_uri does? – Gravity Grave Sep 13 '14 at 20:03

1 Answers1

2

Is there a way to use the sites framework to return the true absolute URL, like request.build_absolute_uri() does?

The problem is that, in general, there isn't a true absolute URL. Django can work just fine without a web server at all, generating emails, saving models (and triggering the post-save signal), etc. And if there is a web server there's no way for Django to know what domain names it's servicing, which is why you need a request object and why you need settings like ALLOWED_HOSTS, etc.

The general way you would handle this is by explicitly telling Django what site it is serving. For example, you would have a different settings file for each distribution and each of those files would indicate what domain name it should use (via the SITE_ID setting, if you're using the sites framework; or else you could state it explicitly with your own setting). Using different settings files for different servers is common practice.

Now, in your particular case it sounds like the save is being triggered from inside a web request (via the admin?), so if you could get ahold of that request you could then use its domain name to construct your link. Django doesn't include any way to for you get that request except by being passed it as an argument. You're not the first to want this, though, so if you search for "Django global request" you can see how others have approached the problem. This article, for example, presents a couple ideas, and this app is designed to "give access to Django's HTTPRequest object whenever is needed, without explicitly passing it down the path of code".

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
  • Argh, not the answer I was hoping for, but no point shooting the messenger. Thanks for the info! I'm probably just going to give up on this and use the "inaccurate" sites framework domain. – Gravity Grave Sep 14 '14 at 01:46