Django 404 on POST request - python

I'm trying to send a form via POST on my site, however, it appears the first part of the POST URL is being chopped off leading to it not being able to match the correct url.
The form I'm trying to send in the template:
<form action= "{% url 'change_portrait' %}" method="post">
{% csrf_token %}
<h5>Edit Portrait</h5>
<input type="hidden" name="portrait_id" value="{{image.pk}}">
<button type="submit" name="button" class="btn btn-primary">Submit Changes</button>
</form>
which correctly renders to:
<form action= "/AdminDashboard/EditPortraits/ChangePortrait/" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='iCt1xvIc1KZXErhbcMNepk2daHXApGih' />
<h5>Edit Portrait</h5>
<input type="hidden" name="portrait_id" value="14">
<button type="submit" name="button" class="btn btn-primary">Submit Changes</button>
</form>
The action URL is correct, and is where I want the form to be submitted to. However, when the form is submitted I get a 404 with the following message:
Using the URLconf defined in ZachWebsite.urls, Django tried these URL patterns, in this order:
^admin/
^AdminDashboard/
^ ^SundayPortraits/ [name='sunday_portraits']
^ ^Contact/ [name='contact_submission']
^ ^SubmitOrder/ [name='submit_buy_order']
^ ^BuyPrint/ [name='buy_print']
^ ^$ [name='index']
^ ^Login [name='login']
^ ^Logout [name='logout']
^ ^media\/(?P<path>.*)$
The current URL, EditPortraits/ChangePortrait/, didn't match any of these.
Here is my base urls.py
urlpatterns = [
url(r'^AdminDashboard/', include('AdminDashboard.urls')),
url(r'^', include('mainApp.urls'))
]
And AdminDashboard.urls:
urlpatterns = [
url(r'^EditPortraits/ChangePortrait/', views.change_portrait, name='change_portrait'),
url(r'^EditPortraits/', views.edit_portraits, name='edit_portraits'),
url(r'^', views.dashboard, name='dashboard'),
]
For some reason unbeknownst to me submitting the form cuts off the initial '/AdminDashboard/' and only tests against the remaining url 'EditPortraits/ChangePortrait/'. This of course causes it to skip over the first url pattern in the base urls.py (where it should go) and instead match with the catch all second url and subsequently fail to match all the urls in mainApp.urls. I cannot for the life of me figure out why submitting the form cuts off the first section of the action URL. It works as intended on the built in django development server but fails when deployed to the production server.
Any help will be extremely appreciated.
EDIT
mainApp.urls (these are what are shown in the trace but should never be attempted in the first place):
urlpatterns = [
url(r'^SundayPortraits/', views.sunday_portraits, name='sunday_portraits'),
url(r'^Contact/', views.contact, name='contact_submission'),
url(r'^SubmitOrder/', views.submit_buy_order, name='submit_buy_order'),
url(r'^BuyPrint/', views.buy_print, name='buy_print'),
url(r'^$', views.index, name='index'),
url(r'^Login', views.login, name='login'),
url(r'^Logout', views.logout, name='logout')
]
views.change_portrait:
def change_portrait(request):
return HttpResponse('in')
This never shows which means it never gets to the view.
EDIT 2:
It should also be noted that on form submission the url in my browser is correct: i.e. it shows 'mysite.com/AdminDashboard/EditPortraits/ChangePortrait/'. Django for some reason says the current URL is '/EditPortraits/ChangePortrait/'. If I go to that url ('mysite.com/AdminDashboard/EditPortraits/ChangePortrait/') regularly in my browser it correctly loads the page. It's only when I attempt to POST to that page that it fails.
EDIT 3:
The view can be updated to correctly accept the POST variables as follows:
def change_portrait(request):
portrait_id = request.POST.get('portrait_id', None)
return HttpResponse('in')
However the error still persists.
EDIT 4:
view that renders template:
def edit_portraits(request):
if not request.session.get('logged_in', None):
return redirect('../')
portraits = Portrait.objects.filter(isSundayPortrait=True)
context = {'portraits': portraits,
'SundayPortraits': True}
return render(request, 'AdminDashboard/editportraits.html', context)
Moving url(r'^EditPortraits/ChangePortrait/', views.change_portrait, name='change_portrait') to mainApp.urls (where it goes to try to match) results in an updated mainApp.urls:
urlpatterns = [
url(r'^SundayPortraits/', views.sunday_portraits, name='sunday_portraits'),
url(r'^Contact/', views.contact, name='contact_submission'),
url(r'^SubmitOrder/', views.submit_buy_order, name='submit_buy_order'),
url(r'^BuyPrint/', views.buy_print, name='buy_print'),
url(r'^$', views.index, name='index'),
url(r'^EditPortraits/ChangePortrait/$', views.change_portrait, name='change_portrait'),
url(r'^Login', views.login, name='login'),
url(r'^Logout', views.logout, name='logout')
]
mainApp.views.change_portrait:
def change_portrait(request):
if request.method == 'POST':
return HttpResponse('POST')
else:
return HttpResponse('GETTING')
Using the same form as originally shown, doing a get request results in an HttpResponse 'GETTING' as expected. Changing the form to a POST request results in a new stack trace:
Request Method: POST
Request URL: http://www.example.com/EditPortraits/ChangePortrait/
Raised by: mainApp.views.change_portrait
Using the URLconf defined in ZachWebsite.urls, Django tried these URL patterns, in this order:
^admin/
^AdminDashboard/
^ ^SundayPortraits/ [name='sunday_portraits']
^ ^Contact/ [name='contact_submission']
^ ^SubmitOrder/ [name='submit_buy_order']
^ ^BuyPrint/ [name='buy_print']
^ ^$ [name='index']
^ ^EditPortraits/ChangePortrait/ [name='change_portrait']
^ ^Login [name='login']
^ ^Logout [name='logout']
^ ^media\/(?P<path>.*)$
The current URL, ChangePortrait/, didn't match any of these.
Moving to mainApp.urls (where it should catch it) now cuts off the first part of that url 'EditPortrait/' and now only tests against 'ChangePortrait/'

