# {% url %} template tag, get\_absolute\_url(), and views

**URL:** <https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249>\
**Category:** Templates & Frontend\
**Created:** [May 30, 2023, 2:18pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249 "2023-05-30T14:18:31Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![StephanieAG](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/stephanieag/32/17567_2.png) [@StephanieAG](https://forum.djangoproject.com/u/StephanieAG)\
**Post date:** [May 30, 2023, 2:18pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/1 "2023-05-30T14:18:31Z")

</div>

**GENERAL CONTEXT**

- My understanding of [this related post](https://forum.djangoproject.com/t/get-absolute-url-vs-url-item-detail-item-pk/2239/3) and the [get\_absolute\_url() Django docs section](https://docs.djangoproject.com/en/4.2/ref/models/instances/#get-absolute-url) is essentially that get\_absolute\_url() is useful and preferred because using it 1) allows templates to be more generic, 2) reduces the number of places to update if a change is needed / check if a problem arises, and 3) is used in the django admin app etc. to make links work.
- I recently posted [this question about using get\_absolute\_url()](https://forum.djangoproject.com/t/using-get-absolute-url-works-in-a-for-loop-but-not-in-a-single-href-link/21071/14) where get\_absolute\_url() is on a model and the main problem I had was that I was missing the context data in the right view.

**EXAMPLE PROJECT STRUCTURE CONTEXT**  
_Note: I used excerpts where possible to avoid clutter_  
I have a **\_base.html** file that includes a header. One functioning link in the header’s dropdown menu is:  
`<li><a class="dropdown-item" href="{% url 'profile_list' %}">Profile List</a></li>`

I have a **profiles app** that includes:

Profile model using get\_absolute\_url()

```auto
class Profile(models.Model):
    id = models.UUIDField( 
        primary_key=True,
        default=uuid.uuid4,
        editable=False)
    user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)

    def get_absolute_url(self):
        return reverse("profile_detail", args=[str(self.id)])

```

URLs

```auto
from django.urls import path

from .views import ProfileListView, ProfileDetailView

urlpatterns = [
    path("", ProfileListView.as_view(), name="profile_list"),
    path("<uuid:pk>", ProfileDetailView.as_view(), name="profile_detail"), 
]

```

View

```auto
from django.views.generic import ListView, UpdateView  

from .models import Profile

class ProfileListView(ListView):
    model = Profile
    context_object_name = "profile_list"
    template_name = "profiles/profile_list.html"

class ProfileDetailView(UpdateView):
    model = Profile
    context_object_name = "profile"  
    template_name = "profiles/profile_detail.html"

```

Template - profiles/profile\_list.html

```auto
{% for profile in profile_list %}
            <div class="card mb-3" style="max-width: 540px;">
                <div class="row g-0">
                    <div class="col-md-4">
                        <img src="{% static 'images/testimage.jpg' %}" class="img-fluid rounded-start" alt="Profile Image">
                    </div>
                    <div class="col-md-8">
                        <div class="card-body">
                            <h5 class="card-title">{{ profile.user.username }}</h5>
                            <p class="card-text"><a href="{{ profile.get_absolute_url }}">{{ profile.user.username }}</a></p>
                            <br />
                            <p class="card-text"><small class="text-body-secondary">{{ profile.date_modified }}</small></p>
                        </div>
                    </div>
                </div>
            </div>
        {% endfor %}

```

Template - profiles/profile\_detail.html

```auto
<h1>{{ profile.user.username }}'s Profile</h1>

```

I have a **pages app** that includes static pages, one of which is the following:

URL

```auto
from django.urls import path

from .views import AboutPageView

urlpatterns = [
    path("about/", AboutPageView.as_view(), name="about"),
]

```

View - _Created with help from [this post about using get\_absolute\_url()](https://forum.djangoproject.com/t/using-get-absolute-url-works-in-a-for-loop-but-not-in-a-single-href-link/21071/14)_

```auto
class AboutPageView(TemplateView): 
    template_name = "pages/about.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["my_profile"] = self.request.user.profile
        return context

```

Template

```auto
<a href="{{ my_profile.get_absolute_url }}">My Profile</a>

```

**QUESTIONS**

1a. Why does the link for the Profile List in the \_base.html header menu not need context data in a \_base.html view file to work? Is it because the {% url %} tag can be used without passing anything except the URL name in?  
1b. Is get\_absolute\_url() preferred for template links like this (aka: non-detail page related links) too? If yes, how would you update this profile list link as an example? Would adding the below to the Profile model work? / Can there be more than one get\_absolute\_url() per model class?

```auto
    def get_absolute_url(self):
        return reverse("profile_list")

```

2a. If I wanted to add a link to the \_base.html header menu that links to the authenticated user’s profile, it seems like I need to create a \_base.html view file; is that correct?  
2b. If yes, is it normal to just have a \_base.html view file kind of free floating in the main folder of the project (like in the same folder as the manage.py file?). In other words, I’m used to having the views files be within the app folders but since the \_base file isn’t app-specific I want to know if there’s a certain place a related view file normally goes. If no, how/where would you put the context you need for the link to work?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 30, 2023, 4:15pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/3 "2023-05-30T16:15:11Z")

</div>

> [@StephanieAG](#):
>
> 1a. Why does the link for the Profile List in the \_base.html header menu not need context data in a \_base.html view file to work? Is it because the {% url %} tag can be used without passing anything except the URL name in?

Because the url being rendered doesn’t require any parameters.

This one doesn’t require any data to render:

> [@StephanieAG](#):
>
> ```auto
> path("", ProfileListView.as_view(), name="profile_list"),
> 
> ```

But this one requires a context variable to populate the field named `pk`:

> [@StephanieAG](#):
>
> ```auto
> path("<uuid:pk>", ProfileDetailView.as_view(), name="profile_detail"), 
> 
> ```

> [@StephanieAG](#):
>
> 1b. Is get\_absolute\_url() preferred for template links like this (aka: non-detail page related links) too?

No. The `get_absolute_url` function is intended to return a url for a specific instance of an object - in which case you need to specify _which_ instance you want referenced.

> [@StephanieAG](#):
>
> a. If I wanted to add a link to the \_base.html header menu that links to the authenticated user’s profile, it seems like I need to create a \_base.html view file; is that correct?

No, that is not correct. You have one view to render the entire page, the identified template and the base template it extends (as well as any other templates it may include). Your base template has access to the context of the template being rendered.

---

<div class="post-metadata">

**Author:** ![StephanieAG](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/stephanieag/32/17567_2.png) [@StephanieAG](https://forum.djangoproject.com/u/StephanieAG)\
**Post date:** [May 30, 2023, 9:08pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/4 "2023-05-30T21:08:27Z")

</div>

Thanks, Ken.

> [@KenWhitesell](#):
>
> The `get_absolute_url` function is intended to return a url for a specific instance of an object - in which case you need to specify _which_ instance you want referenced.

If I’m understanding correctly, in practice you would only ever have a maximum of one `get_absolute_url` function in each model which would use `self.id` (or whatever parameter is required by the URL) to complete the URLs for all of the individual instances of all the objects within the model. When the template tag uses just the general model name, like in `{{ profile.get_absolute_url }}`, it can work only because the template tag is within a for loop (or something that iterates through several of the objects). This is possible because the `self.id` in that model’s `get_absolute_url` function can be used to refer to any/all of the instances.

In order to direct to one _specific_ object in the model, you set a context data variable in that page’s views file to equal the parameter you need to complete the URL of the specific object that you want. Then, in the template (using this example), the `my_profile` part of `{{ my_profile.get_absolute_url }}` is the parameter information needed in the URL which is used by the `get_absolute_url` function to replace the `self.id` with the specific id associated with, in this case, `self.request.user.profile`.

Is all of that accurate? I’m most unsure about that last sentence.

> [@KenWhitesell](#):
>
> You have one view to render the entire page, the identified template and the base template it extends (as well as any other templates it may include). Your base template has access to the context of the template being rendered.

Got it. So, I updated the \_base.html file to include this link in the header menu:  
`<li><a class="dropdown-item" href="{{ my_profile.get_absolute_url }}">My Profile</a></li>`  
and this works if I go to the `about` URL specifically (since it has the context data), however it didn’t work on any other page. Given what you noted above, I tried adding this:

```auto
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["my_profile"] = self.request.user.profile
        return context

```

to the other views as well. Doing this did allow the “My Profile” link to work in the header menu on each page.

Adding the same context data snippet to each view seems repetitive, so I’m wondering: is it normal to do the above? If no, how do folks normally do this less repetitively?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 30, 2023, 10:34pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/5 "2023-05-30T22:34:23Z")

</div>

> [@StephanieAG](#):
>
> in practice you would only ever have a maximum of one `get_absolute_url` function in each model

You can only have one function of a given name in a class. You may not have multiple `get_absolute_url` functions in the same class.

> [@StephanieAG](#):
>
> When the template tag uses just the general model name, like in `{{ profile.get_absolute_url }}`, it can work only because the template tag is within a for loop (or something that iterates through several of the objects).

For clarity - the `profile` in that tag, is _not_ a “general model name”, it’s a reference to a Python variable. That variable can be set any number of ways - being the iteration variable in a loop is just one of them.

> [@StephanieAG](#):
>
> This is possible because the `self.id` in that model’s `get_absolute_url` function can be used to refer to any/all of the instances.

I’m not entirely comfortable with the way you’ve worded this.

A Python class, such as a Django Model, is a “cookie cutter” if you will. It’s a type of template from which you will create an indeterminate number of instances. Each of those instances has access to its own variables by referencing the variable named `self`. (Note: The usage of `self` is a convention - it’s not actually required to be `self`. You’ll find some old Python articles that use `s` as a shortened version.)

> [@StephanieAG](#):
>
> In order to direct to one _specific_ object in the model, …

The `my_profile` part of that tag, `{{ my_profile.get_absolute_url }}`, is the key to the context dict. The value for that key is supposed to be an object with an attribute named `get_absolute_url`. Note that there’s no _requirement_ that `get_absolute_url` be a function. See the docs at [The Django template language | Django documentation | Django](https://docs.djangoproject.com/en/4.2/ref/templates/language/#variables)

So specifically, it has _nothing_ to do with the url itself! From the perspective of the template, `my_profile.get_absolute_url` is any one of the types of data identified by the link above.

> [@StephanieAG](#):
>
> If no, how do folks normally do this less repetitively?

You have a couple different options. First, you can create your own [`context processor`](https://docs.djangoproject.com/en/4.2/ref/templates/api/#writing-your-own-context-processors) to add data to the context of every render operation. This is the general process used in the common case.

Or, in _this_ specific case (and ones similar to it), you can take advantage of the existing [`auth`](https://docs.djangoproject.com/en/4.2/ref/templates/api/#django-contrib-auth-context-processors-auth) context processor and leverage the fact that you already have a reference to a `user` object in your template.

---

<div class="post-metadata">

**Author:** ![StephanieAG](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/stephanieag/32/17567_2.png) [@StephanieAG](https://forum.djangoproject.com/u/StephanieAG)\
**Post date:** [May 31, 2023, 9:28pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/6 "2023-05-31T21:28:46Z")

</div>

Thanks, Ken; your clarifications throughout have all been incredibly helpful. I think I’m starting to put it together.

Regarding the last section, I successfully used a context processor to avoid editing each view to make the My Profile menu link work. Just in case it’s helpful for any potential future readers, this was what it looks like:

```auto
def profile_associated_with_authenticated_user(request):
    my_profile = request.user.profile
    return {'my_profile': my_profile}

```

Plus adding this to the project settings.py file: `'context_processors.profile_associated_with_authenticated_user',`

Though that’s functioning, for the sake of learning, might you also please elaborate on this:

> [@KenWhitesell](#):
>
> Or, in _this_ specific case (and ones similar to it), you can take advantage of the existing [`auth`](https://docs.djangoproject.com/en/4.2/ref/templates/api/#django-contrib-auth-context-processors-auth) context processor and leverage the fact that you already have a reference to a `user` object in your template.

Specifically, I see that `'django.contrib.auth.context_processors.auth'`, is already in the project settings.py file. I guess I’m unclear about which template you were referring to and how that would help avoid repeating the get\_context\_data snippet in all the views. In other words, even if a reference to a `user` object is in one of my templates, how does this help me bridge to the need for the context to be available for all of the pages across the site (needed because the link was in the header menu)?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 31, 2023, 11:10pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/7 "2023-05-31T23:10:24Z")

</div>

> [@StephanieAG](#):
>
> Specifically, I see that `'django.contrib.auth.context_processors.auth'`, is already in the project settings.py file.

What this means is that in _every_ template you render, you have `user` available as a variable. You don’t have to add it to every context in your views and you don’t need to add any other context processor to make it available.

If you already have `user` available, then you don’t need to do anything else to get the profile. It’s already there as `user.profile`.

---

<div class="post-metadata">

**Author:** ![StephanieAG](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/stephanieag/32/17567_2.png) [@StephanieAG](https://forum.djangoproject.com/u/StephanieAG)\
**Post date:** [June 1, 2023, 12:53pm UTC](https://forum.djangoproject.com/t/url-template-tag-get-absolute-url-and-views/21249/8 "2023-06-01T12:53:53Z")

</div>

Ohh, I see now & tried it out. Thank you again; I’ve learned a lot and really appreciate your help.
