Django | capture sub domain as a string - django

How do I capture a part of sub-domain name and get that name as a string in my views through a request.
ex:
user.domain.com
developer.domain.com
I want to capture the user part of this domain name through a request (lets say when the first time user hits the page).
Thanks.

This can be done using middleware.
Here is what I have been using...
class SubdomainMiddleware:
""" Make the subdomain publicly available to classes """
def process_request(self, request):
domain_parts = request.get_host().split('.')
if (len(domain_parts) > 2) or (len(domain_parts) == 2 and domain_parts[1].find('localhost') != -1):
subdomain = domain_parts[0]
if (subdomain.lower() == 'www'):
subdomain = None
domain = '.'.join(domain_parts[1:])
else:
subdomain = None
domain = request.get_host()
request.subdomain = subdomain
request.domain = domain
I got this code from somewhere and modified it a little, although I can't recall where it was originally from.
Just Put that in a file somewhere, then add it to your MIDDLEWARE_CLASSES list in settings.py.
Then, you'll be able to access the subdomain using request.subdomain wherever request is available (e.g. in views, where I assume you will need it)

Related

Permanent redirect absolute url

I am struggling with a basic redirect functionality.
I need to redirect all traffic not matching certain paths to another domain.
in my urls.py
re_path(r'^(?P<shortcode>[\w-]+)/$', core_views.myView)
and the corresponding function in views.py
def myView(request, shortcode=None):
url = 'www.newdomain.cz/' + str(shortcode)
return HttpResponsePermanentRedirect(url)
but what it does is - when called for example www.olddomain.com/sdfasd it redirects me to www.olddomain.com/sdfasd/www.newdomain.cz/sdfasd but I obviously need only www.newdomain.cz/sdfasd
what am I missing?
You need to use a fully qualified url.
def myView(request, shortcode=None):
url = 'http://www.newdomain.cz/' + str(shortcode)
See the doc here.

DRF - How do I whitelist a site for endpoint requests

This is my scenario:
Hosted Django application running DRF
External site hosted elsewhere. Could be multiple sites.
My question is: How do I whitelist any Javascript POST request from an external site? For example I want to add a Javascript post request on a forms confirmation page. How do I do this so my DRF application only accepts requests from this site only? I'm trying not to use server site code. Just Javascript so it easily applied.
My concern is anyone that views the source will be able to view the API endpoint and would be able to submit POST requests.
You can implement a whitelist by creating a custom permissions class, here is an example from the docs.
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
"""
def has_permission(self, request, view):
ip_addr = request.META['REMOTE_ADDR']
blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
return not blacklisted
For a whitelist implementation you would want to grab the REMOTE_HOST from the incoming request and compare it against a predefined list of urls, which could be stored in the database or in a list in the class. it might look something list this:
# Model
class Whitelist(models.Model):
host = models.CharField()
# Permission
class WhitelistPermission(permissions.BasePermission):
"""
Global permission check for whitelisted IPs.
"""
def has_permission(self, request, view):
domain = request.META['REMOTE_HOST']
whitelisted = Whitelist.objects.filter(host=domain).exists()
return whitelisted
Or you could whitelist based off IP be replacing REMOTE_HOST with REMOTE_ADDR
Maybe you can configure this in your web server. So that request is blocked at the entry level itself.
If you're using nginx then something like this https://support.hypernode.com/knowledgebase/blocking-allowing-ip-addresses-in-nginx/

Override django get_current_site - Work with subdomain

I have a Django project with Sites enabled.
This project will handle multiple domains and multiple sub-domains.
I have domain.com in Sites table, but when I access sub.domain.com/admin, I get the error Site matching query does not exist. If I add sub.domain.com to Sites table, everything works fine.
Site.objects.create(domain='domain.com', name='Test1')
# Site.objects.create(domain='sub.domain.com', name='Test2')
The problem is that get_current_site(request) does not consider sub-domains and I don't want to create several instances in Sites in order to represent every single sub-domain and domain.
Is there any way I can override get_current_site(request)? I've already built my version but Django itself calls the original one when accessing Django admin and that's a problem.
def get_current_site(request: HttpRequest = None):
from django.contrib.sites.models import Site
host = request.get_host()
extract = tldextract.extract(host)
domain = extract.registered_domain # removes subdomain
return Site.objects.get(domain__iexact=domain)

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

django: redirect subdomain to another url

In Django in the urls.py file, how do I write a url redirect so that login.domain.com does a 301 redirect to domain.com/login? I'm looking for a way to redirect a subdomain to a url. I realize this can be handled using ningx, however, I'd like to be able to maintain it in Django.
The 3rd party apps usually put this functionality into Middleware and use the process_request hook to take care of subdomain recoginition and then perform appropriate redirect.
Example displaying custom middleware and use of process_request from django-subdomains
class SubdomainMiddleware(object):
"""
A middleware class that adds a ``subdomain`` attribute to the current request.
"""
def get_domain_for_request(self, request):
"""
Returns the domain that will be used to identify the subdomain part
for this request.
"""
return get_domain()
def process_request(self, request):
"""
Adds a ``subdomain`` attribute to the ``request`` parameter.
"""
domain, host = map(lower,
(self.get_domain_for_request(request), request.get_host()))
pattern = r'^(?:(?P<subdomain>.*?)\.)?%s(?::.*)?$' % re.escape(domain)
matches = re.match(pattern, host)
if matches:
request.subdomain = matches.group('subdomain')
else:
request.subdomain = None
logger.warning('The host %s does not belong to the domain %s, '
'unable to identify the subdomain for this request',
request.get_host(), domain)