In your AdminDashboard.urls:
All your regular expressions missed the $
Note: your base urls.py doesn't need to have the $ at the end
url(r'^EditPortraits/ChangePortrait/',
views.change_portrait, name='change_portrait')
the good one is
url(r'^EditPortraits/ChangePortrait/$',
views.change_portrait, name='change_portrait')

After more investigation and testing, I seem to have figured out the problem. Turns out to be a problem with how Django is handling script_name and path_info. Because the form is posting to a sub url, the first part of the url '/AdminDashboard/' was being cut off and saved as the script_name. The remainder of the url, 'EditPortrait/ChangePortrait/' was being saved as the path_info. Django then attempted to match path_info against urls. Obviously it didn't match anything as it was missing the first part of the url that had been saved in script_name. Changing this line:
self.path_info = path_info
in django.core.handlers.wsgi.py to:
self.path_info = script_name + path_info
fixed the problem and is now working properly.

You say "It works as intended on the built-in Django development server but fails when deployed to the production server".
To me, this indicates a problem with the deployment on the production server that does not exist on the development server. For example, maybe on the production server, you are using a web server such as Apache or NGINX that you are not using on development.
I would check and double check any infrastructure such as web servers, load balancers, etc that gets the request before Django does, and make sure it isn't rewriting the path or otherwise passing incorrect configuration to Django.

Related

Django URL with parameters not working

I'm trying to use pass a parameter to a URL in Django, but I keep getting this error:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/%7B%25%20url%20review%20review_id%3D3%20%25%7D
Using the URLconf defined in soundclinic.urls, Django tried these URL patterns, in this order:
[name='index']
login/ [name='login']
register/ [name='register']
create_user [name='create_user']
<int:review_id>/review/ [name='review']
admin/
main/
The current path, {% url review review_id=3 %}, didn't match any of these.
The link I am follow has a tag like above: {% url review review_id=3 %}
I'm not sure what I'm not including right, as it looks like I'm writing out the url tag correctly and urls.py seems to be configured correcely as well.
Manually entering in the URL calls the correct views.py function, so it only has to do with my urls.py file.
As kszl already said, you must use " ' " around your URL name.
{% url 'review' review_id=3 %}
It supposes you have a URL in some url.py that has the name "review".
It seems to be the case though since we can see
<int:review_id>/review/ [name='review']
in your log message.

Django how to define default app for home URL?

