Search code examples
pythondjangourl-pattern

Why do errors differ when I interchange the order of the members of url patterns


Note: I'm not requesting for a help to fix the errors, but to ask the reason why they differ by interchanging the order of url patterns.

The following is how my urls.py of my django application (basic_app) looks like:

from django.urls import path, re_path
from basic_app import views

app_name = 'basic_app'

urlpatterns = [
    path('', views.SchoolListView.as_view(), name='list'),
    re_path(r'^(?P<pk>[-\w]+)/$', views.SchoolDetailView.as_view(), name='detail'),
    path('create/', views.SchoolCreateView.as_view(), name='create'),
]

when I run the server and type in the url http://127.0.0.1:8000/basic_app/create/, it throws the following error:

ValueError at /basic_app/create/
Field 'id' expected a number but got 'create'.
Request Method: GET
Request URL:    http://127.0.0.1:8000/basic_app/create/
Django Version: 3.2.4
Exception Type: ValueError
Exception Value:    
Field 'id' expected a number but got 'create'.

Interestingly when I interchanged the order of the 2nd and 3rd url patterns as follows:

from django.urls import path, re_path
from basic_app import views

app_name = 'basic_app'

urlpatterns = [
    path('', views.SchoolListView.as_view(), name='list'),
    path('create/', views.SchoolCreateView.as_view(), name='create'),
    re_path(r'^(?P<pk>[-\w]+)/$', views.SchoolDetailView.as_view(), name='detail'),
]

I got a different error:

ImproperlyConfigured at /basic_app/create/
Using ModelFormMixin (base class of SchoolCreateView) without the 'fields' attribute is prohibited.
Request Method: GET
Request URL:    http://127.0.0.1:8000/basic_app/create/
Django Version: 3.2.4
Exception Type: ImproperlyConfigured
Exception Value:    
Using ModelFormMixin (base class of SchoolCreateView) without the 'fields' attribute is prohibited.

I am new to django and curious about this event. Why doen't python let create/ go to the next pattern to check since it did not fit the first pattern?


My views.py file looks like this:

from django.shortcuts import render
from django.views.generic import (
    View,
    TemplateView,
    ListView,
    DetailView,
    CreateView,
    UpdateView,
    DeleteView
)
from . import models


# Create your views here.
class IndexView(TemplateView):
    template_name = 'index.html'


class SchoolListView(ListView):
    context_object_name = 'schools'
    model = models.School


class SchoolDetailView(DetailView):
    context_object_name = 'school_detail'
    model = models.School
    template_name = 'basic_app/school_detail.html'


class SchoolCreateView(CreateView):
    model = models.School

Solution

  • Django aims to match url's top to bottom. If you thus visit /basic_app/create/, and the rule with the SchoolDetailView is first, it will fire that view, since create/ matches with the regex (?P<pk>[-\w+])/$. Since the primary key of your object is likely an AutoField, it makes no sense to match a create/ with pk, since we expect this to be an integer.

    Another problem is that your SchoolCreateView is not configured correctly, you need to specify what fields will be used in the CreateView, so we can implement this with:

    class SchoolCreateView(CreateView):
        model = models.School
        fields = '__all__'

    We can refine the re_path of the detail view to only be triggered with an int, not with an arbitrary slug:

    re_path(r'^(?P<pk>\d+)/$', views.SchoolDetailView.as_view(), name='detail'),

    or wiht a path converter:

    path(r'<int:pk>/', views.SchoolDetailView.as_view(), name='detail'),