It seems like they have a gzip_page decorator for views
from django.views.decorators.gzip import gzip_page
#gzip_page
def viewFunc(request):
return HttpResponse("hello"*100)
Reference here: https://docs.djangoproject.com/en/1.4/topics/http/decorators/#module-django.views.decorators.gzip
as well as a middleware class you can add to settings and then use.
MIDDLEWARE_CLASSES = (
django.middleware.gzip.GZipMiddleware,
...
)
from django.middleware.gzip import GZipMiddleware
gzip_middleware = GZipMiddleware()
def dec(request, *args, **kwargs):
response = func(request, *args, **kwargs)
return gzip_middleware.process_response(request, response)
return dec
Using the page decorator kind of sped up the rendering of a huge ammt. of data (for a table), was wondering which way was best/ what the differences were.
Thanks for any info!
If you look in Django sources, you will see that gzip_page decorator and GZipMiddleware are in fact one and the same thing:
gzip_page = decorator_from_middleware(GZipMiddleware)
So there is no difference at all (apart from some overhead needed to convert middleware to decorator). If you need to enable gzip only for one specific view you should obviously use the decorator, but if you want to have gzip on for the entire project, use the middleware.
As a sidenote, I'm sure you will get much better performance if you leave gzipping to whatever web server you are using (Apache, nginx, etc.)
Also be aware that you shouldn't use gzip for HTTPS protected websites, more on it here: http://breachattack.com/
Related
I'm building a project and now I'm new to VueJS I'm currently learning it. and I found that you can make HTTP Requests on APIs using axios. And to make my project easy, Can I call functions on my views.py thru axios?
Like I'm fetching urls in urls.py to execute some functions on my backend.
Is it okay? I mean for security and best practices. etc.
Thanks
Absolutely ok, that's what Django is for:
urls.py:
urlpatterns = [
...
path('my_view_function/', views.my_view_function, name='my_view_function'),
...
]
views.py:
def my_view_function(request):
# unpack data:
my_value = request.GET['my_key']
# some logic:
...
# pack response:
response = json.dumps({
'some_other_key' : 'some_other_value'
})
return HttpResponse(response)
Another option is that you use signals in django, some time ago I used signals that for when a new record is created without a field, it will be completed with the algorithm you want, for example an order and automatically placed a code, you can apply it only by pointing to your models, when you want something more punctual.
#receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
...
The my_handler function will only be called when an instance of MyModel is saved.
Here I leave the documentation in case you want to review it
https://docs.djangoproject.com/en/3.1/topics/signals/
I want to store (in files) all request/response pairs of views in a Django app.
How to do this?
I could probably use tcpflow, but would prefer to use a Python solution in Django itself rather than external programs like tcpflow (which also requires root privileges, what I dislike).
You can create a middleware, something like (adapted the example from the doc):
def simple_middleware(get_response):
def middleware(request):
response = get_response(request)
# SAVE REQUEST AND RESPONSE HERE
return response
return middleware
And adding it to the MIDDLEWARE setting.
I'm trying to work with the following scenario:
When a GET request comes in to my "/" route I normally want to handle it with my HomeView. However, my site is heavy AJAX so if the request's UserAgent is a bot then I serve it up with a fully rendered version of the page (standard PhantomJS stuff). The approach works fine, but the performance of the fully rendered version, and the SLA for that version, is very different than the regular user view. As such, I would like to use a piece of middleware to do the bot detection and based on that middleware I would then like to send the request to a different View.
The middleware part is easy, I have a process_request handler that detects the bot - no big deal. However, I can't figure any option for overriding the View function that will be invoked. Is there a "proper" way to do this in Django? My current thoughts are:
modify request.path_info to change the requested URL so that the router will then send HtmlRendererView rather than HomeView
Call the HtmlRendererView directly from the middleware and return the appropriate HttpResponse. This feels clunky because it then takes away the opportunity for any other middleware to run.
Notes:
I don't want to return a redirect, the crawler is getting a different version of the same resource
I'm on heroku so I can't rewrite the route before it hits Django. If I was using nginx I'd probably just put this logic at that layer and rewrite the URL before it hit Django.
This is not a direct answer to your question ("Reroute a request to a different view"), but maybe this solution could adress your problem.
First, you keep your middleware, but use it only to detect if the visitor is a bot:
def process_request(self, request):
request.is_bot = is_bot(request) # assuming you have a function for detecting bots
return
Then you create a class based view that call a specific method when request.is_bot is True:
class BotViewMixin(object):
def dispatch(self, request, **kwargs):
if request.is_bot:
return self.handle_bot()
return super(BotViewMixin, self).dispatch(request, **kwargs)
You can then inherit this view anywhere you need (e.g. for your Home Page View). You just have to create a handle_bot method on your view, that will return your response for bots.
Advantages of this solution:
You don't need to write different views for bots, just create a dedicated method
You don't block other middlewares
Your logic stay in your views (and not in your middleware)
This is not tested though, so you may need to adapt the code.
EDIT:
Since you use NewRelic and must use a dedicated view for bots in order to get accurate statistics, this approach won't work for you.
You can go with the middleware thing, and still get all middlewares working. You just have to put your own middleware last in MIDDLWARE_CLASSES:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.CrawlerDetector',
)
Also, I think you should write two middlewares methods: process_request for detecting bots, and process_view for redirecting bots to dedicated view.
The following code should probably work for your situation:
from django.core.urlresolvers import reverse
class CrawlerDetector(object):
def process_request(self, request):
"""detect if the user agent is a bot"""
user_agent = request.META.get('HTTP_USER_AGENT', "")
request.is_bot = self.is_crawler(user_agent)
return
def process_view(request, view_func, view_args, view_kwargs):
if request.is_bot and request.path == reverse('home_page'):
return HtmlRendererView().get(request)
return
My current working solution, while not as clean as Eliot's suggested solution, looks (basically) like this:
class CrawlerDetector(object):
# Middleware that detects requests that should be rendered by the HtmlRendererView.
def process_request(self, request):
user_agent = request.META.get('HTTP_USER_AGENT', "")
if not self.is_crawler(user_agent):
return None
return HtmlRendererView().get(request)
It has the downside of removing any downstream middleware from the flow, but it does allow me to call my crawler-specific View before the root View is routed to.
I have enabled site-wide Django caching, but the third-party apps I am using have not specified any cache-control expectations. So, I am guessing that their views will get cached.
The problem is that I do not want Django to cache the views of some apps. How do I apply url-level cache control on include()?
url(r"^account/", include("pinax.apps.account.urls")) #How to apply cache control here?
You can't. The per-site cache is achieved through middlewares which considers only request and response instead of specific view.
However, you could achieve this by providing a patched django.middleware.cache.FetchFromCacheMiddleware.
class ManagedFetchFromCacheMiddle(FetchFromCacheMiddleware):
def process_request(self, request):
if should_exempt(request):
request._cache_update_cache = False
return
return super(ManagedFetchFromCacheMiddle, self).process_request(request)
def should_exempt(request):
"""Any predicator to exempt cache on a request
For your case, it looks like
if request.path.startswith('/account/'):
return True
"""
Replace 'django.middleware.cache.FetchFromCacheMiddleware' with the path of the above in MIDDLEWARE_CLASSES.
Maybe generic version of above is suitable for commiting patch to Django community =p
I have a number of sites under one Django application that I would like to implement site wide caching on. However it is proving to be a real hassle.
what happens is that settings.CACHE_MIDDLEWARE_KEY_PREFIX is set once on startup, and I cannot go ahead and change it depending on what the current site is. As a result if a page of url http://website1.com/abc/ is cached then http://website2.com/abc/ renders the cached version of http://website1.com/abc/. Both these websites are running on the same Django instance as this is what Django Sites appears to allow us to do.
Is this an incorrect approach? Because I cannot dynamically set CACHE_MIDDLEWARE_KEY_PREFIX during runtime I am unable to cache multiple sites using Django's Site wide caching. I also am unable to do this for template and view caching.
I get the impression that the way this really needs to be setup is that each site needs its own Django instance which is pretty much identical except for the settings file, which in my case will differ only by the value of CACHE_MIDDLEWARE_KEY_PREFIX. These Django instances all read and write to the same database. This concerns me as it could create a number of new issues.
Am I going down the right track or am I mistaken about how multi site architecture needs to work? I have checked the Django docs and there is not real mention of how to handle caching (that isn't low level caching) for Django applications that serve multiple sites.
(Disclaimer: the following is purely speculation and has not been tested. Consume with a pinch of salt.)
It might be possible to use the vary_on_headers view decorator to include the 'Host' header in the cache key. That should result in cache keys that include the HTTP Host header, thus effectively isolating the caches for your sites.
#vary_on_headers('Host')
def my_view(request):
# ....
Of course, that will only work on a per-view basis, and having to add a decorator to all views can be a big hassle.
Digging into the source of #vary_on_headers reveals the use of patch_vary_headers() which one might be able to use in a middleware to apply the same behaviour on a site level. Something along the lines of:
from django.utils.cache import patch_vary_headers
class VaryByHostMiddleware(object):
def process_response(self, request, response):
patch_vary_headers(response, ('Host',))
return response
I faced this problem recently. What I did based on the documentation was to create a custom method to add the site id to the key used to cache the view.
In settings.py add the KEY_FUNCTION argument:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
'LOCATION': 'path.to.location',
'TIMEOUT': 60,
'KEY_FUNCTION': 'path.to.custom.make_key_per_site',
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
And my custom make_key method:
def make_key_per_site(key, key_prefix, version):
site_id = ''
try:
site = get_current_site() # Whatever you use to get your site's data
site_id = site['id']
except:
pass
return ':'.join([key_prefix, site_id, str(version), key])
You need to change get_full_path to build_absolute_uri in django.util.cache
def _generate_cache_header_key(key_prefix, request):
"""Returns a cache key for the header cache."""
#path = md5_constructor(iri_to_uri(request.get_full_path()))
path = md5_constructor(iri_to_uri(request.build_absolute_uri())) # patch using full path
cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
key_prefix, path.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)
def _generate_cache_key(request, method, headerlist, key_prefix):
"""Returns a cache key from the headers given in the header list."""
ctx = md5_constructor()
for header in headerlist:
value = request.META.get(header, None)
if value is not None:
ctx.update(value)
#path = md5_constructor(iri_to_uri(request.get_full_path()))
path = md5_constructor(iri_to_uri(request.build_absolute_uri()))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
key_prefix, request.method, path.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)
Or create you own slightly changed cache middleware for multisite.
http://macrotoma.blogspot.com/2012/06/custom-multisite-caching-on-django.html