I'm learning Django, and so far I always had to use URL's like
projectname/appname/viewname
but what if I don't want appname to appear in the URLs for the "default" app, how can I configure my urls so that
projectname/viewname
will load the view viewname from my default app?
P.S. : Of course my primary goal is to be able to use the URL projectname/ to load the default view for the default app.
Details
Currently my ProjectName/urls.py has this:
urlpatterns = patterns('',
url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root', settings.STATIC_ROOT}
),
url(r'^admin/', include(admin.site.urls)),
url(r'^myapp1/', include('myapp1.urls', namespace='myapp1', app_name='myapp1')),
url(r'^myapp2/', include('myapp2.urls', namespace='myapp2', app_name='myapp2')),
)
so when I deploy my project to Heroku, and visit myproject.heroku.com, I get the error :
Page not found (404)
Request Method: GET
Request URL: https://myproject.herokuapp.com/
Using the URLconf defined in MyProject.urls, Django tried these URL patterns, in this order:
^static/(?P<path>.*)$
^admin/
^myapp1/
^myapp2/
I know this is supposed to be, but how do I fix (or hack) this to get myproject.heroku.com to work?
If not possible, how can I redirect the homepage to myproject/myapp1/defaultview ?
Thanks in advance !
my app's urls.py looks like this :
urlpatterns = patterns('myapp1.views',
url(r'^view1/$', 'view1', name='view1'), # the default view
url(r'^view2/(?P<oid>\d+)/(?P<pid>\d+)/$', 'view2', name='view2'),
)
Edit
After trying #Wallace 's suggestion url(r'^$', include('myapp1.urls', namespace='myapp1', app_name='myapp1')), and hitting the homepage, I now get the error:
Using the URLconf defined in myproject.urls, Django tried these URL patterns, in this order:
^static/(?P<path>.*)$
^admin/
^$ ^view1/$ [name='view1']
^$ ^view2/(?P<oid>\d+)/(?P<pid>\d+)/$ [name='view2']
^myapp2/
Tried changing your project urls.py with:
url(r'', include('myapp1.urls', ...)
This will include all urls from myapp1.urls where they all append to /.
The reason why r'^$' won't work is because the regex ends with $ which means there can only be 1 x url /, and because your app1.urls only has 2 urls defined and without a / or ^$ equivalent, the url resolver will then fail.
But be aware of url clashes, if your project has a ^view1/$ url it will clash to your app1's view1 for example.
Try not including your appname in the regular expression.
url(r'', include('myapp1.urls', namespace='myapp1', app_name='myapp1')),

NoReverseMatch at /rest-auth/password/reset/

I have a django application with an angular front-end. When from the front-end I try to send a request for passwordReset, I get the following error:
Reverse for 'password_reset_confirm' with arguments '()' and keyword
arguments '{u'uidb64': 'MTE', u'token': u'3z4-eadc7ab3866d7d9436cb'}'
not found. 0 pattern(s) tried: []
Its a POST request going to http://127.0.0.1:8080/rest-auth/password/reset/
Following is what my urls.py looks like:
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
url(r'^account/', include('allauth.urls'))
)
I also was having this problem, and found this github issue it said we need to add
url(r'^', include('django.contrib.auth.urls')),
on the urlpatterns.
As stated there it says that The PasswordReset view depends on django.contrib.auth.views.password_reset_confirm view.
As Roar Skullestad pointed out, the problem is with the default email template, which tries to resolve URL by reversing viewname "password_reset_confirm", which is undefined.
It is enough to register a viewname "password_reset_confirm" with a custom route and then default email template rendering will work fine.
You can register viewname with custom route by adding a path to urls.py:
urlpatterns = [
...,
path('password-reset/<uidb64>/<token>/', empty_view, name='password_reset_confirm'),
]
password-reset - custom route that password reset confirmation view. If you have SPA (Angular) - it will be the URL of your SPA view (such as the route to Angular component) that will handle the password reset.
This is the URL that will be resolved and embedded in the email. For this example it will be something like:
http://my-spa.com/app-name/password-reset/Nw/51v-490d4b372ec930e49049/
empty_view - in case of SPA (Angular), you don't really need server-side implementation, because the front end will actually handle this route. I used this implementation of a view, but it can be anything else:
from django.http import HttpResponse
def empty_view(request):
return HttpResponse('')
And since I'm using Angular, this is the route for my Angular component:
{
path: 'password-reset/:uid/:token',
component: PasswordRecoveryComponent
}
For me the problem was this line in site-packages/django/contrib/admin/templates/registration/password_reset_email.html:
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
From what I understand the problem is caused by reverse lookup not working for this line in contrib/auth/urls.py:
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
'django.contrib.auth.views.password_reset_confirm',
name='password_reset_confirm'),
My (at least temporarily) solution was to override the template and hardcode the reverse lookup part of the url for the link in the email.
The path to the new template is specified in settings.py:
TEMPLATE_DIRS =(
"/absolute/path/to/my/templates/directory",
)
Since I am using angular front end, I also changed the link so that it triggers password reset confirmation via the angular client:
{{ protocol }}://{{ domain }}/#/passwordResetConfirm/{{ uid }}/{{ token }}
#AbimaelCarrasquillo's solutions works but you probably do not want to expose those endpoints as #dpstart mentioned in the comments.
I solved this by overriding the PasswordResetSerializer of rest-auth and simply replacing the reset form:
password_reset_form_class = PasswordResetForm
from the internal django.contrib.auth.forms.PasswordResetForm to allauth.account.forms.ResetPasswordForm
Make sure to add the following to your settings:
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER':'path.to.PasswordResetSerializer'
}
For those who are still struggling with this issue, I found that the reverse look up internal view looks for reverse lookup within the core project urls and not within any application. It could work within application with some tweak, but I am not sure. But it works creating the reset urls directly on core project urls.py
{
path(r'password_reset/', PasswordResetView.as_view(template_name='password_reset_form.html'), name='password_reset'),
path(r'password_reset_done/', PasswordResetDoneView.as_view(template_name='password_reset_done.html'), name='password_reset_done'),
path(r'password_reset_confirm/<uidb64>/<token>/', PasswordResetConfirmView.as_view(template_name='password_reset_confirm.html'), name='password_reset_confirm'),
path(r'password_reset_complete/', PasswordResetCompleteView.as_view(template_name='password_reset_complete.html'), name='password_reset_complete'),
}
Well I was also facing this problem. Every time I entered my email and pressed the button it took me to NoReverseMatch url, and I am using:
re_path(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
TemplateView.as_view(template_name="password_reset_confirm.html"),name='password_reset_confirm')
(url is not used to match regex in django anymore re_path is used)
My solution is to change the regex a little bit because the "csrf token" is longer than 20 words – that's why I was getting an error and you should try the same.
re_path(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$',
TemplateView.as_view(template_name="password_reset_confirm.html"),
name='password_reset_confirm')
You can either use + (in regex it means one or more) or {1,40} (in regex it means match from 1 to 40)
Add this to your project url.py file
url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
url('', include('social.apps.django_app.urls', namespace='social')),
Check out the FAQ: It explains this error and how to fix it. It references you to the demo program which contains:
# this url is used to generate email content
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
TemplateView.as_view(template_name="password_reset_confirm.html"),
name='password_reset_confirm'),
My solution was to override the email template that calls the reverse of "password_reset_confirm". Make sure email template sends a URL to your frontend app with the UID and Token in the URL (instead of trying to reverse "password_reset_confirm").
Your frontend's route should take the URL, parse it and then with the updated user's password and send it back as an API call to your backend to confirm.
I resolved this issue by moving these:
path('reset_password/', auth_views.PasswordResetView.as_view(), name='password_reset'),
path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset_password_sent/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset_password_complete/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
from accounts/urls.py to yourProjectName/urls.py.
I guess that path('', include("accounts.urls")), was causing the problem.
I have a headless backend, so adding or renaming URLs only for this was not a good option.
The issue is on the email template, you can override it. Just take care that you have the correct path on the settings.
In my case I have all this logic inside users app. So I have something like this.
users/templates/registration/password_reset_email.html
Inside this template I have a new custom message without the reverse URL call.
If you need more than just override the template, or maybe you need to send additional data to the template. You must override the serializer also. To do that you have to create your new serializer
from dj_rest_auth.serializers import PasswordResetSerializer as RestPasswordResetSerializer
class PasswordResetSerializer(RestPasswordResetSerializer):
def get_email_options(self):
return {
'html_email_template_name': 'registration/password_reset_email_html.html', # if you want to use an HTML template you can declare here
'extra_email_context': {'custom_key': 'custom value for my template',}
}
and add to settings.
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER':'path.to.PasswordResetSerializer'
}
On the serializer you can add custom validation if needed also.
In your views.py, if you have set a token, pass it along with the path similar to:
path('resetpassword_validate/<uidb64>/<token>/', views.resetpassword_validate, name='resetpassword_validate'),

