middleware not executed on 404 - django

We have middleware that records the last url the user has visited:
class LastSiteUrl(object):
def is_admin_url(self, url):
if re.search('^(http:\/\/.*){0,1}\/admin\/', url) is None: return(False)
else: return(True)
def process_request(self, request):
if self.is_admin_url(request.path) and \
not self.is_admin_url(request.META['HTTP_REFERER']):
request.session['last_site_url'] = request.META['HTTP_REFERER']
We use this to return them back to the last app page they were at,
from an admin page, no matter how deep into admin they were. This is
working fine.
We have a custom 404 page, and from there I want to provide a link to
take them back to where they were in the app. But it seems the
middleware does not run when they go to an invalid page. Is there a
way to make it run in that case as well?

If you use handler404 in your base urls.py you can serve your 404 page from a django view instead of straight from the server, and add that middleware the way you would to any other django view.
https://docs.djangoproject.com/en/dev/ref/urls/#django.conf.urls.handler404
in urls.py
handler404 = 'app.views.view'
or
handler404 = ClassView.as_view()

I don't know if this was the answer 10 years ago, but I've encountered the same issue myself and found that Abdul Aziz Barkat's answer to my question, " Why is the Middleware not processed on 404 page in Django? " was the right answer for me: to use a custom context processor instead of middleware.
The answers suggesting using handler404 led me down a long and unproductive rabbit hole.

Related

Django 2.2: How to redirect a user from a view to a specific page with a context?

Our website is listing properties ads.
When a user publish an ad, I would like to redirect him to a "thank you" page. But inside this thank you page I would like to put a button with the link to the detailed view of the created ad.
I don't want to use "messages" framework because I need a dedicated page for tracking purpose.
How can I send the context (in this case the absolute url of the created object) to the "thank you" page?
It doesn't matter if the solution requires the use of a class based view or a function view.
Many thanks in advance !
I found a clean solution.
In my view I redirect to a success page and pass a parameter. In the urls.py I setup the path of the success page in order to take the parameter in the URL. Then I can use the data passed to the success page.
in views.py using class based views
def post(self, request):
my_param = my_param
return redirect('success', param=my_param)
in urls.py
urlpatterns = [
path('success/<str:param>/', Success.as_view(), name='success'),
]
Hope this will help. It seems basic but it took me some time to figure out what I was looking for :)

how to redirect from HomePage(Page) to your custom app model

I've created an app "Blog". In my app I've got several models including "BlogIndex(Page)". When I run local server I find myself at "home_page.html". What I want is to start my local server at "blog_index.html". I know that I can set a root page at settings>site>localhost to make my "blog_index.html" a root page, but I can't do this because in my app I've got some other models that live at the same level as "BlogIndex(Page)" and they are children of the root which is "HomePage" so it would brake my code. So my question is: can I make a redirect from "HomePage(Page)" to my "BlogIndex" so that when i start my server I would be automatically redirected from "HomePage" to "BlogIndex"? How can I do it? How much it will affect the performance of the site and it's optimization?
I know that there is settings>redirect but it works only for inactive pages, but i need "HomePage" to be active.
Thank you.
Perhaps a better approach would be to display your blog posts (and any other models you want) on your homepage. Just override get_context(). See here: Wagtail Views: extra context
Update:
You can redirect by overriding the serve() method. For example, in your model, you would do something like:
# home/models.py
...
from django.http import HttpResponseRedirect
from django.urls import reverse
class HomePage(Page):
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
def serve(self, request):
# Redirect to blog index page
return HttpResponseRedirect('/blog/')
# only do this if you're using urls.py and namespaces
# return HttpResponseRedirect(reverse('blog:index'))
More info: http://docs.wagtail.io/en/latest/reference/pages/model_recipes.html?highlight=serve()#overriding-the-serve-method

Django avoid user to go back to login page once logged in

