Django sitemap: 'module' object has no attribute 'values' - django

I follow the description on the http://docs.djangoproject.com/en/1.2/ref/contrib/sitemaps/
I from django.contrib import sitemaps add this line
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
to URLconf
make file sitemap.py with:
from django.contrib.sitemaps import Sitemap
from blog.models import Post
class BlogSitemap(Sitemap):
changefreq = 'monthly'
priority = 0.5
def items(self):
return Post.objects.all()
def lastmod(self, obj):
return obj.date
at this address http://127.0.0.1:8000/sitemap.xml I get an error:
Traceback:
File "/usr/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
100. response = callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python2.7/site-packages/django/contrib/sitemaps/views.py" in sitemap
33. maps = sitemaps.values()
Exception Type: AttributeError at /sitemap.xml
Exception Value: 'module' object has no attribute 'values'
Anyone can help me?

You missed a step - look at the example in the documentation.
Instead of importing the sitemaps module in your urls.py, import your BlogSitemap class, then create a sitemaps dictionary:
sitemaps = {'blog': BlogSitemap}

I encountered this issue as well. Between the documentation and the code samples I was looking at, I still couldn't understand why I was seeing this error.
The misread documentation on my part was from https://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/:
It may also map to an instance of a Sitemap class (e.g., BlogSitemap(some_var)).
I then looked at the Django source a bit closer. The view is as follows (django.contrib.sitemaps.views.sitemap):
def sitemap(request, sitemaps, section=None, template_name='sitemap.xml'):
maps, urls = [], []
if section is not None:
if section not in sitemaps:
raise Http404("No sitemap available for section: %r" % section)
maps.append(sitemaps[section])
else:
maps = sitemaps.values() # This is where I was seeing the error.
page = request.GET.get("p", 1)
current_site = get_current_site(request)
for site in maps:
try:
if callable(site):
urls.extend(site().get_urls(page=page, site=current_site))
else:
urls.extend(site.get_urls(page=page, site=current_site))
except EmptyPage:
raise Http404("Page %s empty" % page)
except PageNotAnInteger:
raise Http404("No page '%s'" % page)
xml = smart_str(loader.render_to_string(template_name, {'urlset': urls}))
return HttpResponse(xml, mimetype='application/xml')
It then dawned on me that the parameter sitemaps was actually a dictionary of key to sitemap objects, not a sitemap object itself. It's possible that this should have been obvious to me, but it took a bit for me to overcome my mental block.
The full coding sample that I used looks like the following:
sitemap.py file:
from django.contrib.sitemaps import Sitemap
from articles.models import Article
class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Article.objects.filter(is_active=True)
def lastmod(self, obj):
return obj.publish_date
urls.py file:
from sitemap import BlogSitemap
# a dictionary of sitemaps
sitemaps = {
'blog': BlogSitemap,
}
urlpatterns += patterns ('',
#...<snip out other url patterns>...
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
)

Related

Simple Tests in Django failed

