Redirecting legacy root URL in django - django

I'm trying to permanently redirect a legacy root URL to the new location in Django (hosted on Red Hat Openshift).
I've tried this solution but can't get it to work (even if the simplest case of http and without a further path). I'm not experienced with wsgi as you can probably guess and all help is very appreciated.
Here's my attempt to edit the last part of wsgi.py (redirecting from www.olddomain.com to www.newdomain.com). When I try to deploy it, trying to reach www.olddomain.com results in a error ("Can't reach this page"):
...
from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application()
def application(environ, start_response):
if environ['HTTP_HOST'][:17] == 'www.olddomain.com':
start_response('301 Redirect', [('Location', 'http://www.newdomain.com/'),])
return []
return _application(environ, start_response)
Thank you for your help

Check your indentation level. Also, ensure that the value of the HTTP_HOST environment variable is the old domain.
from django.core.handlers.wsgi import WSGIHandler
_application = WSGIHandler()
def application(environ, start_response):
if environ['HTTP_HOST'][:21] != 'www.example.com':
start_response('301 Redirect', [
('Location', 'http://www.example.com/'),
])
return []
return _application(environ, start_response)

Related

Is there a way could deploy Django project on namecheap in ASGI mode?

I'm trying to deploy my Django on Namecheap, the server should work but I got the following message traceback:
[ERROR] [UID:12123][2655752] wsgiAppHandler pApp->start_response() return NULL.
Traceback (most recent call last):
File "/home/alshigpf/website/passenger_wsgi.py", line 41, in __call__
return self.app(environ, start_response)
TypeError: __call__() missing 1 required positional argument: 'send'
[ERROR] [UID:12123][2655752] wsgiAppHandler pApp->start_response() return NULL.
Traceback (most recent call last):
File "/home/alshigpf/website/passenger_wsgi.py", line 41, in __call__
return self.app(environ, start_response)
TypeError: __call__() missing 1 required positional argument: 'send'
This is what my passenger_wsgi.py file looks like:
import os
import sys
import django
sys.path.append(os.getcwd())
os.environ['DJANGO_SETTINGS_MODULE'] = 'website.settings.development'
django.setup()
SCRIPT_NAME = os.getcwd()
print("Script name: ", SCRIPT_NAME)
class PassengerPathInfoFix(object):
"""
Sets PATH_INFO from REQUEST_URI because Passenger doesn't provide it.
"""
def __init__(self, app):
self.app = app
print("Start Init")
print("App is: ", self.app)
print("End Init")
def __call__(self, environ, start_response):
from urllib.parse import unquote
print("Start Calling")
environ['SCRIPT_NAME'] = SCRIPT_NAME
request_uri = unquote(environ['REQUEST_URI'])
script_name = unquote(environ.get('SCRIPT_NAME', ''))
offset = request_uri.startswith(script_name) and len(environ['SCRIPT_NAME']) or 0
environ['PATH_INFO'] = request_uri[offset:].split('?', 1)[0]
return self.app(environ, start_response)
# Get ASGI application
from website.routing import application
print("Before calling application")
application = PassengerPathInfoFix(application)
when I googled I glanced that there's something called "Uvicorn" which is an updated version of Gunicorn to handle ASGI mode.
when I did an advanced search on google to see if namecheap.com talk about using uvicorn I didn't find anything related to this problem.
so, my question is: how can I ignore this issue by still using ASGI mode instead of WSGI?

How to specify environment variables in Apache site config for Django?

How do you specify environment variables for an Apache site?
I have a multi-tenant Django site being served by Apache+ModWSGI. Each site uses the same Django code, but their settings differ slightly. An environment variable tells Django which settings to load. Currently, I'm getting a separate wsgi_<site_name>.py file, containing the variable appropriate for each site, but this violate the DRY rule. I'd like to, instead, put this variable in the Apache site config, and use a single wsgi.py file for all sites.
I can't find much info on how to do this. I found this one old blog post suggesting I could use the Apache config syntax:
SetEnv SITE somename
along with a wsgi file like:
import os, sys
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.abspath(os.path.join(BASE_DIR, '..'))
sys.path.append(PROJECT_DIR)
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'
from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application()
def application(environ, start_response):
os.environ['SITE'] = environ['SITE']
return _application(environ, start_response)
but this has no effect. If I print out the SITE variable, Django never receives the value "somename" and displays the default value.
As far as I can tell the mod_env component is still supported, and I've confirmed it's both installed and enabled on my server.
The problem was that I was calling get_wsgi_application() too early. The correct code is:
from django.core.wsgi import get_wsgi_application
def application(environ, start_response):
os.environ['SITE'] = environ['SITE']
_application = get_wsgi_application()
return _application(environ, start_response)

