How to capture a blank slug value in a django url pattern? - django

I know I can do this with re_path, but is there a way to make a slug optional in a URL? Something like:
path("issues/<int:pk>/<optional-slug:slug>/")
I'm working on upgrading to Django 2.0, and trying to remove as many of my old-style, regex-based url patterns as possible.

The way to do this is to register a "converter". There are some docs about it here:
https://docs.djangoproject.com/en/3.1/topics/http/urls/#registering-custom-path-converters
In this instance, you put something like this in your root urls.py file (not necessarily the one where you want to use it):
from myproject.lib.converters import BlankSlugConverter
register_converter(BlankSlugConverter, "blank-slug")
Then in converters.py, you put:
from django.urls.converters import StringConverter
class BlankSlugConverter(StringConverter):
"""A slug converter that allows blank values
This just swapped out the plus sign in the SlugConverter for an asterisk.
"""
regex = "[-a-zA-Z0-9_]*"
And in your urls.py pattern, you can do:
path("issues/<int:pk>/<blank-slug:slug>/")
And that's it! No need for an additional import or anything in that urls.py file. The register_converter call makes it available everywhere.

Related

Urlpatterns: category/subcategory/article-slug

Django==3.2.5
re_path(r'^.*/.*/<slug:slug>/$', Single.as_view(), name="single"),
Here I'm trying to organize the following pattern: category/subcategory/article-slug. Category and subcategory are not identifying anything in this case. Only slug is meaningful.
Now I try:
http://localhost:8000/progr/django/1/
And get this:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/progr/django/1/
Using the URLconf defined in articles_project.urls, Django tried these URL patterns, in this order:
admin/
^.*/.*/<slug:slug>/$ [name='single']
articles/
^static/(?P<path>.*)$
^media/(?P<path>.*)$
The current path, progr/django/1/, didn’t match any of these.
You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
What can I do to resolve this?
You are mixing the path and re_path functions, re_path has no path converters and only uses regex, hence when you write <slug:slug> that literally means a url having that exact string there, instead you want to capture the pattern [-a-zA-Z0-9_]+ (which is the pattern Django uses for a slug). Also using .* in your pattern will likely cause you problems, as it can match / too and likely cause some of your other urls to be never used, instead you might want to use [^/]*. So you likely want to change your pattern to:
re_path(r'^[^/]*/[^/]*/(?P<slug>[-a-zA-Z0-9_]+)/$', Single.as_view(), name="single"),
This still feels a little problematic to me as it matches two arbitrary patterns and doesn't capture and pass them to the view, you might in fact simply want to shift to using path and capture these patterns too:
from django.urls import path
path('<str:some_string1>/<str:some_string2>/<slug:slug>/', Single.as_view(), name="single"),

Django - issue with APPEND_SLASH and POST requests

