2

I'm using path converter in my django app like so:

# urls.py

from . import views
from django.urls import path

urlpatterns = [
  path('articles/<str:collection>', views.ArticleView),
]


# views.py

@login_required
def ArticleView(request, collection):
    print(collection)
    if collection == "None":
        articles_query = ArticleModel.objects.all()
    ...

This works fine in development for a url suck as : http://localhost:8000/articles/My Collection which gets encoded to http://localhost:8000/articles/My%20Collection, and is decoded properly in the ArticleView. However, in development, I have to edit the view like so to get it to work:

# views.py

import urllib.parse

@login_required
def ArticleView(request, collection):
    collection = urllib.parse.unquote(collection)
    print(collection)
    if collection == "None":
        articles_query = ArticleModel.objects.all()
    ...

Otherwise, the print(collection) shows My%20Collection and the whole logic in the rest of the view fails.

requirements.txt

asgiref==3.2.10
Django==3.1.1
django-crispy-forms==1.9.2
django-floppyforms==1.9.0
django-widget-tweaks==1.4.8
lxml==4.5.2
Pillow==7.2.0
python-pptx==0.6.18
pytz==2020.1
sqlparse==0.3.1
XlsxWriter==1.3.3
pymysql

What am I doing wrong here? Thanks in advance!

Charles
  • 555
  • 4
  • 16
  • 1
    "This happens in the admin interface as well." can you be more specific about what happens there? – schillingt Feb 18 '21 at 16:00
  • I was wrong, the issue I was having in the admin interface was unrelated, I've edited the question accordingly. – Charles Feb 18 '21 at 16:04

1 Answers1

1

The URL is being urlencoded which encodes spaces as %20. There are a number of other encodings. As you've discovered you need to decode that parameter in order to compare it to what you'd expect. As you've likely realized, if you have a value that actually wants The%20News and not The News, you have no recourse. To handle this people will create a slug field. Django has a model field for this in the framework.

This is typically a URL-friendly, unique value for the record.

Assuming you add a slug = models.SlugField() to ArticleModel, your urls and view can change into:

urlpatterns = [
  # Define a path without a slug to identify the show all code path and avoid the None sentinel value.
  path('articles', views.ArticleView, name='article-list'),
  path('articles/<slug:slug>' views.ArticleView, name='article-slug-list'),
]


@login_required
def ArticleView(request, slug=None):
    articles_query = ArticleModel.objects.all()
    if slug:
        articles_query = articles_query.filter(slug=slug)
schillingt
  • 13,493
  • 2
  • 32
  • 34
  • Thanks you very much for your detailed answered. I understand the need for a slug field, however, I still don't understand why not using a slug field like I do worked fine in development but doesn't work correctly in production ? – Charles Feb 18 '21 at 16:12
  • 1
    It seems like `runserver` is auto decoding the parameters. I wonder if that operates differently than the web server you're using. – schillingt Feb 18 '21 at 16:21
  • This is probably what's going on, I'll try to get more infos on that. Thanks you very much! – Charles Feb 18 '21 at 16:26