Search code examples
pythondjangodjango-formsdjango-viewsdjango-generic-views

Django: path doesn't find the right primary key in url path


I'm using django2 and I get an error when I access this url: http://127.0.0.1:8000/hotes/12/access/7/update I get an error 404 "None access object was found"

To make a long story short: I want to update an object linked to another. To do so, I have to send through the link, both primary keys (12 and 7 in the url). Also, I use the generic view "UpdateView" given by Django.

This is the path concerned in my project.urls:

urlpatterns = [
    path('hotes/<int:pk>/access/<int:access_pk>/update/',views.AccessUpdateView.as_view(), name='access_update'),
    path('hotes/add',views.host_add, name='host_add'),
    path('hotes/<int:pk>/', include([
        path('edit',views.HostUpdateView.as_view(), name='host_update'),
        path('delete',views.host_delete, name='host_delete'),
    ])),
    path('hotes/<int:pk>/add/', include([
        path('access',views.access_add, name='access_add'),
        path('oncall',views.onCall_add, name='onCall_add'),
        path('network',views.network_add, name='network_add'),
    ])),
    path('hotes/<int:pk>/',views.host_view, name='host_view'),
    path('hotes/',views.hosts_view, name='hosts_view'),
    path('', views.home, name='home'),
    path('admin/', admin.site.urls),
]

I want the second primary key to be used in my view "AccessUpdateView".

This is a part of my models.py:

class Host(models.Model):
    name = models.CharField(max_length=30, unique=True)
    usage = models.CharField(max_length=30, blank=True)
    function = models.CharField(max_length=30, blank=True)
    production = models.NullBooleanField(blank=True, null=True)
    place = models.CharField(max_length=30, blank=True)
    type = models.CharField(max_length=30, blank=True)
    processor = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
    storage = models.CharField(max_length=10, blank=True)
    memory = models.CharField(max_length=10, blank=True)
    dns_inner = models.CharField(max_length=50, blank=True)
    dns_extern = models.CharField(max_length=50, blank=True)
    os = models.ForeignKey(Os, null=True, related_name='hosts', on_delete=models.SET_NULL, blank=True)

class Access(models.Model):
    service = models.CharField(max_length=20)
    client_access = models.NullBooleanField(blank=True, null=True)
    ip = models.GenericIPAddressField()
    login = models.CharField(max_length=30, blank=True)
    password = models.CharField(max_length=50, blank=True)
    host = models.ForeignKey(Host, related_name='access', on_delete=models.CASCADE)

As you can see on host can have multiple access but an access in linked to only one host.

This is the view concerned:

class AccessUpdateView(UpdateView):
    model = Access
    fields = ('service','client_access','ip','login','password', )
    template_name = 'access_update.html'
    pk_url_kwarg = 'access_pk'
    context_object_name = 'access'

    def form_valid(self, form):
        access = form.save(commit=False)
        host_id = self.kwargs['pk']
        access.host_id = host_id
        access.save()
        return redirect('host_view', pk=host_id)

EDIT: new error when I try to access the url:

NoReverseMatch at /hotes/12/access/7/update/

Reverse for 'host_view' with arguments '('',)' not found. 1 pattern(s) tried: ['hotes\/(?P[0-9]+)\/$']


EDIT: The error was coming from "access_update.html" I removed the href in the Hote link which contained {% url host.pk %}

{% extends 'base.html' %}

{% load widget_tweaks %}

{% block title %}Modifier Acces{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'hosts_view' %}">Hotes</a></li>
  <li class="breadcrumb-item"><a href="">Hote</a></li>
  <li class="breadcrumb-item active">Modification Acces</li>
{% endblock %}

{% block contenu %}
  <form method="post" novalidate>
    {% csrf_token %}
    {% include 'includes/form.html' %}
    <button type="submit" class="btn btn-success">Modifier</button>
  </form>
{% endblock %}

The question is what is the right way to write the pk of the host in the url? (host_id doesn't work)


Solution

  • If you want to use access_pk, then you should set pk_url_kwarg = 'access_pk' in the view.

    In your form_valid method, you are using host without defining it. If pk from the URL is the host id, then you can access it with self.kwargs['pk'].

    def form_valid(self, form):
        access = form.save(commit=False)
        host_id = self.kwargs['pk']
        access.host_id = host_id
        access.save()
        return redirect('host_view', pk=host_id)
    

    Inside the template for the AccessUpdateView, you have access to access since that is the object that is being updated. If you want to use the host or its id, you should access it via access.

    {% url 'host_view' access.host_id %}