Passing environment variables from apache via mod_wsgi to use in django 1.11 settings

Found a few versions of this question, such as Django get environment variables from apache, however the advice I've found so far doesn't seem to work with the latest LTS django (1.11).
I have an apache configuration which holds a number of environment variables, not limited to connection credentials for the DB. Im using this to make my code portable between dev/prod etc.
My apache conf just uses SetEnv to pass in some variables.
I've tried two different styles of approach to use these variables, both seem to suffer from the same issue; it needs to read the settings file before we can write to the environment, and the settings file requires values from the environment.
My two variants are;
import os
import django
from django.core.handlers.wsgi import WSGIHandler
from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application()
def application(environ, start_response):
for key in [keys...]:
if environ.get(key):
os.environ[key] = environ.get(key)
return _application(environ, start_response)
and
import os
import django
from django.core.handlers.wsgi import WSGIHandler
class WSGIEnvironment(WSGIHandler):
def __call__(self, environ, start_response):
for key in [keys...]:
if environ.has_key(key):
print "Key: %s = %s" % (key,environ[key])
os.environ[key] = environ[key]
return super(WSGIEnvironment, self).__call__(environ, start_response)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'apiClient.settings')
django.setup(set_prefix=False)
application = WSGIEnvironment()
Either way im trying to use the values in settings as;
KEY = "thing"
if os.environ.has_key("KEY"):
KEY = os.environ["KEY"]
You can't use SetEnv as the settings file is evaluated before the first request is handled. Setting environment variables from per request WSGI environ values was always a bad idea and can cause problems, so you shouldn't do that anyway.
Result is that you cannot set environment variables from the Apache configuration file. Set them from the WSGI script file. If they are things that should not be added to a Git repository, create a file on the specific host with the values in some format, and have the WSGI script file read that file from the local host to set them when first loaded and before any Django code is executed.
For Django > 1.7 get_wsgi_application calls django.setup() which initializes the settings. So if your env vars aren't set at that point you won't see them in the settings.
To get around it, don't actually call get_wsgi_application until you're ready. This works for me in wsgi.py:
def application(environ, start_response):
os.environ['APPLICATION_ENV'] = environ.get('APPLICATION_ENV', None)
return get_wsgi_application()(environ, start_response)
You have to defer initialising the Django app until the first request. Something like this in your wsgi.py:
from django.core.wsgi import get_wsgi_application
_application = None
def application(environ, start_response):
global _application
if _application == None:
for key in environ:
if key.startswith('ENV_'):
os.environ[key[4:]] = environ[key]
_application = get_wsgi_application()
return _application(environ, start_response)
It's a pity that there appears to be no option in mod_wsgi to set the initial environment when starting the daemon process, as you can with mod_fastcgi.

Failing functional test on GAE, Django, WebTest

I've got a GAE/Django project and I'm trying to make a functional-test enviroment work with WebTest, the project layout is as follows:
/gaeroot
/djangoroot
wsgi.py
urls.py
...
/anapp
urls.py
...
/tests
test_functional.py
wsgi.py (generated by GAE's version of django-admin.py django1.5):
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoroot.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
test_functional.py:
import unittest
import webtest
from djangoroot.wsgi import application
class TestHomePage(unittest.TestCase):
def setUp(self):
self.testapp = webtest.TestApp(application)
def test_get_method_ok(self):
response = self.testapp.get('/path')
self.assertEqual(response.status_int, 200, response)
The failing test message:
Traceback (most recent call last):
...
line 14, in test_get_method_ok
self.assertEqual(response.status_int, 200, response)
AssertionError: Response: 301 MOVED PERMANENTLY
Content-Type: text/html; charset=utf-8
Location: http://localhost:80/path/
Why is throwing a redirect code to the same path, the only thing I can think of is that some code of django is responsible for the redirection because as you can see from the directory tree I have a two level url configuration.
On the other side, why is using port 80? when I test it on the browser it shows an 8080 port, and it shouldn't use a port at all since WebTest it's supposed not to use a port at all since it's testing the WSGI interface right?.
Top level urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^path/', include('djangoroot.anapp.urls')),
)
App level urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns('djangoroot.anapp.views',
url(r'^$', 'home', name='anapp_home'),
)
The browser shows the corret page on the same url, I took the WebTest example from google's support pages, so the problem should be the GAE/Django interop.
Thanks in advance and let me know if you need more info.
The problem seems to be on the django.conf.urls.url function since I tested the root urls.py file and it worked for the root path / with no redirection, but it did redirected me with a path other than the root, I could find nothing that seemed being redirecting my urls on the Django source files.
I found an alternative on the Webtest documentation:
resp = self.testapp.get('/path')
resp = resp.maybe_follow()
with the maybe_follow method you eventually get the final page.
Edit
Finally I found the problem in this line:
response = self.testapp.get('/path')
replace it with this:
response = self.testapp.get('/path/')
It looks like Django redirects the urls to the propper path with the / at the end.