Django admin site with url parameters

I have a Django project with several applications, and I want to add the Django admin site for one of these.
The problem I have is that the main urls.py file has
url(r'^tools/(\w+)/', include('tools.myapp.urls')),
and in my myapp.urls I have added
url(r'^admin/', include(admin.site.urls)),
The problem is that the parent part of the url matching uses a parameter, which is usually passed in the template (that's the application name) like so
{% url "my_view_function" request.info.appname %}
But the default Django template obviously don't include that extra parameter, when calling
{% url 'admin:logout' %}
thus leading to a NoReverseMatch exception.
How can I have the admin site working?
If you just want to have admin run under any tools/whatever/admin URL, you can add this line before the tools.myapp.urls import in your main urls.py:
url(r'^tools/\w+/admin/', include(admin.site.urls)),

Cannot Get Haystack Tutorial Working

I have completed the tutorial for Haystack w/ Whoosh and re-read it about 6 times in the past few hours and cannot seem to figure out what my issue is. Basically, everything seemed to installed correctly, however when I visit the /search URL all I see is my base template without the search template (no input box/header/etc). This is my urls.py at the application level:
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^search/', include('haystack.urls')),
url(r'a/list/$', views.admin_list, name='admin_list'),
url(r'a/list/(?P<list_id>\d+)/edit/$', views.admin_edit, name='admin_edit'),
)
The template lives in myapp/templates/search/search.html. I have added nothing to my views.py, because the tutorial didn't go over adding anything to that file. What am I missing here?
What is content of the data template file ( appname_text) file , if you are keeping document =true in search_index.py file your data template should have the fields that are to be searched .
data template for will be something like.
{{ object.foo }}
{{ object.bar }}

Categories

Resources