Django dynamic settings infrastructure and best practices - django

Django settings includes a list of python variables that are used for a plethora of things from database settings to installed apps. Even many of the reusable apps make some of the settings required.
With a dozens of sites, it is hard to manage the settings of all the projects.
Fortunately settings is just a python module with variables, so you can do any magic to populate the variables you want.
What practices have you followed or you think can be used to separate various related settings into different files?
Apparently, the existing enterprisey practice is that a developer creates a war and the ops department slaps it to the bluefish and takes care of all the database(and such) ops stuff (according to Jacob's email).
What dynamic settings.py can you create that will aid the existing enterprise practices?

Often I've seen settings files with something like:
from localsettings import *
and in localsettings.py things like database connections and DEBUG values are defined. localsettings.py is (or may be) different for each deployment environment (dev/staging/production etc), and doesn't live in source control with everything else.
Something I've found helpful lately is putting this in my settings.py:
try:
from localsettings import *
except ImportError:
from default_localsettings import *
in default_localsettings.py I define a bunch of defaults (DEBUG = True, use a sqlite database in the same directory as default_localsettings.py etc).
This might not be useful once you've got things set up, but I've found it useful just to be able to check my project out of source control and have it work straightaway using runserver, without having to set anything up.

Follow this settings override example to handle dev, staging, and production environments.
http://djangodose.com/articles/2009/09/handling-development-staging-and-production-enviro/
(archived version at Wayback Machine)

Personally, I like it to make a module out of settings like
project/
settings/
__init__.py
settings.py
local.py
The file __init__.py looks like:
from .settings import *
from .local import *
You have adopt the variable BASE_DIR to point to the directory one level higher.
Using this procedure, you don't have to adopt anything else like the import mechanism. Just use from django.conf import settings. All settings in local.py overwrite the ones in settings.py.
However, this possible solution may be combined with the other answers given above.

Related

Where can I keep settings data for a reusable app

Django=3.1
I'd like to push some utilites of mine, template tags, filters etc. to a reusable app.
But this app will needs a huge amount of settings. Maybe 100-200 lines.
I'm thinking of placing a file called something "my_omnibus.py" next to project's settings.py
Is a good idea? If it is, could you tell me how to import that file into the reusable app if the name of the project may change.
If it is not, where can I keep the constants?
In your app's root directory, create a settings.py and dump all settings there. In that app, use relative imports e.g. from .settings import SETTING_A.
You shouldn't create settings next to project's settings.py module because those settings are only relevant to one app. You should only do such a thing only if those settings are going to be used by all apps (an example would be celery application configuration).

Multiple websites using the same app - how to set this up?

I have an app that shows products available in the US. If i want to change the country, I simply modify the value of a variable in my settings.py file.
Now... each country I serve needs to have its own site, e.g. example.co.uk, example.ca, etc. They'll all be hosted on the same server and use the same database. The views, static files,etc. would be almost the same for each country.
What's the best way of setting this up? Should I have one main app and then have per-country apps that extend the app?
(Using Django 1.6.2/Python 2.7)
I recently had something similar to do.
I have for each domain a specific setting file with an unique SITE_ID and also a wsgi file per site. Then in my http.conf (I'm using apache on webfaction) i set up multiple VirtualHost instances, each pointing out to the specific wsgi file.
My configuration looks something like this:
random_django_app/
__init__.py
models.py
...
another_app/
...
settings_app/
settings/
__init__.py
base.py
example_co_uk.py
example_ca.py
...
wsgis/
__init__.py
example_co_uk.py
example_ca.py
__init__.py
urls.py
Maybe try django-dynamicsites, not sure how well it will work with 1.6.2, I've used it on a project a few versions back. It worked great, you could override settings, urls and templates for each site and share everything else.
There is another project called django-dynamicsites-lite that looks a bit more recent.

How to alter django settings based on current request?

I'm running multiple sites from a single django instance, and expect the framework to eventually serve several hundred sites from one or several installations.
I need to patch some django settings based on the current request. I've written some middleware to monkey patch the settings, but I need these settings to be patched before the middleware gets invoked because other apps aren't taking the monkey-patched changes (i.e. apps get run then middleware gets run so the apps don't use the monkey-patched settings).
I should also add this is mainly for the benefit of third-party apps that I haven't written, so I don't want to go round adding decorators or anything like that because that would mess up my upgrade path.
So:
How can I get access to the current request in an app's init.py file?
Will an app's init.py get called once per request or only once? If it's only once, how else could I do this so I can manipulate the settings once per request?
Is it safe to do this kind of monkey patching? I know it makes code a bit more opaque, but I don't want to use different wsgi files per site because I want to allow users to edit some of these settings and have my monkey patching come from the database.
Is there a better solution that would allow certain settings to be stored in the database?
This module - django-tupperware does what you are asking about: https://bitbucket.org/jiaaro/django-tupperware/
Give it a try.
Never ever ever change settings on the fly. You cannot predict how the application may one day be deployed, and in most of them the project will fail in.. interesting ways.
If you really want to have hundreds of sites which will be dynamically reconfigured all the time, you might try to Djangos django/conf/__init__.py which is used to supply the settings to the rest of the framework. You might be able to alter it in a way it depends on the request and it's Host field. Or you'll get many interesting failures at the very least.
My solution to this problem is to give each request, ALL the settings and update them on the fly with a middleware, how do i do this?
Is rather simple:
This is the middleware that does it all
from django.conf import settings
class DumpObject: pass
class Settings(object):
def process_request(self,request):
request.settings = DumpObject()
for setting in dir(settings):
if not setting.startswith('__'):
setattr(request.settings, setting, getattr(settings,setting))
The DumpObject is just there so i can use the object.property notation, it could have been a dictionary, but i wanted to keep some similarity in the syntax.
This assumes that none of your settings name starts with __ which is a fair assumption.
So if i want to override a particular setting, i don't keep a settings file i do it in this middleware. like this:
class Settings(object):
def process_request(self,request):
request.settings = DumpObject()
for setting in dir(settings):
if not setting.startswith('__'):
setattr(request.settings, setting, getattr(settings,setting))
if 'mydomain' in str(request.host): #this is thanks to django-hosts project
request.settings.GOOGLE_ANALYTICS_ID = '89298393-238'
Of course this doesnt take into account the problem of accessing the settings the old way
from django.conf import settings
settings.GOOGLE_ANALYTICS_ID = 'the value in settings.py'
But it really doesn't matter because you will probably only want to change the settings on the context of having a request object, available.

What gets executed as the server starts vs. as a request comes in?

I've been doing some class hacking in Django. And I call my changes from settings.py as I figure this runs before anything else.
However, I've just come across a situation where this doesn't work for me. So, is there another place after settings.py, which I can guarantee will always be run by the server before it starts handling any requests?
You should never put code in settings.py that requires importing anything from any part of Django. Since many parts of Django require settings to be available, this is very likely to get you into circular import problems.
Your ROOT_URLCONF (urls.py) is a reasonable place to put project-level code that you want run once for each server Python process, before any requests are served.
If the code is specific to a particular app (and only needed if that app is in use) then you could put it in that app's models.py or __init__.py.
For a broader look at the issue, see this blog post.
I'm not sure exactly what you mean by 'class hacking' but have you tried calling your changes from ./manage.py?
From the docs:
In addition, manage.py is
automatically created in each Django
project. manage.py is a thin wrapper
around django-admin.py that takes care
of two things for you before
delegating to django-admin.py:
It puts your project’s package on
sys.path. It sets the
DJANGO_SETTINGS_MODULE environment
variable so that it points to your
project’s settings.py file.
so if you have some stuff that you want to run after settings.py this might be the case if its a hack you're after.
HTH
Something like the request_started signal?
If you want to put code somewhere in your django project that will get run for certain every time you start up django, pick an app form INSTALLED_APPS. Both the __init__.py and the models.py will be run for sure. They are good places for things like signals or anything you must register.

How do you setup a Django project with different sites using the same data?

I'm currently looking at the the documentation for Django sites:
http://docs.djangoproject.com/en/dev/ref/contrib/sites/#ref-contrib-sites
which explains how to associate content with multiple sites. The example used is LJWorld.com and Lawrence.com.
What does the Django project structure look like for the above? Is each site an app on its own, for instance:
project/
manage.py
settings.py
urls.py
ljworld/
models.py
views.py
lawrence/
models.py
views.py
If ljworld has SITE_ID=1 and lawrence has SITE_ID=2, does the SITE_ID variable has to be explicitly set in ljworld/settings.py and lawrence/settings.py?
How do you run the dev server of either ljworld or lawrence?
Update:
I used two sites with shared content in the above. What should be done if there are n different sites who are sharing the same content? Do I really need n different Django projects on n different servers, all connected to the same database server?
Moreover, if I need to make a change in settings.py which should affect all those web sites, it will be very tedious to change each of those files manually.
No, each site is not an app on its own; each site is a project on its own. The whole idea is to have different projects with a (fully or partially) shared content. So you might have a structure such as:
ljworld/
manage.py
settings.py
urls.py
ljworld_specific_app1/
...
lawrence/
manage.py
settings.py
urls.py
lawrence_specific_app1/
You would normally use two Web servers to serve the projects - though normally both would refer to the same DB server. Naturally you can also have apps which are shared between the two projects - just keep them somewhere in the server's PYTHONPATH.
Edit:
"Two Web servers" of course doesn't necessarily mean two physically different servers. They could well be two virtual hosts running under the same Web server instance - heck, you could even map the two projects to two different directories under the same virtual host.
For shared settings, you could use the same technique as for shared apps. Have a global_settings module which contains the shared settings available somewhere on the PYTHONPATH and import it from each of the settings.py.
And if you wanted something really hackish, you could probably even drop all the different projects, use just one and create a middleware that changes settings on the fly. But I would advise against it.