The admin app in django has this behaviour, and I want to replicate it in my project.
In the admin, once you login and press the browser back button, shows the home page again, and the it does not let you go back any further.
It doesn't seem it uses javascript.
Any ideas how to accomplish this?
Thanks.
My code
def login_user(request):
if request.user.is_authenticated:
return redirect(reverse('sinapsis-home'))
else:
...# code processing the authentication
# the redirect
return redirect(reverse('sinapsis-home'))
The solution is partially the proposed answer, but adding a cache decorator.
It is not javascript, and it was so easy to prove just disabling javascript in the browser. I was slow on that point.
As you navigate the browser back and for, the cached page does not load again, that's why you can go back, in this case, to the login page even if you are logged in. I think this behaviour is prone to problems, for example a csrf error. I repeat, not permitting the user go back to the login page once logged in is a standard behaviour of web apps, including the django admin.
So the solution to this problem is adding to the login view a redirect if the user is logged in and a never_cache decorator:
from django.views.decorators.cache import never_cache
#never_cache
def login_user(request):
...
if user.is_authenticated:
return HttpResponseRedirect(reverse('my_redirect'))
...
Well, Like you said that isn't js, It's as simple as an if conditional like:
if not request.user.is_authenticated:
# Return a login form to login
else:
return redirect('success:page')
Edit:
After add your code, the problem was that you were using request.user.is_autenticated() with parentheses and it was deprecated in Django 1.10 more information

django multiple domains, single app

