Using {% url ??? %} in django templates - django

I have looked a lot on google for answers of how to use the 'url' tag in templates only to find many responses saying 'You just insert it into your template and point it at the view you want the url for'. Well no joy for me :( I have tried every permutation possible and have resorted to posting here as a last resort.
So here it is. My urls.py looks like this:
from django.conf.urls.defaults import *
from login.views import *
from mainapp.views import *
import settings
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^weclaim/', include('weclaim.foo.urls')),
(r'^login/', login_view),
(r'^logout/', logout_view),
('^$', main_view),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
#(r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root': '/home/arthur/Software/django/weclaim/templates/static'}),
(r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT}),
)
My 'views.py' in my 'login' directory looks like:
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.contrib import auth
def login_view(request):
if request.method == 'POST':
uname = request.POST.get('username', '')
psword = request.POST.get('password', '')
user = auth.authenticate(username=uname, password=psword)
# if the user logs in and is active
if user is not None and user.is_active:
auth.login(request, user)
return render_to_response('main/main.html', {}, context_instance=RequestContext(request))
#return redirect(main_view)
else:
return render_to_response('loginpage.html', {'box_width': '402', 'login_failed': '1',}, context_instance=RequestContext(request))
else:
return render_to_response('loginpage.html', {'box_width': '400',}, context_instance=RequestContext(request))
def logout_view(request):
auth.logout(request)
return render_to_response('loginpage.html', {'box_width': '402', 'logged_out': '1',}, context_instance=RequestContext(request))
and finally the main.html to which the login_view points looks like:
<html>
<body>
test! logout
</body>
</html>
So why do I get 'NoReverseMatch' every time?
*(on a slightly different note I had to use 'context_instance=RequestContext(request)' at the end of all my render-to-response's because otherwise it would not recognise {{ MEDIA_URL }} in my templates and I couldn't reference any css or js files. I'm not to sure why this is. Doesn't seem right to me)*

The selected answer is out of date and no others worked for me (Django 1.6 and [apparantly] no registered namespace.)
For Django 1.5 and later (from the docs)
Warning
Don’t forget to put quotes around the function path or pattern name!
With a named URL you could do:
(r'^login/', login_view, name='login'),
...
logout
Just as easy if the view takes another parameter
def login(request, extra_param):
...
login

Instead of importing the logout_view function, you should provide a string in your urls.py file:
So not (r'^login/', login_view),
but (r'^login/', 'login.views.login_view'),
That is the standard way of doing things. Then you can access the URL in your templates using:
{% url login.views.login_view %}

Make sure (django 1.5 and beyond) that you put the url name in quotes, and if your url takes parameters they should be outside of the quotes (I spent hours figuring out this mistake!).
{% url 'namespace:view_name' arg1=value1 arg2=value2 as the_url %}
link_name

The url template tag will pass the parameter as a string and not as a function reference to reverse(). The simplest way to get this working is adding a name to the view:
url(r'^/logout/' , logout_view, name='logout_view')

I run into same problem.
What I found from documentation, we should use namedspace.
in your case {% url login:login_view %}

Judging from your example, shouldn't it be {% url myproject.login.views.login_view %} and end of story? (replace myproject with your actual project name)

Related

Django redirect from form upload

From the page
This page isn’t working. If the problem continues, contact the site owner.
HTTP ERROR 405
From the terminal
Method Not Allowed (POST): /
Method Not Allowed: /
[20/Dec/2021 22:00:27] "POST / HTTP/1.1" 405 0
How to redirect to the same page after page upload click.
form.html->included in sidebar.html-> included in home.html
<form method = "POST" action='.' enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
views.py
from django.shortcuts import render
from .forms import UserProfileForm
def index(request):
print(request.POST)
return render(request,'home.html')
urls.py
from django.conf import settings
from django.urls import path
from django.views.generic.base import TemplateView # new
urlpatterns = [
path('', TemplateView.as_view(template_name='home.html'), name='home'),
]
In your urls.py
Change to:
path(' ', index, name = 'home'),
And you also have to import your view in urls.py
Since you are redirecting to the same page, I assume you are also making a get request when you are serving the form on the webpage.
But when the page is served as a response to a GET request, it is not supposed to contain an empty dictionary in the POST attribute.
Thus, it provides an error.
According to me
def index(request):
if request.method == "POST" :
print(request.POST)
return render(request,'home.html')
Should solve the issue
Acc, to the Django documentation
It’s possible that a request can come in via POST with an empty POST dictionary – if, say, a form is requested via the POST HTTP method but does not include form data. Therefore, you shouldn’t use if request.POST to check for use of the POST method; instead, use if request.method == "POST"
For further reference - https://docs.djangoproject.com/en/3.2/ref/request-response/

Does using Django "is_superuser" require additional URL?

