Django url/route order not maintained - django

I have the following in my root URLconf module (there's more, but not important, so left out):
urlpatterns = [
re_path(r'^password-reset-redirect-view/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
password_reset_redirect,
name = 'password_reset_confirm'),
path('', include('search.urls')),
path('', include('customer_portal.urls')),
path('rest-auth/', include('rest_auth.urls')),
path('rest-auth/registration/', include('rest_auth.registration.urls')),
Here's the customer_portal.urls:
urlpatterns = [
path('customer/contact/', views.contact),
path('', views.home),
re_path(r"^confirm-email/(?P<key>[-:\w]+)/$", views.email_verification,
name="account_confirm_email"),
]
Here's the rest_auth.registration.urls:
urlpatterns = [
url(r'^$', RegisterView.as_view(), name='rest_register'),
url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'),
url(r'^account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
name='account_confirm_email'),
]
As you can see both included urls.py urlpatterns have a view named 'account_confirm_email'.
Somewhere in the code this is ran:
url = reverse(
"account_confirm_email",
args=[emailconfirmation.key])
Since customer_portal.urls is included before rest_auth.registration.urls, I expect the route account_confirm_email in customer_portal.urls to be returned by the above reverse method. But instead I get the rest_auth.registration.urls route URL.
Just to be sure I commented out the route in rest_auth.registration.urls, and then I did get the correct URL (customer_portal URL) returned.
It is filled into an email, I check that email and see that I have the wanted url: http://127.0.0.1:8000/confirm-email/......./, instead of: http://127.0.0.1:8000/rest-auth/registration/account-confirm-email/...../
Can anyone tell me why the customer_portal URL isn't the one being reversed in both cases?
Django docs say:
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL.

Related

Extra space showing in end url

I am having an issue with Panda. I am trying to add a new views to my website and when I type this line in the product/urls.py file I get an error stating that the endpoint was not found by Panda - Page not found (http://127.0.0.1:8000/products/new/)
Using the URLconf defined in pyshop.urls, Django tried these URL patterns, in this order:
admin/
products/
products/ new
The current path, products/new/, didn't match any of these.
I have created a new function to hold the new products view
def new(request):
return HttpResponse('New Product')
Then I have mapped the endpoint in the products/urls.py file
urlpatterns = [
path('', views.index),
**path('new', views.new)**
]
P.S. The index is behaving as expected
The space is just formatting to show that these is a set of suburls. The problem is the trailing slash:
urlpatterns = [
path('', views.index),
path('new/', views.new)
]

How to write a urls.py in django so that I can do something like */page

Here is the problem:
I have an app with the following models: project, position, outreach
A position is connected to a project and project only with a Foreign key
An outreach is connected to a position and a position only with a Foreign key
I can create a new project from almost anywhere in my app (same for the other objects). Currently I wrote that a new project is created from the url dashboard/newjobproject but I would to make it so that depending on the page that I am, the url simply becomes something like www.myapp.com/..../newproject
What's a way to write the urls.py to achieve that?
from django.urls import path
from action import views
app_name = 'action'
urlpatterns = [
# ex: /action/
path('', views.login, name='login'),
path('dashboard/', views.dashboard, name='dashboard'),
path('contacts/', views.contacts, name='contacts'),
path('projects/', views.project, name='project'),
path('contacts/newcontact', views.new_contact, name='new_contact'),
path('projects/newjobproject', views.new_outreach, name='new_outreach'),
path('dashboard/newjobproject', views.new_jobproject, name='new_jobproject'),
path('projects/<uuid>/newjobposition', views.new_jobposition, name='new_jobposition'),
]
However,
Try adding this to the bottom of urlpatterns:
path('<path:p>/newjobproject', views.new_jobproject, name='whatever-name-you-want'),
and in your views.py:
def new_jobproject(request, p):
Tbh though, this is sort of a hacky way to do it. It'll break in a few locations. If you have a main urlpatterns array in which you're including the urls for this 'action' app as APIs, this solution won't work outside the API urls.
For eg. if you have:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('action.urls')),
]
And you access your url like this -> www.myapp.com/api/v1/....../newjobproject, then the code will work. If you try to access www.myapp.com/..../newjobproject or www.myapp.com/admin/..../newjobproject then the code will break and it will not match any of the paths. I'm not sure how you'd get it to work in that case.
If the above scenario is not an issue, if you're not going to be using these views as APIs, and your urlpatterns looks something like this:
urlpatterns = [
path('admin', admin.site.urls),
path('/', include('action.urls')),
]
then the code should work for all urls except for the admin/.../newjobproject case.

How to include part of the url patterns from an Django app

I have two django apps with URLs
app_name = 'app1'
urlpatterns = [
path('url1/', ..., name='name1')
path('<slug:username>/', ..., name='name2')
]
and
app_name = 'app2'
urlpatterns = [
path('url2/', ..., name='name3')
path('<slug:username>/action2/', ..., name='name4')
]
This would not work if I include them in the master urlpatterns as
urlpatterns = [
path('', include('app1.urls'),
path('', include('app2.urls'),
]
because url2/ would first match <slug:username>/ and trigger an error for unknown username.
There are a few potential solutions but none works very well for me:
Use non-slug url2 such as ~url2. That means all urls in app2 has to start with something like ~ or ^.
Redefine some URLs in the master urlpatterns but then I will have to import views from the apps and remove urls from the app urlpattern.
Use regular expression to explicitly exclude some names from the <slug:username>. This could work but then any changes in app2 urlpatterns need to be reflected in app1's <slug:username> ... exclude certain names.
It is possible to do something like
urlpatterns = [
path('', include('app1.urls'), # non-user part
path('', include('app2.urls'), # non-user part
path('', include('app1.urls'), # user part
path('', include('app2.urls'), # user part
]
so that fixed-name URLs will be matched before <slug:username>?
From Django docs:
include((pattern_list, app_namespace), namespace=None)
Parameters:
pattern_list – Iterable of path() and/or re_path() instances.
app_namespace (str) – Application namespace for the URL entries being
included
You can include specific urls with this method:
urlpatterns = [
path('', include(([path('url1/', <YourViewName>)], 'app1'))),
path('', include(([path('url2/', <YourViewName>)], 'app2'))),
path('', include(([path('<slug:username>/', <YourViewName>)], 'app1'))),
path('', include(([path('<slug:username>/action2/', < YourViewName >)], 'app2'))),
]
First element of tuple inside include is the list of path/re_path instances that you want to include, and the second one is the app name.

Django set default empty url

I'm trying to learn Django and I'm following Corey Shafer's tutorials (https://www.youtube.com/watch?v=a48xeeo5Vnk), but when I try to make two different pages, I get automatically directed to the one with an "empty address":
In his:
/Blog
/urls.py
it looks like this:
from django.conf.urls import path
from . import views
urlpatterns = [
path('', views.home, name='blog-home'),
path('about/', views.about, name='blog-about'),
]
and when he goes to localhost:8000/blog/about, the page displays correctly
When I try to imitate his code for blog/urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'', views.home, name='blog-home'),
url(r'^about/', views.about, name='blog-about'),
]
the result of the localhost:8000/blog/about is the content of views.home, and not views.about.
The following works correctly, when I write a name instead of an empty string:
urlpatterns = [
url(r'^home', views.home, name='blog-home'),
url(r'^about/', views.about, name='blog-about'),
]
But I don't understand why it worked in a previous version, why it won't work now, and what could fix it
A url matches if it can find a substring that matches, the empty string r'' thus matches every string.
You should make use of anchors to specify the start (^) and end ($) of the string:
urlpatterns = [
# &downarrow; &downarrow; anchors
url(r'^/$', views.home, name='blog-home'),
url(r'^about/', views.about, name='blog-about'),
]
Note: As of django-3.1, url(…) [Django-doc] is
deprecated in favor of re_path(…) [Django-doc].
Furthermore a new syntax for paths has been introduced with path converters: you
use path(…) [Django-doc] for that.

Router not displaying correct URL in Django RestFramework?

This is how I defined urls.py file of my app
router = DefaultRouter()
router.register('hello-viewset', views.HelloViewSet, base_name='hello-viewset')
router.register('profiles', views.UserProfileViewSet)
router.register('schema', views.SchemaViewSet)
router.register('creddefination', views.CredDefViewSet)
router.register('overalltable', views.OverallViewSet)
urlpatterns = [
path('', include(router.urls)),
]
urls.py of Project:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('DIAPI.urls')),
]
I am not getting correct address for creddefination. But when i manually go to http://127.0.0.1:7000/api/creddefination/ it is working. It is just not displaying correctly. What might be reason for this
I guess views.CredDefViewSet and views.OverallViewSet are using the same model.
If that's true, then the default register's basename will be named after that model and used as name in a call to Django's reverse url construction. Since the API Root view will be trying to resolve both views with the same name, it'll lead to the same url.
Workaround is to explicitly add a basename to one of the view:
router.register('creddefination', views.CredDefViewSet, basename='creddeef')