How to get the variable in my {% url %} tag working

Hello together,

my first own post here. A few weeks ago I started with Django and my project shows already good progress and I am having great fun with Python, HTML, CSS and now also with Django, learning every day (in my middle 50ties…). But now I got stuck at a certain point regarding a link in a template. Please let me explain:

The template does not work for the expression de.quote.pk within the {% url %} tag:

{% for de in deliv %}
  <tr>
      ...
      <td><a href="{% url 'app01:quote_details' de.quote.pk %}">{{ de.quote.pk }}</a></td>
      ...
   </tr>
    {% endfor %}

But it works as soon as I directly bring in the expression pk=2 (the 2 is just an example for the pk. It could also be any other pk-number):

{% for de in deliv %}
  <tr>
      ...
      <td><a href="{% url 'app01:quote_details' pk=2 %}">{{ de.quote.pk }}</a></td>
      ...
   </tr>
    {% endfor %}

So obviously I am wrong in my thinking that the expression de.quote.pk within the {% url %} tag is getting resolved as the pk which I’d like to get handed over to the app01:quote_details template.

Here’s the urls.py,
the last line contains the path I’d like to jump in with my pk of the quote-table which is assigned via its pk as ForeignKey in the deliverables-table. Models see further below.

  from django.urls import path
  from . import views

  app_name = "app01"
  urlpatterns = [
      path("", views.index, name="index"),
      ...
    path("deliverables", views.deliverables, name="deliverables"),
    path("deliverable-details/<int:pk>/", views.deliverable_details, name="deliverable_details"),
    path("quotes", views.quotes, name="quotes"),
    path("quote-details/<int:pk>/", views.quote_details, name="quote_details"),
]

I also tried an additional line with <int:quote_pk>

  urlpatterns = [
      ...
    path("quote-details/<int:pk>/", views.quote_details, name="quote_details"),
    path("quote-details/<int:quote_pk>/", views.quote_details, name="quote_details"),    path("quote-details/<int:quote_pk>/", views.quote_details, name="quote_details"),
]

But it does not work either. Maybe I just haven’t applied the correct syntax or I have a lack of understanding at a certain point how this is supposed to work. Yes, I read a lot of documentation, did much G-search etc but so success so far.

To make it complete, here’s the models:

