Django - custom logger - django

I have some interesting task - write custom logger for Django, that stores request (INFO level) and show data (DEBUG level) in file.
I cannot use standard Django logger.
Can you point me in right direction?
Should I use middleware?
Thanks. I appreciate your time.

So far i figure out something like:
import os
import logging
from django.conf import settings
# logging config
logging.basicConfig(filename=os.path.join(settings.BASE_DIR, 'debug.log'),
level=logging.DEBUG)
logger = logging.getLogger(__name__)
class FileLoggerMiddleware(object):
def process_response(self, request, response):
request_log = 'Path: {}, GET: {}, POST: {}'.format(
request.path, request.GET, request.POST)
logger.info(request_log)
# We have queries logging out of box
# But here what i wood wrote:
# from django.db import connection
# for q in connection.queries:
# query_log = "({}) QUERY ".format(q.time, q.sql ) //and more info
# logger.debug(query_log)
return response

Related

Issues while integrating tornado app with django site

I have a simple chat application in Tornado powered with RethinkDB.
Am trying to integrate this tornado chat application to run with django site.
For that reason, I have adopted below in rechat.py in order for it to work with django.
Namespaces tornado.wsgi and django.core.wsgi (get_wsgi_application)
Set environment variable for Django settings.py
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoapp.settings'
When I try to run it after the above changes, it connects the db server, but doesn't do anything. What am I missing?
How can I make this tornado app to work with django 1.8 site?
Below is my code of rechat.py (https://github.com/v3ss0n/rechat) -
import logging
import tornado.escape
from tornado.ioloop import IOLoop
import tornado.web
import os.path
import rethinkdb as r
from tornado import httpserver
from time import time
# from tornado.concurrent import Future
from tornado import gen
from tornado.options import define, options, parse_command_line
import tornado.wsgi
from django.core.wsgi import get_wsgi_application
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=True, help="run in debug mode")
def setup_db(db_name="rechat", tables=['events']):
connection = r.connect(host="localhost")
try:
r.db_create(db_name).run(connection)
for tbl in tables:
r.db(db_name).table_create(tbl, durability="hard").run(connection)
logging.info('Database setup completed.')
except r.RqlRuntimeError:
logging.warn('Database/Table already exists.')
finally:
connection.close()
class RechatApp(tornado.web.Application):
def __init__(self, db):
handlers = [
(r"/", MainHandler),
(r"/a/message/new", MessageNewHandler),
(r"/a/message/updates", MessageUpdatesHandler),
]
settings = dict(cookie_secret="_asdfasdaasdfasfas",
template_path=os.path.join(
os.path.dirname(__file__), "templates"),
static_path=os.path.join(
os.path.dirname(__file__), "static"),
xsrf_cookies=True,
debug=options.debug)
self.db = db
logging.info(db)
tornado.web.Application.__init__(self, handlers, **settings)
class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.db = self.application.db
self.evt = r.table("events")
class MainHandler(BaseHandler):
#gen.coroutine
def get(self):
curs = yield self.evt.run(self.db)
messages = []
while (yield curs.fetch_next()):
item = yield curs.next()
messages.append(item)
self.render("index.html", messages=messages)
class MessageNewHandler(BaseHandler):
#gen.coroutine
def post(self):
message = {
"body": self.get_argument("body")
}
# to_basestring is necessary for Python 3's json encoder,
# which doesn't accept byte strings.
start = time()
messages = (yield self.evt.insert(message).run(self.db))
time_taken = time() - start
logging.warn("DBINSERT: %s seconds" % time_taken)
message['id'] = messages['generated_keys'][0]
message["html"] = tornado.escape.to_basestring(
self.render_string("message.html", message=message))
if self.get_argument("next", None):
self.redirect(self.get_argument("next"))
else:
self.write(message)
class MessageUpdatesHandler(BaseHandler):
#gen.coroutine
def post(self):
curs = yield self.evt.changes().run(self.db)
while (yield curs.fetch_next()):
feed = yield curs.next()
message = {
'id': feed['new_val']['id'],
'html': tornado.escape.to_basestring(
self.render_string("message.html",
message=feed['new_val']))}
break
self.finish(dict(messages=[message]))
#gen.coroutine
def main():
""" Async main method. It needed to be async due to r.connect is async . """
parse_command_line()
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoapp.settings'
db_name = "rechat"
setup_db(db_name)
r.set_loop_type("tornado")
db = yield r.connect("localhost", db=db_name)
#Single db connection for everything thanks a lot Ben and Jeese
http_server = httpserver.HTTPServer(RechatApp(db))
http_server.listen(options.port)
if __name__ == "__main__":
IOLoop.current().run_sync(main)
IOLoop.current().start()

Cannot login again after resuming crawl. Cookies are not sticky after resuming scrapy