I am trying to set my Django app work with multiple domains (while serving slightly different content)
I wrote this middleware:
class MultiSiteMiddleware(object):
def process_request(self, request):
host = request.get_host()
host_part = host.split(':')[0].split('.com')[0].split('.')
host = host_part[len(host_part)-1] + '.com'
site = Site.objects.get(domain=host)
settings.SITE_ID = site.id
settings.CURRENT_HOST = host
Site.objects.clear_cache()
return
In views I use this:
def get_site(request):
current_site = get_current_site(request)
return current_site.name
def view(request, pk):
site = get_site(request)
if site == 'site1':
# serve content1
...
elif site == 'site2'
# serve content2
...
But now there are 404 errors (I sometimes find them in logs, don't see them while browsing my site manually) where they aren't supposed to be, like my site sometimes is serving content for wrong domains, can they happen because of some flaw in the above middleware and view code or I should look somewhere else?
I had a similar requirement and decided not to use the django sites framework. My middleware looks like
class MultiSiteMiddleware(object):
def process_request(self, request):
try:
domain = request.get_host().split(":")[0]
request.site = Site.objects.get(domain=domain)
except Site.DoesNotExist:
return http.HttpResponseNotFound()
then my views have access to request.site
If you're seeing 404's for sites that aren't yours in your logs it would seem like somebody has pointed their domain at your servers IP address, you could use apache/nginx to filter these out before they hit your app, but your middleware should catch them (though possibly by raising an uncaught 500 error instead of a 404)
Serve multiple domain from one website (django1.8 python3+)
The goal is, obviously, to serve multiple domains, from one Django instance. That means, we use the same models, the same database, the same logic, the same views, the same templates, but to serve different things.
Searching the interwebs, I came to the idea of using the sites framework. The sites framework was designed to do exactly that thing. In fact, the sites framework has been used to do exactly that. But I haven't been able to know on which version of Django it was, and actually, I came to the idea the sites framework was just a vestigial module. Basically, it is just a table, with SITE_ID and SITE_URL, that you can easily access with a function. But I've been unable to find how, from that, you can do a multi-domain website.
So my idea has been, how to modify the url resolver. The idea behind all these is easy : www.domain1.com/bar is resolved to /domain1/bar, and www.domain2.foo is resolved to /domain2/foo. This solves the problem because, if you want to serve multiple domains, you just have to serve multiple folders.
In Django, to achieve this, you have to modify two things :
* the way Django routes requests
* the way django write urls
The way Django routes requests
Django routes requests with middlewares. That's it. So we just have to write a middleware that re-route requests.
To make it easier, middlewares can have a process_request method that process requests (WOW), before requests are handled. So let's write a DomainNameMiddleware
#!python
#app/middleware.py
class DomaineNameMiddleware:
"""
change the request path to add the domain_name at the first
"""
def process_request(self, request):
#first, we split the domain name, and take the part before the extension
request_domain = request.META['HTTP_HOST'].split('.')[-2]
request.path_info = "/%s/%s" % (request_domain, request.path.split('/')[1:])
The way Django writes URL
When I'm talking about django writing url, i'm principally thinking of the {% url %} template tag, the get_absolute_url methods, the resolve and resolve_lazy functions, and those basic Django thing. If we rewrite the way Django handle url, we have to tell Django to write url that way.
But basically it's pretty easy, thanks to Django.
You can easily rewrite basic Django function by just rewriting them, typically in init.py files of modules you added as apps. So :
#!python
#anyapp/__init__.py
from django.core import urlresolvers
old_reverse = urlresolvers.reverse
def new_reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
"""
return an url with the first folder as a domain name in .com
"""
TLD = 'com'
old_reverse_url = old_reverse(viewname, urlconf, args, kwargs, current_app)
# admin will add itself everytime you reload an admin page, so we have to delete it
if current_app == 'admin':
return '/%s' % old_reverse_url[len('admin'):].replace('adminadmin', 'admin')
return '//%s.%s/%s' % (app, TLD, path)
How to use it ?
I use it with base urls.py as dispatchedr.
#!python
#urls.py
from django.conf.urls import include, url
from domain1 import urls as domain1_urls
from domain2 import urls as domain2_urls
urlpatterns = [
url(r'^domain1/', include(domain1_urls, namespace='domain1')),
url(r'^domain2/', include(domain2_urls, namespace='domain2)),
]
Django's Sites framework has built-in middleware to accomplish this.
Simply enable the Sites framework and add this to your MIDDLEWARE:
'django.contrib.sites.middleware.CurrentSiteMiddleware'
This automatically passes a request object to Site.objects.get_current() on every request. It solves your problem by giving you access to request.site on every request.
For reference, the code as of 1.11 is:
from django.utils.deprecation import MiddlewareMixin
from .shortcuts import get_current_site
class CurrentSiteMiddleware(MiddlewareMixin):
"""
Middleware that sets `site` attribute to request object.
"""
def process_request(self, request):
request.site = get_current_site(request)
I will suggest you to use django-multisite .It will fulfill your requirement.
Try using the "sites" framework in django to get the domain name. You already know this I guess.
Take a look here: https://docs.djangoproject.com/en/1.7/ref/contrib/sites/#getting-the-current-domain-for-full-urls
See this:
>>> Site.objects.get_current().domain
'example.com'
Without the https://www or http://www.. Probably your domains will end in .org or some country .pe .ru etc not just .com.
There might be a case when people don't point to your domain but to your IP address for some reason, maybe development of testing so you should always raise an exception with Site.DoesNotExist

Using flatpages to create a simple facebook app, but CSRF problem caused by signed_request

I am trying to create a simple, html-only facebook app for a client's fb page. I would like to use django's flatpages so that the client and his staff can change the content of the app through the django admin of their site. The problem is that Django returns a 403 "CSRF verification failed. Request aborted." error when facebook attempts to send its own POST info and access the url of the app.
I already know about the #csrf_exempt decorator, but I am not sure how I would apply it to the flatpage view, as it is within the django.contrib code. Furthermore I would only want to disable csrf protection when the view is asked to call a specific facebook.html template (not not the default.html template). If there happened to be a {% crsf_exempt %} template tag for example, that would be perfect.
Can anyone think of a way to resolve this problem? Or maybe I should give up on the idea of using the django-flatpages to serve the facebook app?
Try using this decorator on your views that are called facebook:
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
this will disable csrf protection on that view.
Does this help?
I ran into the exact same problem as you. I wanted to diable csrf for flatpages (but not for the rest of the site) and ended up with the following middleware:
class DisableCSRFOnFlatPages(object):
def process_request(self, request):
try:
FlatPage.objects.get(url=request.META.get('PATH_INFO'))
setattr(request, '_dont_enforce_csrf_checks', True)
except FlatPage.DoesNotExist:
return
Add it to your settings and it should disable the csrf check whenever there's a flatpage.