class Quote(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    number = models.CharField(max_length=20, default="QTExxxxxx", unique=True) #QT000001
    name = models.CharField(max_length=120, default="TBD")
    descriptshort = models.CharField(max_length=200, blank=True, default="")
    contact = models.CharField(max_length=200, blank=True, default="")
    descriptlong = models.TextField(blank=True, default="")
    content = models.TextField(blank=True, default="")
    dateinitiated = models.DateField(null=True, blank=True)
    dateoffered = models.DateField(null=True, blank=True)
    dateordered = models.DateField(null=True, blank=True)
    totalamount_offered = models.DecimalField(max_digits=12, decimal_places=0)
    totalamount_ordered = models.DecimalField(max_digits=12, decimal_places=0)
    status = models.ForeignKey(Gatestatus, related_name="+", default=1, blank=True, on_delete=models.CASCADE)
    isactive = models.BooleanField(default=True)
    def __str__(self):
        return f"Quote ID.{self.pk} {self.number}: {self.name}"

class Deliverable(models.Model):
    """z.B. Kundenmodelle"""
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    quote = models.ForeignKey(Quote, on_delete=models.CASCADE, null=True, blank=True)
    name = models.CharField(max_length=120)
    number = models.CharField(max_length=20, default="26DEL1234")
    datedue = models.DateField(null=True, blank=True)
    datedelivered = models.DateField(null=True, blank=True)
    status = models.ForeignKey(Gatestatus, related_name="+", default=1, blank=True, on_delete=models.CASCADE)
    descriptshort = models.CharField(max_length=200, blank=True, default="")
    descriptlong = models.TextField(blank=True, default="")
    isactive = models.BooleanField(default=True)
    def __str__(self):
        return f"Deliverable  {self.number}: {self.datedue} {self.name}"

And finally this is the view for the deliverables which is assigned to the template as described at the top of this post:

def deliverables(request: HttpRequest) -> HttpResponse:
    deliverables = Deliverable.objects.all().order_by("datedue")
    ctx = {"deliv": deliverables}
    return render(request, "app01/deliverables.html", ctx)

And finally this is the view for the quote_details:

def quote_details(request: HttpRequest, pk) -> HttpResponse:
    """Detailansicht Quote mit Parameteruebergabe"""
    quote = get_object_or_404(Quote, pk=pk)
    ctx = {"myquot": quote}
    return render(request, "app01/quote_details.html", ctx)

Sorry for this long post but I have tried to provide all the relevant information.
Thanks in advance!
Mirko

Welcome @raspbuino !

You’ve done good. However, you haven’t specified what error you are receiving, and haven’t posted the full error message with the complete traceback.

If I assume the most likely situation, you have:

This is going to look for the quote attribute in an element named deliv in your context, which are to be instances of Deliverables.

I see that:

Which means that you have no guarantee that the expression de.quote.pk is actually going to resolve to a value - which is the most likely cause of a problem in your template above. If there’s an instance with quote == None, the template will fail.

(Now, if the error isn’t what I think it’s likely to be, NoReverseMatch, then the issue is somewhere else.)

Hi Ken,
thank you very much for this very quick response. The full traceback looks like pasted below.
The null=True attribute has recently been added and it was just another trial to get my issue fixed (but I am aware about this concern, so thank you as well for pointing this out. I will delete it again.)

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/deliverables

Django Version: 6.0
Python Version: 3.12.2
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'app01']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template C:\Users\aschelm1\djangodir\app01\templates\app01\deliverables.html, error at line 53
   Reverse for 'quote_details' with arguments '('',)' not found. 2 pattern(s) tried: ['quote\\-details/(?P<quote_pk>[0-9]+)/\\Z', 'quote\\-details/(?P<pk>[0-9]+)/\\Z']
   43 :                 <tbody>
   44 :                     {% for de in deliv %}
   45 :                         <tr>
   46 :                             <td>{{ de.pk }}</td>
   47 :                             <td>{{ de.project.mainproject.customer.nameshort }}</td>
   48 :                             <td>{{ de.project.mainproject.name }}</td>
   49 :                             <td>{{ de.project.name }}</td>
   50 :                             <td><a href="{% url 'app01:deliverable_details' de.pk %}">{{ de.number }}</a></td>
   51 :                             <td><a href="{% url 'app01:deliverable_details' de.pk %}">{{ de.name }}</a></td>
   52 :                             <td>{{ de.descriptshort }}</td>
   53 :                             <td><a href=" {% url 'app01:quote_details' de.quote.pk %} ">{{ de.quote.pk }}</a></td>
   54 :                             <td>{{ de.quote.pk }} {{ de.quote.number }} {{ de.quote.name }}</td>
   55 :                             <td>{{ de.datedue|date:"Y-m-d" }}</td>
   56 :                             <td>{{ de.datedelivered|date:"Y-m-d" }}</td>
   57 :                             <td class="{% if de.status.pk == 4 %}bggreen {% endif %}">{{ de.status }}</td>
   58 :                             <td>{{ de.isactive }}</td>
   59 :                             {#<td>{{ de.status }}</td>#}
   60 :                             {#<td><small>{{ de.descriptlong|linebreaksbr }}</small></td>#}
   61 :                         </tr>
   62 :                     {% endfor %}
   63 :                 </tbody>


Traceback (most recent call last):
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\core\handlers\base.py", line 198, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\djangodir\app01\views.py", line 80, in deliverables
    return render(request, "app01/deliverables.html", ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\shortcuts.py", line 25, in render
    content = loader.render_to_string(template_name, context, request, using=using)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\loader.py", line 62, in render_to_string
    return template.render(context, request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\backends\django.py", line 107, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\base.py", line 174, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\base.py", line 166, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\base.py", line 1091, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\base.py", line 1052, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\defaulttags.py", line 249, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\base.py", line 1052, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\template\defaulttags.py", line 511, in render
    url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\urls\base.py", line 98, in reverse
    resolved_url = resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\aschelm1\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\urls\resolvers.py", line 842, in _reverse_with_prefix
    raise NoReverseMatch(msg)
    ^^^^^^^^^^^^^^^^^^^^^^^^^

Exception Type: NoReverseMatch at /deliverables
Exception Value: Reverse for 'quote_details' with arguments '('',)' not found. 2 pattern(s) tried: ['quote\\-details/(?P<quote_pk>[0-9]+)/\\Z', 'quote\\-details/(?P<pk>[0-9]+)/\\Z']

Mirko

WOW!
That was it: I assigned a quote to every deliverable and now it works.
:blush:
Thank you Ken for leading me to this solution!!!

However, on the other hand, I am pretty sure I added this null=True statement after I already tried to get the template working. Because it is important to allow also deliverables withot a quote assigned. I am going to try this out once again without this attribute and with quotes dettached.

Thanks a lot, you made my day!
Mirko