Django on Twisted with multiple virtual hosts? - django

I have a django website that I am hosting on twisted via the django WSGIHandler as described here - http://www.clemesha.org/blog/Django-on-Twisted-using-latest-twisted-web-wsgi
All seems OK up to the point where I want to add an extra "site" configuration to my django site using the django Sites framework. Doing so, I add an extra settings.py file for the new site and that seems to work.
What I then want to do is use the twisted NameVirtualHost class to be able to direct one domain (say site1.example.com) to the first settings file, then use another domain (say site2.example.com) to use the second settings file. This works with Apache & mod_wsgi.
The problem I face is that the twisted code can only access one django environment at a time. If I call setup_environ with the first settings file and setup a host for the first domain, a subsequent call to setup_environ will replace the settings file in use so therefore only one set of settings can be used at one time.
Any ideas how to proceed?

Gave up on this in the end. Looks like you cannot easily access 2 Django environments within the same twisted instance. I think it would require multiple instances of twisted with a reverse proxy or some sort of multiprocess hacking - either way its not worth the effort for me so I'm going to try something else...

Related

Self-hosting multiple sites via one Django project + apache - Django Sites framework

I have a self-hosted (raspberry pi) website running on Django - one project with several apps all serving on one domain name. I'm more of a programmer than a server admin, but it works! Now, I'd like to break one of those apps out onto a second domain name, but still hosting from my pi.
--I've looked into a lot of tutorials on how to do this as well as checked out answers like this one but the server-related instructions on everything I've found so far all go a bit over my head or don't seem to match up with my setup.
--I've tried the Django Sites framework and I've also followed this tutorial. While I was able to get my site objects in the db, all requests seem just to go to SITE_ID=1, regardless of domain name. I've seen conflicting information here about whether or not SITE_ID should be present in settings.py, but whenever I remove it, I just get errors about it (even though I do have the CurrentSiteMiddleware installed as well as a custom middleware like indicated in that tutorial).
Here's what I've got for Django (3.2.14, and py3.7.3), essentially:
DjangoApps
-mysite
--settings.py (allowed_hosts includes mydomain)
--urls.py (imports from app urls files)
--(etc)
-app1
--urls.py
--(etc)
-app2
--urls.py
--(etc)
-app3
--urls.py
--(etc)
and then the server confs:
/etc/apache2/sites-available/
-000-default.conf
-default-ssl.conf
-mydomain.com.conf [which points to the DjangoApps directory]
-mydomain.com-le-ssl.conf
I do not have an http.conf as mentioned in many of the tutorials/answers I've read through. I've seen some notes about using virtualenv and/or wsgi but I don't understand them, and I am requesting a more handholding step-by-step explanation of what to do here.
I assume this isn't the most optimal/modern way of doing this, but the key point here is that this main site is already live and working as-is so I'm really not looking to redo the whole thing. All I want to do is make it so that myseconddomain.com serves app3 only, and mydomain.com keeps serving everything (or, everything but app3, if that's easier).
Please help! And please let me know what other info is required.

How to do site specific configuration with Python Django

At the moment i run some django sites with site specific config by using multiple settings.py with different SITE_ID in it and application specifig APP_OPTION="foo" settings.
Now i would like to run the sites from one process selecting the site by domain (which is done by get_current_site). But in this case they share the global settings.
One option would be of course to patch the applications to use APP_OPTION[site_id]="foo", another option would be to place the settings in the database. Both seem not to be the best solutions to the problem.
Which is the most elegant way to have such site specific config values?

How do I make Django figure out which Site object to use based on "Host" header in the HTTP request?

Consider a Django app built to serve multiple sites with slightly differing content using the standard Django sitesframework.
The traditional way to host this would be to configure multiple Site objects and setup the app in multiple Django projects with each project pointing to a different SITE_ID in their respective settings.py:s.
For various reasons I'd like to avoid having to create a new project for each new site. I want to be able to setup one project and have Django figure out which Site object to use based on the hostname referenced in the incoming HTTP request.
What is the recommended way to achieve this functionality?
Clarification: I want the site framework to ignore settings.SITE_ID (which is hard-coded in settings.py) and instead dynamically fetch Site objects based on what is in the Host header. Why this requirement? I'll be adding and removing sites multiple times per hour and the total amount of sites will exceed 10,000, so setting up a Django project for each site is not an option. Is this a problem that is solvable in Django? If so, what is the best way to achieve it?
The recommended way is to not attempt it at all, since settings should never change at runtime. Instead, set a variable in your virtual host configuration and have the WSGI adapter script or settings module pick one of the sites based on that.

How do you Require Login for Media Files in Django

I'm serving "sensitive" information in downloadable PDF's and Spreadsheets within a user registration section of a site.
Is there a way to allow the django authentication to secure this media without serving it (and not have to manually login using basic auth)?
I'm guessing theres (fingers crossed) not a way to do it with the psuedo code below, but it helps better illustrate the end goal.
#urls.py
(r'^protected_media/(?P<filename>.*)$', 'protected_media')
#views.py
from django.contrib.auth.decorators import login_required
#login_required
def protected_media(request, filename):
# #login_required bounces you out to the login url
# if logged in, serve "filename" from Apache
It seems to me that the method you outlined in your code should work. It's really no different than any other protected resource: your views can serve files from disks, records from databases, rendered templates or anything. Just as the login_required decorator prevents unauthorized access to other views, it will prevent such access to your view serving protected media.
Am I missing something from your question here? Please clarify if that's the case.
EDIT: With regard to the django doc link in your comment: that's the method for simply serving any request file from a particular directory. So, in that example URLS like /site_media/foo.jpg, /site_media/somefolder/bar.jpg will automatically look for files foo.jpg and somefolder/bar.jpg under document_root. Basically, every thing under document_root will be publicly available. That's obviously insecure. So you avoid that with your method.
It's also considered inefficient because django is just adding a lot of unnecessary overhead when all you need is something like Apache to take a URL request and map it to a file on the hard drive. (You don't need django sessions, request processing, etc.)
In your case, this may not be such a big concern. First, you've secured the view. Second, it depends on your usage patterns. How many requests do you anticipate for these files? You're only using django for authentication -- does that justify other overhead? If not, you can look into serving those files with Apache and using an authentication provider. For more on this, see the mod_wsgi documentation:
http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
see the section "Apache Authentication Provider" and search for django
There are similar mechanisms available under mod_python I believe. (Update: just noticed the other answer. Please see Andre's answer for the mod_python method.)
EDIT 2: With regard to the code for serving a file, please see this snippet:
http://www.djangosnippets.org/snippets/365/
The send_file method uses a FileWrapper which is good for sending large static files back (it doesn't read the entire file into memory). You would need to change the content_type depending on the type of file you're sending (pdf, jpg, etc).
Read this Django ticket for more info. Start at the bottom to save yourself some time. Looks like it just missed getting into Django 1.2, and I assume also isn't in 1.3.
For Nginx, I found this Django snippet that takes advantage of the X-Accel-Redirect header, but haven't tried it yet.
If I understand your question correctly you want to restrict access to files that are not being served by Django, for example, with an Apache server?
What you would then require is some way for this Apache server to use Django as an authentication source.
This django snippet describes such a method. It creates an access handler in Django which is used by Apache when a request for a static file comes in that needs to be protected:
<Location "/protected/location">
PythonPath "['/path/to/proj/'] + sys.path"
PythonOption DJANGO_SETTINGS_MODULE myproj.settings
PythonOption DjangoPermissionName '<permission.codename>'
PythonAccessHandler my_proj.modpython #this should point to accesshandler
SetHandler None
</Location>
Hope this helps, the snippet was posted a while ago, so things might have changed between Django versions :)
More efficient serving of static files through Django is being looked at currently as part of Google SOC project. For WSGI this will use wsgi.file_wrapper extensions for WSGI if available, as it is for mod_wsgi, and req.sendfile() if using mod_python. It will also support returning of headers such as 'Location', 'X-Accel-Redirect' and others, which different web hosting mechanisms and proxy front ends accept as a means of serving up static files where location is defined by a backend web application, which isn't as effecient as front end for serving static files.
I am not sure if there is a project page for this in Django wiki somewhere or not, but the code changes are being committed into the branches/soc2009/http-wsgi-improvements branch of Django source code repository.
You needn't strictly wait for that stuff. It is just putting a clean and portable interface in place across the different mechanisms. If using nginx as front end in front of Apache/mod_wsgi, you could use X-Accel-Redirect now. If using Apache/mod_wsgi 3.0 and daemon mode, you could use Location now, but do need to ensure you set up Apache correct. Alternatively, you could implement your own WSGI middleware wrapper around the Django application which looks for some response header of your own to indicate file to be returned and which uses wsgi.file_wrapper to return that instead of actual response returned from Django.
BTW, the authentication hook mechanisms listed for both mod_python and mod_wsgi by others would use HTTP basic authentication, which isn't what you wanted. This is presuming you want files to be protected by Django form based login mechanism using cookies and backend sessions.

How to locally test Django's Sites Framework

Django has the sites framework to support multiple web site hosting from a single Django installation.
EDIT (below is an incorrect assumption of the system)
I understand that middleware sets the settings.SITE_ID value based on a lookup/cache of the request domain.
ENDEDIT
But when testing locally, I'm at http://127.0.0.1:8000/, not http://my-actual-domain.com/
How do I locally view my different sites during development?
Create a separate settings.py file for every site, including an appropriate SITE_ID setting. Of course you can use the import statement to share common setting between files.
From now on, when running Django development server specify the --settings option to tell Django which site to run.
For example (assuming you've got two setting files - settings_first.py and settings_second.py):
manage.py runserver --settings settings_first
will run the first site, and
manage.py runserver --settings settings_second
will give you an access to the second site.
You can also run them simultaneously, specifying different ports:
manage.py runserver 8001 --settings settings_first
manage.py runserver 8002 --settings settings_second
The above commands (run on two different consoles) will make the first website accesible under http://127.0.0.1:8001/, and the second one under http://127.0.0.1:8002/
Maybe you are mislead by the documentation. You wrote:
I understand that middleware sets the settings.SITE_ID value based on a lookup/cache of the request domain.
This is not the case. It works exactly the other way around. Django uses the settings.SITE_ID value to look up the correct Site object in the database. This returns your prefered domain and site name.
The sites application was designed to fill the (in my opinion) rare usecase that you want to have multiple sites with the same database in the background. This allows you to publish the same articles on different sites but still have the flexibility that some models are available just for a single site.
For developing multiple projects (that doesn't actually make use of the sites framework) you don't have to specify anything special. You can use the default SITE_ID set to 1. For utilizing the admin's view on website links you can set in your development database the Site's domain to localhost:8000.
If you want to develop multiple sites using the same database (and make use of the sites framework) you must have each project with a distinct SITE_ID but the same database settings. The values for SITE_ID in each project on your development machine are in most cases the same as for your production servers.
FYI - I released django-dynamicsites today which has facilities to solve this issue - https://bitbucket.org/uysrc/django-dynamicsites/src