I'm trying to do simple tests in Django (v. 2.0.5). Since I can not see why I'm getting the '404 != 200' error, I post all relevant data.
test.py
from django.urls import resolve, reverse
from django.test import TestCase
from .views import home, board_topics
from .models import Board
class HomeTests(TestCase):
def test_home_view_status_code(self):
url = reverse('home')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_home_url_resolves_home_view(self):
view = resolve('/home/')
self.assertEqual(view.func, home)
class BoardTopicsTests(TestCase):
def setUp(self):
Board.objects.create(name='Django', description='Django discussion board')
def test_board_topics_view_success_status_code(self):
url = reverse('board_topics', kwargs={'pk': 1})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_board_topics_view_not_found_status_code(self):
url = reverse('board_topics', kwargs={'pk': 99})
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_board_topics_url_resolves_board_topics_view(self):
view = resolve('/boards/1/')
self.assertEqual(view.func, board_topics)
urls.py
from django.contrib import admin
from django.urls import include, path
from boards import views
urlpatterns = [
path('boards/<int:pk>/', views.board_topics, name='board_topics'),
path('home/', views.home, name='home'),
path('admin/', admin.site.urls),
]
views.py
...
def board_topics(request, pk):
try:
board = Board.objects.get(pk=pk)
except Board.DoesNotExist:
raise Http404
return render(request, 'topics.html', {'board': board})
Traceback
FAIL: test_board_topics_view_success_status_code (boards.tests.BoardTopicsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/.../boards/tests.py", line 25, in test_board_topics_view_success_status_code
self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200
I wonder why I'm getting this error because I can call the views and I also get a 404 error when I try to call a page that does not exist (except Board.DoesNotExist). Is there a way to make the tests different (easier)? Thanks in advance for help.
You could try to adjust your test to automatically pickup the ID of the created object.
class BoardTopicsTests(TestCase):
def setUp(self):
self.board = Board.objects.create(name='Django', description='Django discussion board')
def test_board_topics_view_success_status_code(self):
url = reverse('board_topics', kwargs={'pk': self.board.id})
Maybe the database is not properly cleared between the tests?
Use resolve('/') instead of resolve('/home/')
def test_home_url_resolves_home_view(self):
view = resolve('/')
self.assertEquals(view.func, home)

django name render is not defined

I have an error when I try to import render in my program and I fear it is because it is not supported in django 1.9.
I am using python 3.4 and django 1.9.
When I try to run my server, I have the error:
ImportError: cannot import name render`.
Here is my code:
blog/views:
from datetime import datetime
from django.shortcuts import render
def date_actuelle(request):
return render(request, 'blog/date.html', {'date': datetime.now()})
def addition(request, nombre1, nombre2):
total = int(nombre1) + int(nombre2)
# Retourne nombre1, nombre2 et la somme des deux au tpl
return render(request, 'blog/addition.html', locals())
blog/urls:
from django.conf.urls import url, patterns
from . import views
urlpatterns = [
# url(r'^accueil/$', views.home),
# url(r'^article/(?P<id_article>\d+)$', views.view_article),
# url(r'^article/(?P<year>\d{4})/(?P<month>\d{2})$', views.list_articles),
# url(r'^redirection$', views.view_redirection),
url(r'^date/$', views.date_actuelle),
url(r'^addition/(?P<nombre1>\d+)/(?P<nombre2>\d+)/$', views.addition),
]
creps_bretonnes.urls:
from django.conf.urls import patterns, include, url
from django.contrib import admin
from blog import views
urlpatterns = [
url(r'^blog/', include('blog.urls')),
]
I have also tried from django.shortcuts import *, then the server is launched but when I try to access the page it says NameError: name 'render' is not defined.
Would you have an idea?
What is written on the server when I run it:
cmd values when trying to run the server with from django.shortcuts import render
traceback when using from django.shortcuts import * and trying to access localhost:8000/blog/date
Environment:
Request Method: GET
Request URL: http://localhost:8000/blog/date/
Django Version: 1.9.7
Python Version: 3.4.4
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in get_response
149. response = self.process_exception_by_middleware(e, request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in get_response
147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\sperney\Documents\Travail\creps_bretonnes\blog\views.py" in date_actuelle
37. return render(request, 'blog/date.html', {'date': datetime.now()})
Exception Type: NameError at /blog/date/
Exception Value: name 'render' is not defined
here it is:
"""
This module collects helper functions and classes that "span" multiple levels
of MVC. In other words, these functions/classes introduce controlled coupling
for convenience's sake.
"""
from django.template import loader
from django.http import HttpResponse, Http404
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from django.core import urlresolvers
def render_to_response(*args, **kwargs):
"""
Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
def redirect(to, *args, **kwargs):
"""
Returns an HttpResponseRedirect to the apropriate URL for the arguments
passed.
The arguments could be:
* A model: the model's `get_absolute_url()` function will be called.
* A view name, possibly with arguments: `urlresolvers.reverse()` will
be used to reverse-resolve the name.
* A URL, which will be used as-is for the redirect location.
By default issues a temporary redirect; pass permanent=True to issue a
permanent redirect
"""
if kwargs.pop('permanent', False):
redirect_class = HttpResponsePermanentRedirect
else:
redirect_class = HttpResponseRedirect
# If it's a model, use get_absolute_url()
if hasattr(to, 'get_absolute_url'):
return redirect_class(to.get_absolute_url())
# Next try a reverse URL resolution.
try:
return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs))
except urlresolvers.NoReverseMatch:
# If this is a callable, re-raise.
if callable(to):
raise
# If this doesn't "feel" like a URL, re-raise.
if '/' not in to and '.' not in to:
raise
# Finally, fall back and assume it's a URL
return redirect_class(to)
def _get_queryset(klass):
"""
Returns a QuerySet from a Model, Manager, or QuerySet. Created to make
get_object_or_404 and get_list_or_404 more DRY.
"""
if isinstance(klass, QuerySet):
return klass
elif isinstance(klass, Manager):
manager = klass
else:
manager = klass._default_manager
return manager.all()
def get_object_or_404(klass, *args, **kwargs):
"""
Uses get() to return an object, or raises a Http404 exception if the object
does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Note: Like with get(), an MultipleObjectsReturned will be raised if more than one
object is found.
"""
queryset = _get_queryset(klass)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
def get_list_or_404(klass, *args, **kwargs):
"""
Uses filter() to return a list of objects, or raise a Http404 exception if
the list is empty.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the filter() query.
"""
queryset = _get_queryset(klass)
obj_list = list(queryset.filter(*args, **kwargs))
if not obj_list:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
return obj_list
Thanks a lot
Finaly I have deployed a virtual env (ubuntu) using virtual box.
I had absolutely no issues on the ubuntu env to deploy it and follow the steps with the exact same code as above!
Thanks for your help anyway!

How to test 500.html error page in django development env?

I am using Django for a project and is already in production.
In the production environment 500.html is rendered whenever a server error occurs.
How do I test the rendering of 500.html in dev environment? Or how do I render 500.html in dev, if I turn-off debug I still get the errors and not 500.html
background: I include some page elements based on a page and some are missing when 500.html is called and want to debug it in dev environment.
I prefer not to turn DEBUG off. Instead I put the following snippet in the urls.py:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'your_custom_view_if_you_wrote_one'),
(r'^404/$', 'django.views.generic.simple.direct_to_template', {'template': '404.html'}),
)
In the snippet above, the error page uses a custom view, you can easily replace it with Django's direct_to_template view though.
Now you can test 500 and 404 pages by calling their urls: http://example.com/500 and http://example.com/404
In Django 1.6 django.views.generic.simple.direct_to_template does not exists anymore, these are my settings for special views:
# urls.py
from django.views.generic import TemplateView
from django.views.defaults import page_not_found, server_error
urlpatterns += [
url(r'^400/$', TemplateView.as_view(template_name='400.html')),
url(r'^403/$', TemplateView.as_view(template_name='403.html')),
url(r'^404/$', page_not_found),
url(r'^500/$', server_error),
]
And if you want to use the default Django 500 view instead of your custom view:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'django.views.defaults.server_error'),
(r'^404/$', 'django.views.generic.simple.direct_to_template', {'template': '404.html'}),
)
Continuing shanyu's answer, in Django 1.3+ use:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'django.views.defaults.server_error'),
(r'^404/$', 'django.views.defaults.page_not_found'),
)
For Django > 3.0, just set the raise_request_exception value to False.
from django.test import TestCase
class ViewTestClass(TestCase):
def test_error_page(self):
self.client.raise_request_exception = False
response = self.client.get(reverse('error-page'))
self.assertEqual(response.status_code, 500)
self.assertTrue(
'some text from the custom 500 page'
in response.content.decode('utf8'))
Documentation: https://docs.djangoproject.com/en/3.2/topics/testing/tools/
NOTE: if the error page raises an exception, that will show up as an ERROR in the test log. You can turn the test logging up to CRITICAL by default to suppress that error.
Are both debug settings false?
settings.DEBUG = False
settings.TEMPLATE_DEBUG = False
How i do and test custom error handlers
Define custom View based on TemplateView
# views.py
from django.views.generic import TemplateView
class ErrorHandler(TemplateView):
""" Render error template """
error_code = 404
template_name = 'index/error.html'
def dispatch(self, request, *args, **kwargs):
""" For error on any methods return just GET """
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['error_code'] = self.error_code
return context
def render_to_response(self, context, **response_kwargs):
""" Return correct status code """
response_kwargs = response_kwargs or {}
response_kwargs.update(status=self.error_code)
return super().render_to_response(context, **response_kwargs)
Tell django to use custom error handlers
# urls.py
from index.views import ErrorHandler
# error handing handlers - fly binding
for code in (400, 403, 404, 500):
vars()['handler{}'.format(code)] = ErrorHandler.as_view(error_code=code)
Testcase for custom error handlers
# tests.py
from unittest import mock
from django.test import TestCase
from django.core.exceptions import SuspiciousOperation, PermissionDenied
from django.http import Http404
from index import views
class ErrorHandlersTestCase(TestCase):
""" Check is correct error handlers work """
def raise_(exception):
def wrapped(*args, **kwargs):
raise exception('Test exception')
return wrapped
def test_index_page(self):
""" Should check is 200 on index page """
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'index/index.html')
#mock.patch('index.views.IndexView.get', raise_(Http404))
def test_404_page(self):
""" Should check is 404 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 404)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('404 Page not found', response.content.decode('utf-8'))
#mock.patch('index.views.IndexView.get', views.ErrorHandler.as_view(error_code=500))
def test_500_page(self):
""" Should check is 500 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 500)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('500 Server Error', response.content.decode('utf-8'))
#mock.patch('index.views.IndexView.get', raise_(SuspiciousOperation))
def test_400_page(self):
""" Should check is 400 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 400)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('400 Bad request', response.content.decode('utf-8'))
#mock.patch('index.views.IndexView.get', raise_(PermissionDenied))
def test_403_page(self):
""" Should check is 403 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 403)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('403 Permission Denied', response.content.decode('utf-8'))
urls.py
handler500 = 'project.apps.core.views.handler500'
handler404 = 'project.apps.core.views.handler404'
views.py
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponseServerError, HttpResponseNotFound
def handler500(request, template_name='500.html'):
t = get_template(template_name)
ctx = Context({})
return HttpResponseServerError(t.render(ctx))
def handler404(request, template_name='404.html'):
t = get_template(template_name)
ctx = Context({})
return HttpResponseNotFound(t.render(ctx))
tests.py
from django.test import TestCase
from django.test.client import RequestFactory
from project import urls
from ..views import handler404, handler500
class TestErrorPages(TestCase):
def test_error_handlers(self):
self.assertTrue(urls.handler404.endswith('.handler404'))
self.assertTrue(urls.handler500.endswith('.handler500'))
factory = RequestFactory()
request = factory.get('/')
response = handler404(request)
self.assertEqual(response.status_code, 404)
self.assertIn('404 Not Found!!', unicode(response))
response = handler500(request)
self.assertEqual(response.status_code, 500)
self.assertIn('500 Internal Server Error', unicode(response))
Update for Django > 1.6 and without getting
page_not_found() missing 1 required positional argument: 'exception'
Inspired by this answer:
# urls.py
from django.views.defaults import page_not_found, server_error, permission_denied, bad_request
[...]
if settings.DEBUG:
# This allows the error pages to be debugged during development, just visit
# these url in browser to see how these error pages look like.
urlpatterns += [
path('400/', bad_request, kwargs={'exception': Exception('Bad Request!')}),
path('403/', permission_denied, kwargs={'exception': Exception('Permission Denied')}),
path('404/', page_not_found, kwargs={'exception': Exception('Page not Found')}),
path('500/', server_error),
You can simply define the handler404 and handler500 for errors in your main views.py file as detailed in this answer:
https://stackoverflow.com/a/18009660/1913888
This will return the error that you desire when Django routes to that handler. No custom URL configuration is needed to route to a different URL name.
In Django versions < 3.0, you should do as follows:
client.py
from django.core.signals import got_request_exception
from django.template import TemplateDoesNotExist
from django.test import signals
from django.test.client import Client as DjangoClient, store_rendered_templates
from django.urls import resolve
from django.utils import six
from django.utils.functional import SimpleLazyObject, curry
class Client(DjangoClient):
"""Test client that does not raise Exceptions if requested."""
def __init__(self,
enforce_csrf_checks=False,
raise_request_exception=True, **defaults):
super(Client, self).__init__(enforce_csrf_checks=enforce_csrf_checks,
**defaults)
self.raise_request_exception = raise_request_exception
def request(self, **request):
"""
The master request method. Composes the environment dictionary
and passes to the handler, returning the result of the handler.
Assumes defaults for the query environment, which can be overridden
using the arguments to the request.
"""
environ = self._base_environ(**request)
# Curry a data dictionary into an instance of the template renderer
# callback function.
data = {}
on_template_render = curry(store_rendered_templates, data)
signal_uid = "template-render-%s" % id(request)
signals.template_rendered.connect(on_template_render,
dispatch_uid=signal_uid)
# Capture exceptions created by the handler.
exception_uid = "request-exception-%s" % id(request)
got_request_exception.connect(self.store_exc_info,
dispatch_uid=exception_uid)
try:
try:
response = self.handler(environ)
except TemplateDoesNotExist as e:
# If the view raises an exception, Django will attempt to show
# the 500.html template. If that template is not available,
# we should ignore the error in favor of re-raising the
# underlying exception that caused the 500 error. Any other
# template found to be missing during view error handling
# should be reported as-is.
if e.args != ('500.html',):
raise
# Look for a signalled exception, clear the current context
# exception data, then re-raise the signalled exception.
# Also make sure that the signalled exception is cleared from
# the local cache!
response.exc_info = self.exc_info # Patch exception handling
if self.exc_info:
exc_info = self.exc_info
self.exc_info = None
if self.raise_request_exception: # Patch exception handling
six.reraise(*exc_info)
# Save the client and request that stimulated the response.
response.client = self
response.request = request
# Add any rendered template detail to the response.
response.templates = data.get("templates", [])
response.context = data.get("context")
response.json = curry(self._parse_json, response)
# Attach the ResolverMatch instance to the response
response.resolver_match = SimpleLazyObject(
lambda: resolve(request['PATH_INFO'])
)
# Flatten a single context. Not really necessary anymore thanks to
# the __getattr__ flattening in ContextList, but has some edge-case
# backwards-compatibility implications.
if response.context and len(response.context) == 1:
response.context = response.context[0]
# Update persistent cookie data.
if response.cookies:
self.cookies.update(response.cookies)
return response
finally:
signals.template_rendered.disconnect(dispatch_uid=signal_uid)
got_request_exception.disconnect(dispatch_uid=exception_uid)
tests.py
from unittest import mock
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings
from .client import Client # Important, we use our own Client here!
class TestErrors(TestCase):
"""Test errors."""
#classmethod
def setUpClass(cls):
super(TestErrors, cls).setUpClass()
cls.username = 'admin'
cls.email = 'admin#localhost'
cls.password = 'test1234test1234'
cls.not_found_url = '/i-do-not-exist/'
cls.internal_server_error_url = reverse('password_reset')
def setUp(self):
super(TestErrors, self).setUp()
User = get_user_model()
User.objects.create_user(
self.username,
self.email,
self.password,
is_staff=True,
is_active=True
)
self.client = Client(raise_request_exception=False)
# Mock in order to trigger Exception and resulting Internal server error
#mock.patch('django.contrib.auth.views.PasswordResetView.form_class', None)
#override_settings(DEBUG=False)
def test_errors(self):
self.client.login(username=self.username, password=self.password)
with self.subTest("Not found (404)"):
response = self.client.get(self.not_found_url, follow=True)
self.assertNotIn('^admin/', str(response.content))
with self.subTest("Internal server error (500)"):
response = self.client.get(self.internal_server_error_url,
follow=True)
self.assertNotIn('TypeError', str(response.content))
Starting from Django 3.0 you could skip the custom Client definition and just use the code from tests.py.

Can login_required by applied to an entire app?

Is there a way I can apply the login_required decorator to an entire app? When I say "app" I mean it in the django sense, which is to say a set of urls and views, not an entire project.
Yes, you should use middleware.
Try to look through solutions which have some differences:
http://www.djangosnippets.org/snippets/1179/ - with list of exceptions.
http://www.djangosnippets.org/snippets/1158/ - with list of exceptions.
http://www.djangosnippets.org/snippets/966/ - conversely with list of login required urls.
http://www.djangosnippets.org/snippets/136/ - simplest.
As of Django 3+, you can set login_require() to an entire app by applying a middleware. Do like followings:
Step 1: Create a new file anything.py in your yourapp directory and write the following:
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
//for registering a class as middleware you at least __init__() and __call__()
//for this case we additionally need process_view() which will be automatically called by Django before rendering a view/template
class ClassName(object):
//need for one time initialization, here response is a function which will be called to get response from view/template
def __init__(self, response):
self.get_response = response
self.required = tuple(re.compile(url) for url in settings.AUTH_URLS)
self.exceptions = tuple(re.compile(url)for url in settings.NO_AUTH_URLS)
def __call__(self, request):
//any code written here will be called before requesting response
response = self.get_response(request)
//any code written here will be called after response
return response
//this is called before requesting response
def process_view(self, request, view_func, view_args, view_kwargs):
//if authenticated return no exception
if request.user.is_authenticated:
return None
//return login_required()
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
//default case, no exception
return None
Step 2: Add this anything.py to Middleware[] in project/settings.py like followings
MIDDLEWARE = [
// your previous middleware
'yourapp.anything.ClassName',
]
Step 3: Also add the following snippet into project/settings.py
AUTH_URLS = (
//disallowing app url, use the url/path that you added on mysite/urls.py (not myapp/urls.py) to include as your app urls
r'/your_app_url(.*)$',
)
I think you are looking for this snippet, containing login-required middleware.
This is an old question. But here goes:
Django Decorator Include
This is a substitute of include in URLConf. Pefect for applying login_required to an entire app.
I clicked all the links in the anwsers, but they were all based on some kind of regular expressions. On Django 3+ you can do the following to restrict for a specific app:
Declare app_name="myapp" in your app's urls.py (https://docs.djangoproject.com/en/3.2/intro/tutorial03/#namespacing-url-names)
(now all these urls should be called with there namespace "myapp:urlname")
Create a middleware.py file in your app with this:
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import ImproperlyConfigured
from django.urls import resolve
class LoginRequiredAccess:
"""All urls starting with the given prefix require the user to be logged in"""
APP_NAME = 'myapp'
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured(
"Requires the django's authentication middleware"
" to be installed.")
user = request.user
if resolve(request.path).app_name == self.APP_NAME: # match app_name defined in myapp.urls.py
if not user.is_authenticated:
path = request.get_full_path()
return redirect_to_login(path)
return self.get_response(request)
Put "myapp.middleware.LoginRequiredAccess" in your MIDDLEWARE constant from settings.py
Then in your main project urls.py
urlpatterns = [
path('foobar', include('otherapp.urls')), # this will not be redirected
path('whatever', include('myapp.urls')), # all these urls will be redirected to login
]
On of the avantage of this method is it can still works with a root url path, e.g path('', include('myapp.urls')), while the others will do an infinite redirect loop.
I'm wondering if there is any solution to make it works like this:
/app/app.py
class AppConfig(AppConfig):
login_required = True
/project/urls.py
urlpatterns = [
url(r'app/', include('app.urls', namespace='app'))
]
/common/middleare.py
def LogMiddleware(get_response):
def middleware(request):
# solution 1
app = get_app(request)
if app.login_required is True and request.is_authenticated is Fasle:
return HttpResponseRedirect(redirect_url)
# solution 2
url_space = get_url_space(request.get_raw_uri())
if url_space.namespace in ['app', 'admin', 'staff', 'manage'] and \
request.is_authenticated is False:
return HttpResponseRedirect(redirect_url)
I will check if there is any methoded to get the app or url name of a request. I think it looks prettier.

Django sitemap index example

I have following models relation:
class Section(models.Model):
section = models.CharField(max_length=200, unique=True)
name = models.CharField(max_length=200, blank = True)
class Article (models.Model):
url = models.CharField(max_length = 30, unique=True)
is_published = models.BooleanField()
section = models.ForeignKey(Section)
I need to create a sitemap for articles, which contains sitemap files for sections. I was reading django documentation about it here http://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/
But didn't manage to find answer how can I:
Define sitemap class in this case
How can I pass section parameters into url file (as it's explained in
the docs)
From where can I get {'sitemaps': sitemaps} if I defined
sitemap as a python class in another file in the application
If i understand correctly you want to use sitemap index that would point at seperate sitemap xml files each for every section.
Django supports this feature by providing a separate sitemap view for index sitemaps.
Haven't used that feature before but something like the following would probably work in your case.
### sitemaps.py
from django.contrib.sitemaps import GenericSitemap
from models import Section
all_sitemaps = {}
for section in Section.objects.all():
info_dict = {
'queryset': section.article_set.filter(is_published=True),
}
sitemap = GenericSitemap(info_dict,priority=0.6)
# dict key is provided as 'section' in sitemap index view
all_sitemaps[section.name] = sitemap
### urls.py
from sitemaps import all_sitemaps as sitemaps
...
...
...
urlpatterns += patterns('',
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps}),
(r'^sitemap-(?P<section>.+)\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
)
Django==2.2
Python==3.6
Here is the better and easy way to use sitemap index in Django,
install django.contrib.sitemaps in the project by adding it to INSTALLED_APPS of settings.py.
Write sitemaps.py file in your apps and define classes as you need. Example extend django.contrib.sitemap.Sitemap in StaticViewSitemap sitemap class for static URLs, make sure your all static URL has the name for reverse lookup(getting URL from URL name)
# app/sitemap.py
from django.contrib import sitemaps
from django.urls import reverse
class StaticViewSitemap(sitemaps.Sitemap):
priority = 0.6
changefreq = 'monthly'
def items(self):
# URLs names
return ['index', 'aboutus', 'ourstory',]
def location(self, item):
return reverse(item)
Import all sitemaps in urls.py
import sitemap and index from django.contrib.sitemaps.views
then create a dictionary with sitemaps
# urls.py
from django.contrib.sitemaps.views import sitemap, index
from app.sitemaps import StaticViewSitemap
# add as many as sitemap you need as one key
sitemaps = {
"static" : StaticViewSitemap,
}
urlpatterns = [
# sitemap.xml index will have all sitemap-......xmls index
path('sitemap.xml', index, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.index'),
# sitemap-<section>.xml here <section> will be replaced by the key from sitemaps dict
path('sitemap-<section>.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
]
Here you will have two sitemaps 1. sitemaps.xml 2. sitemaps-static.xml
Run server open URL: http://localhost:8000/sitemap.xml
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>http://127.0.0.1:8000/sitemap-static.xml</loc>
</sitemap>
</sitemapindex>
Django automatically created an index of sitemaps now open URL: http://127.0.0.1:8000/sitemap-static.xml
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://localhost:8000/</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>http://localhost:8000/about-us</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>http://localhost:8000/our-story</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
</urlset>
For me, the accepted answer was impacting development and testing cycle speed since it made python manage.py commands run more slowly -- I had a little more to do in the DB than this example.
Here are the changes I made to mitigate (adapted to the example). Have yet to battle test it but this seems to do the trick (Python3):
### sitemaps.py
class SitemapLookup():
"""
Instantiated class replaces the dictionary of {'sitemap-section': Sitemap} for urls.py
Speeds up application load time by only querying the DB when a sitemap is first requested.
"""
def __init__(self):
self.sitemaps = {}
def __iter__(self):
self._generate_sitemaps_dict()
return self.sitemaps.__iter__()
def __getitem__(self, key):
self._generate_sitemaps_dict()
return self.sitemaps[key]
def items(self):
self._generate_sitemaps_dict()
return self.sitemaps.items()
def _generate_sitemaps_dict(self):
if self.sitemaps:
return
for section in Section.objects.all():
info_dict = {
'queryset': section.article_set.filter(is_published=True),
}
# dict key is provided as 'section' in sitemap index view
self.sitemaps[section.name] = GenericSitemap(info_dict, priority=0.6)
### urls.py
from sitemaps import SitemapLookup
...
...
...
sitemaps = SitemapLookup()
urlpatterns += patterns('',
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps}),
(r'^sitemap-(?P<section>.+)\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
)