Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the URL (with protocol and domain) in Django (without request)?

Tags:

url

django

cron

I want to send mails in a cron job. The mail should contain a link to my application.

In cron job, I don't have a request object, and can't use request.build_absolute_uri().

AFAIK the site framework can help here. But does not give me the protocol (http vs https)?

My application is reusable and sometimes gets hosted on http and sometimes on https sites.

Update

I search a common django way. Creating custom settings is possible, but a solution with django standards is preferred.

like image 497
guettli Avatar asked Jan 25 '16 09:01

guettli


People also ask

How can I get full URL in Django?

Use handy request. build_absolute_uri() method on request, pass it the relative url and it'll give you full one. By default, the absolute URL for request. get_full_path() is returned, but you can pass it a relative URL as the first argument to convert it to an absolute URL.


2 Answers

TL;DR: There's no any "standard" "Django-ish" way of doing that, but the DRY principle promoted by the framework assumes the single configuration store, so a custom setting seems to be a good way to go.

By default Django can serve any number of domains from a single instance, and the HTTP request (more accurately, its HTTP_HOST header) is the only thing Django uses to determine the current host. As your cron jobs are obviously out of the HTTP cycle, you should store your domain somewhere in settings...

# settings.py DEFAULT_DOMAIN = 'https://foobar.com' # or, depending on your configuration: DEFAULT_DOMAIN = 'https://{}'.format(ALLOWED_HOSTS[0]) 

...with a tiny context processor to make it easier to handle templating:

# yourapp/context_processors.py from django.conf import settings  def default_domain(request):     return {'default_domain': settings.DEFAULT_DOMAIN} 

...and then use it in your emails:

# yourapp/templates/email/body.html <a href="{{ default_domain }}{% url 'target' %}">Click here</a> 

Alternatively you can make use of the sites framework, but if you're serving a single domain, the settings-based solution seems much more simpler and cleaner to me.

like image 200
Alex Morozov Avatar answered Oct 05 '22 10:10

Alex Morozov


There's special standard module for this task - Sites Framework. It adds Site model, which describes specific site. This model has field domain for domain of the project, and a name - human-readable name of the site.

You associate your models with sites. Like so:

from django.db import models from django.contrib.sites.models import Site  class Article(models.Model):     headline = models.CharField(max_length=200)     # ...     site = models.ForeignKey(Site, on_delete=models.CASCADE) 

When you need to make an url for the object you may use something like the following code:

   >>> from django.contrib.sites.models import Site    >>> obj = MyModel.objects.get(id=3)    >>> obj.get_absolute_url()    '/mymodel/objects/3/'    >>> Site.objects.get_current().domain    'example.com'    >>> 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())    'https://example.com/mymodel/objects/3/' 

This way you can have multiple domains and content spread between them. Even if you have only one domain I recommend use it to achieve a good standard comfortable way to keep domain settings.

Installatioin is quite easy:

  1. Add 'django.contrib.sites' to your INSTALLED_APPS setting.

  2. Define a SITE_ID setting:

     SITE_ID = 1 
  3. Run migrate.

like image 42
Eugene Lisitsky Avatar answered Oct 05 '22 11:10

Eugene Lisitsky