I have a CrawlSpider, the code is below. I use Tor through tsocks.
When I start my spider, everything works fine. Using init_request I can login on site and crawl with sticky cookies.
But problem occurred when I stopped and resumed spider. Cookies became not sticky.
I give you the response from Scrapy.
=======================INIT_REQUEST================
2013-01-30 03:03:58+0300 [my] INFO: Spider opened
2013-01-30 03:03:58+0300 [my] INFO: Resuming crawl (675 requests scheduled)
............ And here crawling began
So... callback=self.login_url in def init_request is not fired!!!
I thought that scrapy engine didn't want to send again request on login page. Before resuming scrapy I changed login_page (I can login from every page on site) to different that not included in restrict_xpaths.
Result is - After resuming I cannot login and previous cookies are lost.
Does anyone have some assumptions?
from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from scrapy.contrib.loader import XPathItemLoader
from scrapy.contrib.loader.processor import TakeFirst, MapCompose, Join, Identity
from beles_com_ua.items import Product
from scrapy.utils.response import get_base_url
from scrapy.utils.url import urljoin_rfc
from scrapy.utils.markup import remove_entities
from django.utils.html import strip_tags
from datetime import datetime
from scrapy import log
import re
from scrapy.http import Request, FormRequest
class ProductLoader(XPathItemLoader):
.... some code is here ...
class MySpider(CrawlSpider):
name = 'my'
login_page = 'http://test.com/index.php?section=6&type=12'
allowed_domains = ['test.com']
start_urls = [
'http://test.com/index.php?section=142',
]
rules = (
Rule(SgmlLinkExtractor(allow=('.',),restrict_xpaths=('...my xpath...')),callback='parse_item', follow=True),
)
def start_requests(self):
return self.init_request()
def init_request(self):
print '=======================INIT_REQUEST================'
return [Request(self.login_page, callback=self.login_url)]
def login_url(self, response):
print '=======================LOGIN======================='
"""Generate a login request."""
return FormRequest.from_response(response,
formdata={'login': 'mylogin', 'pswd': 'mypass'},
callback=self.after_login)
def after_login(self, response):
print '=======================AFTER_LOGIN ...======================='
if "images/info_enter.png" in response.body:
print "==============Bad times :(==============="
else:
print "=========Successfully logged in.========="
for url in self.start_urls:
yield self.make_requests_from_url(url)
def parse_item(self, response):
hxs = HtmlXPathSelector(response)
entry = hxs.select("//div[#class='price']/text()").extract()
l = ProductLoader(Product(), hxs)
if entry:
name = hxs.select("//div[#class='header_box']/text()").extract()[0]
l.add_value('name', name)
... some code is here ...
return l.load_item()
The init_request(self): is available only when you subclass from InitSpider not CrawlSpider
You need to subclass your spider from InitSpider like this
class WorkingSpider(InitSpider):
login_page = 'http://www.example.org/login.php'
def init_request(self):
#"""This function is called before crawling starts."""
return Request(url=self.login_page, callback=self.login)
But then remember that you can't define Rules in initSpider as its only avaiable in CrawlSpider you need to manually extract the links

Scrapy: Login with Selenium webdriver, transfer cookies to spider object?

I was just wondering if there's any reasonable way to pass authentication cookies from webdriver.Firefox() instance to the spider itself? It would be helpful to perform some webdriver stuff and then go about scraping "business as usual". Something to the effect of:
def __init__(self):
BaseSpider.__init__(self)
self.selenium = webdriver.Firefox()
def __del__(self):
self.selenium.quit()
print self.verificationErrors
def parse(self, response):
# Initialize the webdriver, get login page
sel = self.selenium
sel.get(response.url)
sleep(3)
##### Transfer (sel) cookies to (self) and crawl normally??? #####
...
...
Transfer Cookies from Selenium to Scrapy Spider
Scrapying File
from selenium import webdriver
driver=webdriver.Firefox()
data=driver.get_cookies()
# write to temp file
with open('cookie.json', 'w') as outputfile:
json.dump(data, outputfile)
driver.close()
outputfile.close()
....
Spider
import os
if os.stat("cookie.json").st_size > 2:
with open('./cookie.json', 'r') as inputfile:
self.cookie = json.load(inputfile)
inputfile.close()
You can try to override BaseSpider.start_requests method to attach to starting requests needed cookies using scrapy.http.cookies.CookieJar.
See also: Scrapy - how to manage cookies/sessions
This works with chrome driver but not Firefox (Tested OK)
refer https://christopher.su/2015/selenium-chromedriver-ubuntu/ for installation.
import scrapy
from scrapy.spiders.init import InitSpider
from scrapy.http import Request
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pickle
class HybridSpider(InitSpider):
name = 'hybrid'
def init_request(self):
driver = webdriver.Chrome()`
driver.get('https://example.com')
driver.find_element_by_id('js-login').click()
driver.find_element_by_id('email').send_keys('mymail#example.net')
driver.find_element_by_id('password').send_keys('mypasssword',Keys.ENTER)
pickle.dump( driver.get_cookies() , open(os.getenv("HOME")+"/my_cookies","wb"))
cookies = pickle.load(open(os.getenv("HOME")+"/my_cookies", "rb"))
FH = open(os.getenv("HOME")+"/my_urls", 'r')
for url in FH.readlines():
pass
yield Request(url,cookies=cookies,callback=self.parse)
def parse(self, response):
pass
Haven't tried directly passing the cookies like
yield Request(url,cookies=driver.get_cookies(),callback=self.parse)
Might work too..
driver = webdriver.Chrome()
Then perform the login or interact with the page through the browser. Now when using the crawler in scrapy, set the cookies parameter:
request = Request(URL, cookies=driver.get_cookies(), callback=self.mycallback)

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.