django.urls.exceptions.NoReverseMatch: 1 pattern(s) tried: ['link/edit/(?P<pk>[0-9]+)/\\Z']

72 views Asked by At

I am trying to use Django with HTMX. My task is to do editable row in the link list with HTMX. When I click the edit button in the row, I am getting NoReverseMatch error.

raise NoReverseMatch(msg) django.urls.exceptions.NoReverseMatch: Reverse for 'link_update' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['link/edit/(?P[0-9]+)/\Z'] [17/Oct/2023 12:54:42] "GET /link/edit/97/ HTTP/1.1" 500 118059

I've tried so many methods on stackoverflow but I didn't figure it out. Here my code is:

urls.py in my project


urlpatterns = [
    path('', home, name='home'),
    path('admin/', admin.site.urls),
    path("user/", include("user_profile.urls", namespace="user")),
    path("link/", include("link.urls", namespace='link')),

]

urls.py in my link app

from django.urls import path
from link.views import tag_view, category_view, delete_links, delete_link, get_row_view, LinkUpdateView, LinkyListView


app_name = 'link'

urlpatterns = [
    path('edit/<int:pk>/', LinkUpdateView.as_view(), name='link_update'),
    path('edit/<int:pk>/get_row_view/', get_row_view, name='get_row_view'),
    path('tag/<slug:tag_slug>/', tag_view, name='tag_view'),
    path('category/<slug:category_slug>/', category_view, name='category_view'),
    path('delete/', delete_links, name='delete_links'),
    path('delete-item/<int:pk>/', delete_link, name='delete_link'),
    path('list', LinkyListView.as_view(), name='link_list_view'),
]

views.py in my link app

def get_row_view(request, pk):
    link = LinkUrl.objects.get(pk=pk)
    context = dict(
        link=link,
    )
    return render(request, 'link/row.html', context)

class LinkUpdateView(UpdateView):
    model = LinkUrl
    form_class = LinkModelForm
    template_name = 'link/edit_link_form.html'

    def get_success_url(self):
        return reverse_lazy('link:link_update', kwargs={'pk': self.object.pk})

row.html

<tr id="link_list_{{link.pk}}">
    <td>{{ link.title }}</td>
    <td>{{ link.get_full_url }}</td>
    <td>  
      <span class="p-1 border fs-12 rounded text-light bg-primary">
        <a href="{{ link.category.get_absolute_url }}" class="text-decoration-none text-light">
          {{ link.category }}
        </a>
      </span>
    </td>
    <td>   
        {% for shortcode, tags in tags_by_shortcode.items %}
        {% if shortcode == link.shortcode %}
          {% for tag in tags %}
            <li class="list-inline-item border p-1 text-muted fs-12 mb-1 rounded">
              <a href="{{ tag.get_absolute_url }}" class="text-decoration-none text-muted">
                {{ tag.title }}
              </a>
            </li>
          {% endfor %}
        {% endif %}
      {% endfor %}
    </td>

    <td>
        <button class="btn btn-warning" 
        hx-get="{% url 'link:link_update' pk=link.pk %}"
        hx-target="#link_list_{{link.pk}}">
        Edit
        </button>
    </td>
    <td>
        <button
        class="btn btn-danger btn-sm float-end"
        hx-delete="{% url 'link:delete_link' link.pk %}"
        hx-confirm="Are you sure you want to delete this item?"
        hx-target="#link_list_{{link.id}}"
        hx-swap="outerHTML"> Delete
        </button>    
      </td>
  </tr>

edit_link_form.html

<td>
    <form hx-post="{% url 'link:link_update' pk=link.pk %}">
        {% csrf_token %}
          {% load crispy_forms_tags %}
         
          {% for field in form %}
               <td colspan="{{ form|length }}">
                    <div class="form-row">
                    <div class="form-group col-md-12">
                    {{ field|as_crispy_field }}
                    </div>
                    </div>
                </td>    
          {% endfor %}
        <td>
            <button class="btn btn-primary" type="submit" hx-get="{% url 'link:get_row_view' pk=link.pk %}" hx-target="#form-container"> Save </button>   
        </td>
        <td>
            <button class="btn btn-danger" type="button">Cancel</button>
        </td>
    </form>
</td>

2

There are 2 answers

0
KingRanTheMan On BEST ANSWER

This part of your error message highlights the problem: NoReverseMatch: Reverse for 'link_update' with keyword arguments '{'pk': ''}' not found. Specifically, an empty string is being passed when you call link.pk, rather than the primary_key (id) of the link.

Now the problem is not with the hx-get attribute inside your edit button; that is working because we can see from your traceback that clicking it makes a get request to the appropriate id (97).

So the error is inside the edit_link_form.html template, and most likely the form itself inside its hx-post attribute.

My suspicion is that because this template is rendered by the 'LinkUpdateView' which inherits from 'UpdateView' class, so you are encountering this issue because 'link' does not exist in the context dictionary passed to the template --- instead it is referred to as 'object', which is the default context variable for the UpdateView.

So to fix this, inside edit_link_form.html change any reference to link to object (including the save button as well as the form), and hopefully you will be fine now (see below for full code):

edit_link_form.html

<td>
    <form hx-post="{% url 'link:link_update' pk=object.pk %}"> 
    <!-- notice it is now 'object.pk' rather than 'link.pk' -->
        {% csrf_token %}
          {% load crispy_forms_tags %}
         
          {% for field in form %}
               <td colspan="{{ form|length }}">
                    <div class="form-row">
                    <div class="form-group col-md-12">
                    {{ field|as_crispy_field }}
                    </div>
                    </div>
                </td>    
          {% endfor %}
        <td>
            <button class="btn btn-primary" type="submit" hx-get="{% url 'link:get_row_view' pk=object.pk %}" hx-target="#form-container"> 
            <!-- as above, 'object.pk' rather than 'link.pk' -->
                Save 
            </button>   
        </td>
        <td>
            <button class="btn btn-danger" type="button">Cancel</button>
        </td>
    </form>
</td>
2
Roberto Rubertelli On

The target id and the tr id in your html

hx-target="#link_list_{{link.pk}}">

and

Try to change them with:

hx-target="#link_list">

and

<tr id="link_list">

It is not common to put the row id in targets and in tags. The id has to stay in the hx-get - hx-post - hx-patch - hx-delete - hx-put to just to mention the CRUD concept. It is a suggestion to keep in mind.

If you want to give a look to some Htmx example and demo here my own page

https://www.managepy.it/demo/