Masquerade
Masquerade

Reputation: 23

How do I generate absolute urls in Django template?

I’m having trouble generating the absolute url for a Django model. In "courses.html" template I iterate through two models, category and course, but am only able to generate the relative url <slug> rather than the full url /courses/<slug> from each object.

Is there any way to generate an absolute url for each object?

main views.py

def courses(request):
    return render(request=request,
                  template_name="main/courses.html",
                  context={'courses': Course.objects.all,
                           'categories': Category.objects.all},
                  )

courses.html


{% extends "main/header.html" %}
{% block content %}

{% for category in categories %}
    <a href="{{ category.slug }}"/>
      <p>{{ category }}</p>
    </a>
{% endfor %}

{% for course in courses %}
    <a href="{{ course.slug }}"/>
      <p>{{ course }}</p>
    </a>
{% endfor %}

Upvotes: 1

Views: 7507

Answers (2)

Olivier
Olivier

Reputation: 416

@pygeek answer is not the right one:

  1. there is a much simpler to achieve his solution: just use <a href={% url 'main:category_detail' category.slug %}> in your template, I don't see the need for writing the get_absolute_url method in the models each time
  2. also @pygeek's solution produces a relative url: if you really need to provide an absolute URL (ie something which starts with http:// or https://), then the only solution I see is to create a new template tag. In template/tags, create a file (eg myapp_tags.py) and in myapp_tags.py:
@register.simple_tag(takes_context=True)
def absolute_url(context, relative_url):
  request = context['request']
  return request.get_absolute_uri(relative_url)

then, in the template:

{%load myapp_tags%}
...
{%url 'main:course_detail' category.slug as relative_url %}
<a href="{%absolute_url relative_url%}">...</a>

Note: This works only for templates invoked with

def my_view(request):
  return render(request, 'template.html', {'context': 'data'})

It won't work if you directly render the template w/o the request like in

  template = loader.get_template('template.html')
  return HttpResponse(template.render({'context': 'data'}))

Upvotes: 0

pygeek
pygeek

Reputation: 7404

Problem

Get absolute url for a model object that correlates with a url.

Solution

A common idiosyncrasy in django is to define get_absolute_url on the model and use reverse to build the url with context from the model. Keeping business logic contextual to the model in the model simplifies maintenance and automated testing. This pattern is used throughout the Django framework (ie: https://github.com/django/django/blob/555e3a848e7ac13580371c7eafbc89195fee6ea9/tests/contenttypes_tests/models.py#L16-L20).

Example

urls.py

...
    path('<int:course_id>/', views.CourseDetail.as_view(), name='course_detail'),
    path('<int:category_id>/', views.CategoryDetail.as_view(), name='category_detail'),
...

models.py

class Course(models.Model):
    def get_absolute_url(self):
        return reverse('course_detail', kwargs={"id": str(self.id)})

class Category(models.Model):
    def get_absolute_url(self):
        return reverse('category_detail', kwargs={"id": str(self.id)})

courses.html

{% extends "main/header.html" %}
{% block content %}

{% for category in categories %}
    <a href="{{ category.get_absolute_url }}"/>
      <p>{{ category }}</p>
    </a>
{% endfor %}

{% for course in courses %}
    <a href="{{ course.get_absolute_url }}"/>
      <p>{{ course }}</p>
    </a>
{% endfor %}

Upvotes: 2

Related Questions