I'm creating an administrative interface/dashboard for a Django 1.7 project. Instead of creating a new admin application from scratch, I'm trying to use Django's built-in admin site. I have "root" (superuser) and "reviewer" (non-superuser) staff accounts. Both accounts are in the default "admin" group. Both accounts can log in to the admin site but some pages are accessible by both the root and reviewer accounts while access to others is limited to the superuser. I've created the two following views:
# mysite/apps/admin/views.py
from django.shortcuts import render
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: u.is_staff)
def all_admins(request, template):
return render(request, template)
#user_passes_test(lambda u: u.is_superuser)
def superuser_only(request, template):
return render(request, template)
The problem I'm running into is that if I log is using the reviewer account and click the anchor for the "superuser_only" page, I get the following error:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8001/accounts/login/?next=/admin/super/
Using the URLconf defined in conf.urls, Django tried these URL patterns, in this order:
^admin/
^admin/all_admins/$ [name='all-admins']
^admin/super/$ [name='superuser-only']
The current URL, accounts/login/, didn't match any of these.
Is there something else I need to do to implement this superuser-only view and template so that this error doesn't occur if the reviewer clicks the link?
I followed the Django docs instructions and created my own copies of Django's base.html, base_site.html, and index.html templates in my mysite/apps/admin/templates/admin directory and then added the "all_admins" and "superuser_only" anchors to the bottom of the index.html page.
Here are my files:
# part of mysite/mysite/settings.py
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, 'apps/admin/templates'),
os.path.join(BASE_DIR, 'apps/admin/templates/admin'),)
# mysite/mysite/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/all_admins/$',
'apps.admin.views.all_admins',
{'template': 'all_admins.html'},
name='all-admins'),
url(r'^admin/super/$',
'apps.admin.views.superuser_only',
{'template': 'superuser_only.html'},
name='superuser-only'),
)
Then user doesn't pass the test django redirects it to the login page. Defult url for this page is /accounts/login/. Set the LOGIN_URL setting to the admin's login page:
LOGIN_URL = 'admin:login'
Or, if you have non-staff users on the site, set the auth urls in the urls.py:
url('^accounts/', include('django.contrib.auth.urls')),
But this will require you to create several templates (login, logout, change password etc.)
UPDATE: If you want to show a message in the login page then you can create the custom login template for admin. Add this line to your urls.py:
admin.site.login_template = 'my_login.html'
And then create my_login.html template which is extended from admin/login.html:
{% extends 'admin/login.html' %}
{% block content %}
{% if user.is_staff %}
<p class="errornote">You need to be a superuser to access that page.</p>
{% endif %}
{{ block.super }}
{% endblock %}
UPDATE 2: If you don't want to change site-wide LOGIN_URL or implement auth by including django.contrib.auth.urls in your urls.py then you can pass login_url parameter to the #user_passes_test decorator:
#user_passes_test(lambda u: u.is_superuser, login_url='/admin/login/')
def superuser_only(request, template):
return render(request, template)

How can I not use Django's admin login view?