Deploying Django project using mod_wsgi and virtualenv

Trying to deploy a Django 1.4 project using mod_wsgi and virtualenv, i'm running into a 500. The Apache error_log reveals:
mod_wsgi (pid=30452): Exception occurred processing WSGI script '/path/to/project/site-packages/projectapp/wsgi.py'.
[...] Traceback (most recent call last):
[...] File "/path/to/project/env/myenv/lib/python2.6/site-packages/django/core/handlers/wsgi.py", line 219, in __call__
[...] self.load_middleware()
[...] File "/path/to/project/env/myenv/lib/python2.6/site-packages/django/core/handlers/base.py", line 47, in load_middleware
[...] raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e))
[...] ImproperlyConfigured: Error importing middleware projectapp.content.middleware: "cannot import name SomeModel"
From the error message i would expect that this is some kind of a path issue. However, when the offending middleware is removed from the Django settings, the site launches just fine, and there are other modules loaded from projectapp.content, SomeModel is also loaded in this case, otherwise the whole site wouldn't be able to run.
The import error raised doesn't come directly from the middleware, as it doesn't import the model. SomeModel is defined in a speparate app which is actually checked out into the src directory of the virtualenv. But the directory containing this app is also in the path.
The wsgi.py file i'm using:
import os
import sys
sys.stdout = sys.stderr
sys.path.insert(0, '/path/to/project/env/myenv/lib/python2.6/site-packages/')
# The module inside the following directory
# defines SomeModel from the error message
sys.path.insert(0, '/path/to/project/env/myenv/src/some-app/')
sys.path.insert(0, '/path/to/project/site-packages/')
import django.core.handlers.wsgi
os.environ['DJANGO_SETTINGS_MODULE'] = 'projectapp.settings'
application = django.core.handlers.wsgi.WSGIHandler()
Printing sys.path after inserting the module paths shows everything in the expected order, /path/to/project/site-packages/ is listed first and /path/to/project/env/myenv/src/some-app/ (which defines SomeModel) second.
I've also tried a different variant, based on the example from the mod_wsgi docs:
import os
import sys
import site
ALLDIRS = [
'/path/to/project/site-packages/',
'/path/to/project/env/myenv/lib/python2.6/site-packages/',
'/path/to/project/env/myenv/src/some-app/',
]
# Remember original sys.path
prev_sys_path = list(sys.path)
sys.stdout = sys.stderr
# Add each new site-packages directory
for directory in ALLDIRS:
site.addsitedir(directory)
# Reorder sys.path so new directories are at the front
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
#activate_this = '/path/to/project/env/myenv/bin/activate_this.py'
#execfile(activate_this, dict(__file__=activate_this))
import django.core.handlers.wsgi
os.environ['DJANGO_SETTINGS_MODULE'] = 'projectapp.settings'
application = django.core.handlers.wsgi.WSGIHandler()
The error logged by Apache is exactly the same.
I have to add that the middleware is loaded just fine when i'm running the development server on the same machine, so i have no idea what's going wrong. Is there a way to get a better traceback from mod_wsgi?
EDIT:
Fogot to mention that i'm using mod_wsgi in daemon mode. Here are the relevant parts from my vhost config:
<VirtualHost x.x.x.x:80>
WSGIDaemonProcess foo user=foo threads=10 umask=0002
WSGIProcessGroup foo
WSGIScriptAlias / /path/to/project/site-packages/projectapp/wsgi.py
</VirtualHost>
Okay after hours of debugging this turned out to be a race condition caused by the middleware. The middleware i'm using is similiar to the FlatpageFallbackMiddleware from Django contrib and actually the import of the view caused the problem.
from projectapp.content.views import generic_content_detail
class ContentFallbackMiddleware(object):
def process_response(self, request, response):
[...]
Moving the import statement inside the process_response method solved the problem for me:
class ContentFallbackMiddleware(object):
def process_response(self, request, response):
from projectapp.content.views import generic_content_detail
[...]