I am using Django 1.10, and my goal now is to make urls available both with and without trailing slash. To do this, I added slash to all my URLs in the URLConf files, and then set APPEND_SLASH variable value to True (well, this is the default value).
Now the problem is that external POST requests (which I can't control) yield the following error:
You called this URL via POST, but the URL doesn't end in a slash and
you have APPEND_SLASH set. Django can't redirect to the slash URL
while maintaining POST data. Change your form to point to
127.0.0.1:8000/Calendar/AddAccounts/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
They mention this in Django doc, and yet after hours of surfing the net, I can't figure out how to address this issue.
I've also come across this question, but the proposed solution doesn't seem acceptable to me. It says I have to make the users call my URLs only with trailing slash. While I know that in other languages (C# for example) it is possible to enable both options
It seems weird to me that you want to support both cases. Ideally you would want to redirect from non slash to slash(or the other way around if you want that) on the server level (nginx/apache/whatever you use) before the request hits Django.
Just choose a strategy and stick to it, so add the trailing slash to your form and never look back. :)
It's important to be consistent. https://www.branded3.com/blog/urls-trailing-slash-seo/
If the urls are used for APIs or the SEO is not important to you, you can consider both with slash and without slash by adding "/?". In django 3.X:
from django.urls import re_path
re_path(r'^query/?$', 'search.views.query'),
re_path(r'^add/?$', 'search.views.add'),
In Restframework routers:
from rest_framework.routers import DefaultRouter
class CustomDefaultRouter(DefaultRouter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.trailing_slash = '/?'
router = CustomDefaultRouter()
router.register('link', ViewSet, basename='link')

Is it possible to write my views in multiple files in Django?

The file "views.py" of my Django application has over 4000 lines of code right now, and it is difficult to scroll down every time to a specific view in my text editor.
I would like to divide this file up into multiple ones.
Is this feasible? I imagine that my "urls.py" would change accordingly. Would I still need to keep my "views.py" file?
Unlike models.py, there's nothing magical about the views.py file in Django. A view is simply a function referred to by a URLconf, and that can be in any module at all. Just make sure you refer to it properly, either by giving the full module as text, using the prefix, or importing it:
urls = patterns('',
(r'url1', 'app.views1.my_view1'),
(r'url2', 'app.views2.my_view2')
...
or
from app import views1, views2
urls = patterns('',
(r'url1', views1.my_view1),
(r'url2', views2.my_view2)
...
or even:
urls = patterns('app.views1',
(r'url1', 'my_view1'),
urls += patterns('app.views2',
(r'url2', 'my_view2'),
Sure, you can separate your views. You don't have to change your urls either. Just import the other file, and your original views file will inherit everything in the other file.
#views.py
from app.other_view import *
def your_view(request):
#your code here
#other_view.py
def your_view(request):
#your code here
There's ways to break up the default views.py, models.py, etc. files into modules of their own within the app, but I've found this usually ends up creating more problems than it solves.
In general, if you find the files too weighty, it's probably a better use of your time to refactor your code, instead. Do you really need all those views? Is there any way to simplify the logic? Would you benefit from breaking functionality out into an entirely separate app? These are the questions you should ask.

Referring to databrowse urls in templates

I'd like to integrate django Databrowse into my application.
It comes down to pointing to databrowse urls from within template or view for enhanced drill down functionality of the databrowse.
Is there an easy way to extract the url from a databrowse object?
Well, one easy way would be to just construct the url you want, and pass it into the template:
databrowse_url = '/'.join((obj._meta.app_label, obj._meta.module_name, 'objects', str(obj.id)))
And then in the template (assuming you have databrowse situated at /databrowse:
<a href="/databrowse/{{ databrowse_url }}">
Which would give you a url like: /databrowse/app_name/model_name/objects/1.
You could recreate the databrowse urls in the format thats show in the databrowse urls.py
You might be able to get the url tag to work in your template by passing the view name + arguments.
However, if you browse the source, it looks like databrowse will add a 'url' attribute to the objects it works with.
EDIT:
Given an EasyModel instance, you can do the following:
my_easy_model_instance.url()
Most of the 'Easy' classes have a url() or urls() method on them.
Ended up writing mixin class that fetches relevant EasyInstance and reuses the url() of it:
from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse import site
class DatabrowseMixin:
def url(pyClass):
if not site.root_url:
#hack, but root_url is not set until the first databrowse view
#and resolving urlconf does not work either
site.root_url = '/databrowse/'
easy_model = EasyModel(site, pyClass.__class__)
obj = easy_model.object_by_pk(pyClass.pk)
return obj.url()
class MyModel(models.Model, DatabrowseMixin):
...
Now in my templates I can reuse my_model_instance.url tag pointing to databrowse object url.

django urlpatterns from sql

I am trying to create urlpatterns with sql query, but this will work only for those things that already was in sql table at the moment of server start. If it possible to make django to check for new urls dynamically from database?
I know, this can be done with regular expressions, but they are too greedy, I mean, that i need to make this at root level of my site and regexp will "eat" all matching names and this regexp must be the last of urlpatterns list.
Going on your comment to pyeleven's answer, it seems you have understood the point of urlpatterns. You don't need or want to specify the choices of your section in the urlconf. What you do is grab the value of each section of the url, and pass that as a parameter to the view. So, for example:
(r'^?P<section>\w+)/$', 'my_view')
This will grab urls like /name1/ and /name2/, and pass name1 and name2 to the view as the section parameter. So there's no need to change the code whenever you add a section.
Although this is the nastiest, most un-django-esque thing imaginable, you can get your urls from the db, if you really, really want to:
models.py:
from django.db import models
class Url(models.Model):
name = models.CharField(max_length=20)
urls.py:
from my_app.models import Url
urls = []
for url_object in Url.objects.all():
urls.append(url(url_object.name, 'my_view'))
urlpatterns = patterns('my_app.views', *urls)
Voilà. It actually works. Url patterns directly from the db. Please don't do this.
I'm going to go take a shower now.
Have you checked django flatpages?
http://docs.djangoproject.com/en/dev/ref/contrib/flatpages/?from=olddocs
Dynamic url might not be such a good idea, for example a bad url line added dynamically might make the server stop functioning.
Can you elaborate on your goals?