I created my own view for login. However if a user goes directly to /admin it brings them to the admin login page and doesn't use my custom view. How can I make it redirect to the login view used for everything not /admin?
From http://djangosnippets.org/snippets/2127/—wrap the admin login page with login_required. For example, in urls.py:
from django.contrib.auth.decorators import login_required
from django.contrib import admin
admin.autodiscover()
admin.site.login = login_required(admin.site.login)
You probably already have the middle two lines and maybe even the first line; adding that fourth line will cause anything that would have hit the admin.site.login function to redirect to your LOGIN_URL with the appropriate next parameter.
While #Isaac's solution should reject majority of malicious bots, it doesn't provide protection for professional penetrating. As a logged in user gets the following message when trying to login to admin:
We should instead use the admin decorator to reject all non-privileged users:
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import admin
[ ... ]
admin.site.login = staff_member_required(admin.site.login, login_url=settings.LOGIN_URL)
To the best of my knowledge, the decorator was added in 1.9.
I found that the answer above does not respect the "next" query parameter correctly.
An easy way to solve this problem is to use a simple redirect. In your site's urls file, immediately before including the admin urls, put a line like this:
url(r'^admin/login$', RedirectView.as_view(pattern_name='my_login_page', permanent=True, query_string=True))
Holá
I found a very simple solution.
Just tell django that the url for admin login is handle by your own login view
You just need to modify the urls.py fle of the project (note, not the application one)
In your PROJECT folder locate the file urls.py.
Add this line to the imports section
from your_app_name import views
Locate this line
url(r'^admin/', include(admin.site.urls))
Add above that line the following
url(r'^admin/login/', views.your_login_view),
This is an example
from django.conf.urls import include, url
from django.contrib import admin
from your_app import views
urlpatterns = [
url(r'^your_app_start/', include('your_app.urls',namespace="your_app_name")),
url(r'^admin/login/', views.your_app_login),
url(r'^admin/', include(admin.site.urls)),
]
http://blog.montylounge.com/2009/07/5/customizing-django-admin-branding/
(web archive)
I'm trying to solve exactly this problem and I found the solution at this guys blog. Basically, override the admin template and use your own template. In short, just make a file called login.html in /path-to-project/templates/admin/ and it will replace the admin login page. You can copy the original (django/contrib/admin/templates/login.html) and modify a line or two. If you want to scrap the default login page entirely you can do something like this:
{% extends "my-login-page.html" %}
There it is. One line in one file. Django is amazing.
I had the same issue, tried to use the accepted answer, but has the same issue as pointed in the comment above.
Then I've did something bit different, pasting here if this would be helpful to someone.
def staff_or_404(u):
if u.is_active:
if u.is_staff:
return True
raise Http404()
return False
admin.site.login = user_passes_test(
staff_or_404,
)(admin.site.login)
The idea is that if the user is login, and tried to access the admin, then he gets 404. Otherwise, it will force you to the normal login page (unless you are already logged in)
In your ROOT_URLCONF file (by default, it's urls.py in the project's root folder), is there a line like this:
urlpatterns = patterns('',
...
(r'^admin/', include(admin.site.urls)),
...
)
If so, you'd want to replace include(admin.site.urls) with the custom view you created:
(r'^admin/', 'myapp.views.myloginview'),
or if your app has its own urls.py, you could include it like this:
(r'^admin/', include(myapp.urls)),
This is my solution with custom AdminSite class:
class AdminSite(admin.AdminSite):
def _is_login_redirect(self, response):
if isinstance(response, HttpResponseRedirect):
login_url = reverse('admin:login', current_app=self.name)
response_url = urllib.parse.urlparse(response.url).path
return login_url == response_url
else:
return False
def admin_view(self, view, cacheable=False):
inner = super().admin_view(view, cacheable)
def wrapper(request, *args, **kwargs):
response = inner(request, *args, **kwargs)
if self._is_login_redirect(response):
if request.user.is_authenticated():
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
else:
return redirect_to_login(request.get_full_path(), reverse('accounts_login'))
else:
return response
return wrapper
You can redirect admin login url to the auth login view :
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('your_app.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('admin/login/', RedirectView.as_view(url='/accounts/login/?next=/admin/', permanent=True)),
path('admin/', admin.site.urls),
]
As of August 2020, django.contrib.admin.sites.AdminSite has a login_template attribute. So you can just subclass AdminSite and specify a custom template i.e.,
class MyAdminSite(AdminSite):
login_template = 'my_login_template.html'
my_admin_site = MyAdminSite()
Then just use my_admin_site everywhere instead of admin.site.

django {% tag %} problem

I don't know if its me but {% tag ??? %} has bee behaving a bit sporadically round me (django ver 1.2.3). I have the following main.html file:
<html>
{% include 'main/main_css.html' %}
<body>
test! logout
test! logout
</body>
</html>
with the urls.py being:
from django.conf.urls.defaults import *
import settings
from login.views import *
from mainapp.views import *
from client.views import *
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^weclaim/', include('weclaim.foo.urls')),
(r'^login/$', 'login.views.login_view'),
(r'^logout/$', 'login.views.logout_view'),
(r'^$', 'mainapp.views.main_view'),
(r'^client/search/last_name/(A-Za-z)/$', 'client.views.client_search_last_name_view'),
#(r'^client/search/post_code/(A-Za-z)/$', 'client.views.client_search_last_name_view'),
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
(r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT}),
)
and the views.py for login being:
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.contrib import auth
import mainapp.views
def login_view(request):
if request.method == 'POST':
uname = request.POST.get('username', '')
psword = request.POST.get('password', '')
user = auth.authenticate(username=uname, password=psword)
# if the user logs in and is active
if user is not None and user.is_active:
auth.login(request, user)
return redirect(mainapp.views.main_view)
else:
return render_to_response('loginpage.html', {'login_failed': '1',}, context_instance=RequestContext(request))
else:
return render_to_response('loginpage.html', {'dave': '1',}, context_instance=RequestContext(request))
def logout_view(request):
auth.logout(request)
return render_to_response('loginpage.html', {'logged_out': '1',}, context_instance=RequestContext(request))
and the views.py for clients being:
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
import login.views
def client_search_last_name_view(request):
if request.user.is_authenticated():
return render_to_response('client/client_search_last_name.html', {}, context_instance=RequestContext(request))
else:
return redirect(login.views.login_view)
Yet when I login it django raises an 'NoReverseMatch' for {% url client.views.client_search_last_name_view %} but not for {% url login.views.logout_view %}
Now why would this be?
The "client.views.client_search_last_name_view" url's regex capatures a value (with the parens), so in order to {% url %} it, you need to pass it a value for that parameter.
If you are creating a URL which is supposed to accept a last name the correct way would be as follows:
(r'^client/search/last_name/(?P<last_name>[a-zA-Z]+)/$',
'client.views.client_search_last_name_view'),
The (?P<last_name>[a-zA-Z]+) part of the regex allows you to capture the last name which is at least one character in length and then have it passed as an argument to your view function.
However you then need to ensure that your view does accept this argument:
def client_search_last_name_view(request, last_name):
...
The reason you cannot then do:
{% url client.views.client_search_last_name_view %}
is because your regex states (like the view) that it needs one argument, which is a string consisting of lower or upper cases letters from A to Z. So for example this would work:
{% url client.views.client_search_last_name_view 'somelastname' %}
If you want to give your URL a name as another answer has suggested you can, but that is a separate matter and has no effect other than shortening that template tag.
The reason {% url login.views.logout_view %} does work is because its entry in urls.py does not specify any arguments to be passed to the view and, through default alone you have not passed any.
AFAIK you want to add a name='foo' arg to each of your url regexes. That name is what is used in the reverse match. Like this:
urls.py
(r'^login/$', 'login.views.login_view', name="login"),
template.html
{% url login %}

Django reset_password_confirm TemplateSyntaxError problem

when I use django.contrib.auth.views.password_reset_confirm without arguments at all it works and I can render the template without any problem, when adding uidb36 and token arguments it fails.
Caught NoReverseMatch while rendering: Reverse for 'django.contrib.auth.views.password_reset_confirm' with arguments '()' and keyword arguments '{'uidb36': '111', 'token': '1111111111111'}' not found.
Most likely it is an issue with your urls.py. You need to setup the right pattern to grab the uidb36 and token values passed as URL parameters. If not, it will throw a similar error to what you see above.
Something like:
(r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', {'template_name' : 'registration/password_reset.html', 'post_reset_redirect': '/logout/' })
registration/password_reset.html - is my custom template
logout - is my custom logout action
I had this issue in Django 1.3, and wasted a lot of time because the error can mask a number of underlying issues.
I needed to add this to the top of the reset email template:
{% load url from future %}
Also, the example in the Django docs didn't match the sample url:
{{ protocol}}://{{ domain }}{% url 'auth_password_reset_confirm' uidb36=uid token=token %}
So I had to change the auth_password_reset_confirm above to password_reset_confirm.
If you're using Django 1.6+ and run into something like this it could be that you need to update uidb36 to uidb64 in both your template and your urls.
Example url:
url(r'^password/reset/confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
auth_views.password_reset_confirm
and reset link in template:
{{ protocol}}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
For Django 1.8+ users, just copy this URL to your main urls.py file, so that it recognizes the URL name
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'),
And add this mentioned by: #Lunulata to your password_reset_email.html file:
{{ protocol}}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
Try adding following to your urls.py
(r'^reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 'django.contrib.auth.views.password_reset_confirm'),
I found this to work, copied from the default url
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
auth_views.password_reset_confirm, name='password_reset_confirm'),
Just add this line to your urls.py:
url('^', include('django.contrib.auth.urls')),
This enables the django reset_password workflow.
Then override your login.html to include the line:
<div class="password-reset-link">
href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a></div>
Now you should be able to use the builtin Django PasswordResetView included with Django as long as your email settings are set up.
if you are using app_name in every urls.py
suppose you have an app and in that app in urls.py you have mentioned app_name="accounts"
in order to retrieve the page you need to mention two things
template_name and success_url inside the PasswordResetView(template_name="accounts/password_reset.html" , success_url= reverse_lazy('accounts:password_reset_sent'))
dont forget to import reverse_lazy from django.urls inside urls.py
so your final code of accounts/urls.py should look like
My app name is landing instead of accounts
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views
from django.urls import reverse_lazy
app_name='landing'
urlpatterns = [
path('',views.home,name="home"),
path('terms/',views.terms,name="terms"),
path('login/',views.loginUser,name="login"),
path('signup/',views.signupUser,name="signup"),
path('about/',views.about,name="about"),
path('logout/',views.logoutUser,name="logout"),
path('password_reset/',
auth_views.PasswordResetView.as_view(template_name='landing/password_reset.html',success_url=reverse_lazy('landing:password_reset_done')),
name="password_reset"),
path('password_reset_sent/',
auth_views.PasswordResetDoneView.as_view(template_name='landing/password_reset_sent.html'),
name="password_reset_done"),
path('reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(template_name='landing/password_reset_form.html',success_url=reverse_lazy('landing:password_reset_complete')),
name="password_reset_confirm"),
path('password_reset_complete/',
auth_views.PasswordResetCompleteView.as_view(template_name='landing/password_reset_done.html'),
name="password_reset_complete"),
]
you have to use app_name: before the name of url you have mentioned it is very important