In my project (based on Django) I need a custom option in order to switch on/off some functionality in my view.
if (FLAG):
.....
else:
.....
As I know, if I set this variable in settings.py I wouldn't have imported it from settings.py, because settings.py is not a module and I need import settings.py as a whole object. What else can I use as a setting variable in Django?
You can import as you wish like
from django.conf import settings
settings.py
FUNCTIONALITY_A = True
Then in views.py
If settings.FUNCTIONALITY_A == True
#do your stuff
Otherwise
from yourproject import settings
You can use request.session. It stores the values as dictionary
request.session['flag'] = True #or False as per your requirement
...
if (request.session['flag']):
...
else:
...
When you are done using this variable, just delete it using
del request.session['flag']
Create custom setting YOUR_SETTING, place in settings.py. In your views you can use settings like this
from django.conf import settings
...
# in view
if settings.YOUR_SETTING:
# do something
More info.
Related
How could I override or change the project's settings from an app? For example, I have a context processor function in my app, and I want to include it into a context_processors option of the TEMPLATE setting. How could I do that without changing project's settings.py file directly?
In the documentation i found, that they cannot be changed at runtime:
You shouldn’t alter settings in your applications at runtime. For
example, don’t do this in a view:
from django.conf import settings
settings.DEBUG = True # Don't do this!
The only place you should assign to settings is in a settings file.
So is it possible at all?
I keep getting the following error: name 'STATIC_DIRS' is not defined, when I try to access it in a view from my settings.py. What do I import to make this work in my views?
You can import the following:
from django.conf import settings
And then you can access variables set in your settings file by doing settings.variable
Is it possible to dynamically load Django apps at runtime? Usually, apps are loaded at initialization, using the INSTALLED_APPS tuple in settings.py. However, is it possible to load additional apps at runtime? I am encountering this issue in different situations. One situation, for example, arises during testing, when I would like to dynamically load or unload apps.
In order to make the problem more concrete, imagine I have a directory called apps where I put my apps and I would like to automatically install any new app that goes in there without manually editing the settings.py.
This is easy enough. Following the example code in
Django: Dynamically add apps as plugin, building urls and other settings automatically
we put the following code in settings.py to could loop over the names of all sub-directories in the app directory and increment the INSTALLED_APPS tuple in settings.py like this:
APPS_DIR = '/path_to/apps/'
for item in os.listdir(APPS_DIR):
if os.path.isdir(os.path.join(APPS_DIR, item)):
app_name = 'apps.%s' % item
if app_name not in INSTALLED_APPS:
INSTALLED_APPS += (app_name, )
After that, if I was in a Django shell, I could something like
from django.conf import settings
and the apps would be listed in settings.INSTALLED_APPS. And if I did
from django.core import management
management.call_command('syncdb', interactive=False)
that would create the necessary DB tables for the apps.
However, if I were to now add some more apps to the apps/ directory, without re-starting, these would not be listed in settings.INSTALLED_APPS, and so a subsequent call to the syncdb would have no effect.
What I would like to know is if there is something I could do --- without restarting --- to reload the settings and load/install new apps.
I have tried to directly import my settings.py, i.e.
from myproject import settings
and then reload that settings using the python builtin after any app directory changes. Although settings.INSTALLED_APPS is now changed to include the newly added apps, this ultimately makes no difference. For example,
from django.db import models
models.get_apps()
shows only the original apps in apps and not the newly added ones and likewise
management.call_command('syncdb', interactive=False)
will not see the newly added apps.
As I stated above, I am thinking about this situation particularly in the context of testings where I dynamically would add or remove apps.
Ps. I working with Django 1.6, but on the advice of #RickyA, I see that there are some substantial changes to Django's treatment of applications in 1.7
https://docs.djangoproject.com/en/1.7/ref/applications/
I'm still not sure what this might mean for the problem I am facing.
Update for Django 1.8 on how to load an app that is not loaded yet
from collections import OrderedDict
from django.apps import apps
from django.conf import settings
from django.core import management
new_app_name = "my_new_app"
settings.INSTALLED_APPS += (new_app_name, )
# To load the new app let's reset app_configs, the dictionary
# with the configuration of loaded apps
apps.app_configs = OrderedDict()
# set ready to false so that populate will work
apps.ready = False
# re-initialize them all; is there a way to add just one without reloading them all?
apps.populate(settings.INSTALLED_APPS)
# now I can generate the migrations for the new app
management.call_command('makemigrations', new_app_name, interactive=False)
# and migrate it
management.call_command('migrate', new_app_name, interactive=False)
With Django 2.2 this work for me
from collections import OrderedDict
from django.apps import apps
from django.conf import settings
from django.core import management
new_app_name = "my_new_app"
settings.INSTALLED_APPS += (new_app_name, )
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
apps.clear_cache()
apps.populate(settings.INSTALLED_APPS)
management.call_command('makemigrations', new_app_name, interactive=False)
management.call_command('migrate', new_app_name, interactive=False)
To answer my own question...
While I do not have a completely general solution to this problem, I do have one that is sufficient for the purposes of dynamically loading apps during testing.
The basic solution is simple, and I found it at a wee little bixly blog.
Continuing with my example above, if I was in a django shell and wanted to add and load some new apps that were added to my apps directory, I could do
import os
from django.conf import settings
from django.db.models import loading
from django.core import management
APPS_DIR = '/path_to/apps/'
for item in os.listdir(APPS_DIR):
if os.path.isdir(os.path.join(APPS_DIR, item)):
app_name = 'apps.%s' % item
if app_name not in settings.INSTALLED_APPS:
settings.INSTALLED_APPS += (app_name, )
and then
loading.cache.loaded = False
management.call_command('syncdb', interactive=False)
It is possible to dynamically load and unload applications in tests in Django >= 1.7 (and also in the current 4.1) by a override_settings() decorator:
#override_settings(INSTALLED_APPS=[...]) # added or removed some apps
class MyTest(TestCase):
# some tests with these apps
def test_foo(self):
pass
It has been possible since September 2014, not before the question.
Many other topics from the question are solved by django.apps also in Django >= 1.7. Generally: Dynamic configuration at startup is easy in the current Django. Dynamic loading and unloading after startup can't be recommended in production, even that it could work eventually.
Yes! Anything (or almost everything) in Python is possible. You should use os.walk() in order to get all folders, subfolders and files within your apps path in order to get all of your apps including the nested ones.
def get_installed_apps():
from os import walk, chdir, getcwd
previous_path = getcwd()
master = []
APPS_ROOT_PATH = '/my/project/apps/folder'
chdir(APPS_ROOT_PATH)
for root, directories, files in walk(top=getcwd(), topdown=False):
for file in files:
if 'apps.py' in file:
app_path = f"{root.replace(BASE_DIR + '/', '').replace('/', '.')}.apps"
print(app_path)
master.append(app_path)
chdir(previous_path)
return master
print(get_installed_apps())
this is somewhat related to this question
Why is django's settings object a LazyObject?
In my django project i have several applications. Each application can have its own non-trivial settings file.
proj/
proj/
settings.py
app/
settings.py
views.py
What is the general best practice here?
should app/settings.py do
from django.conf import settings
APP_SETTING= lambda: settings.getattr('APP_SETTING', 'custom_value')
PROJ_SETTING= lambda: settings.PROJ_SETTING
and then in app/views.py do
import .settings
X = settings.APP_SETTING
Y = settings.PROJ_SETTING
or should I be modifying the django lazy settings object in app/settings.py as per the django coding style?
from django.conf import settings
# not even sure how I would check for a default value that was specified in proj/settings.py
settings.configure(APP_SETTING='custom_value')
and then each app/views.py just consumes proj/settings.py via django.conf settings?
from django.conf import settings
X = settings.APP_SETTING
Y = settings.PROJ_SETTING
There are obviously quite a few other permutations but I think my intent is clear.
Thanks in advance.
The simplest solution is to use the getattr(settings, 'MY_SETTING', 'my_default') trick that you mention youself. It can become a bit tedious to have to do this in multiple places, though.
Extra recommendation: use a per-app prefix like MYAPP_MY_SETTING.
There is a django app, however, that gets rid of the getattr and that handles the prefix for you. See http://django-appconf.readthedocs.org/en/latest/
Normally you create a conf.py per app with contents like this:
from django.conf import settings
from appconf import AppConf
class MyAppConf(AppConf):
SETTING_1 = "one"
SETTING_2 = (
"two",
)
And in your code:
from myapp.conf import settings
def my_view(request):
return settings.MYAPP_SETTINGS_1 # Note the handy prefix
Should you need to customize the setting in your site, a regular entry in your site's settings.py is all you need to do:
MYAPP_SETTINGS_1 = "four, four I say"
Since Django 1.7 there is a django-based structure for app-oriented configurations!
You could find descriptive solution here
In this new structure, conventionally you could have an apps.py file in your applications' folder which are embeded in project, something like this:
proj/
proj/
settings.py
app1/
apps.py
views.py
app2/
apps.py
views.py
app1/apps.py file could include something like this:
from django.apps import AppConfig
class App1Config(AppConfig):
# typical systemic configurations
name = 'app1'
verbose_name = 'First App'
# your desired configurations
OPTION_A = 'default_value'
APP_NAMESPACE = 'APP'
APP_OPTION_B = 4
you could have app2/apps.py something different like this:
from django.apps import AppConfig
class App2Config(AppConfig):
# typical systemic configurations
name = 'app2'
verbose_name = 'Second App'
# your desired configurations
OTHER_CONFIGURATION = 'default_value'
OPTION_C = 5
and so etc for other apps.pys in you Django Application folder.
It's important that you should import applications you included apps.py in, as follows:
# proj/settings.py
INSTALLED_APPS = [
'app1.apps.App1Config',
'app2.apps.App2Config',
# ...
]
Now, You could access desired app-based configuration someway like this:
from django.apps import apps
apps.get_app_config('app1').OPTION_A
Not sure about best practices but I don't have any problems with following style:
proj/settings.py
OPTION_A = 'value'
# or with namespace
APP_NAMESPACE = 'APP'
APP_OPTION_B = 4
app/settings.py
from django.conf import settings
from django.utils.functional import SimpleLazyObject
OPTION_A = getattr(settings, 'OPTION_A', 'default_value')
# or with namespace
NAMESPACE = getattr(settings, APP_NAMESPACE, 'APP')
OPTION_B = getattr(settings, '_'.join([NAMESPACE, 'OPTION_B']), 'default_value')
OPTION_C = getattr(settings, '_'.join([NAMESPACE, 'OPTION_C']), None)
if OPTION_C is None:
raise ImproperlyConfigured('...')
# lazy option with long initialization
OPTION_D = SimpleLazyObject(lambda: open('file.txt').read())
app/views.py
from .settings import OPTION_A, OPTION_B
# or
from . import settings as app_settings
app_settings.OPTION_C
app_settings.OPTION_D # initialized on access
My site is just starting and I want a the simplest but flexible configuration solution. That's what I came across with.
# in the end of the site's settings.py
. . .
# Custom settings
MYAPP_CONFIG_FILE = "/home/user/config/myapp_config.ini"
In my application's models.py:
from django.conf import settings
import configparser
config = configparser.ConfigParser()
config.read(settings.MYAPP_CONFIG_FILE, encoding='utf_8')
This config parser is described here. It's convenient enough but definitely not the only option.
you can use django-zero-settings, it lets you define your defaults and a key for your user settings, then it will handle user overrides too.
it also auto imports your settings, has the ability to handle removed settings, and can pre-check settings too.
as an example, define your settings:
from zero_settings import ZeroSettings
app_settings = ZeroSettings(
key="APP",
defaults={
"TOKEN": "token"
},
)
then you can use it like:
from app.settings import app_settings
print(app_settings.TOKEN)
When I run python manage.py shell, I can print out the python path
>>> import sys
>>> sys.path
What should I type to introspect all my django settings ?
from django.conf import settings
dir(settings)
and then choose attribute from what dir(settings) have shown you to say:
settings.name
where name is the attribute that is of your interest
Alternatively:
settings.__dict__
prints all the settings. But it prints also the module standard attributes, which may somewhat clutter the output.
I know that this is an old question, but with current versions of django (1.6+), you can accomplish this from the command line the following way:
python manage.py diffsettings --all
The result will show all of the settings including the defautls (denoted by ### in front of the settings name).
In case a newbie stumbles upon this question wanting to be spoon fed the way to print out the values for all settings:
def show_settings():
from django.conf import settings
for name in dir(settings):
print(name, getattr(settings, name))
To show all django settings (including default settings not specified in your local settings file):
from django.conf import settings
dir(settings)
In your shell, you can call Django's built-in diffsettings:
from django.core.management.commands import diffsettings
output = diffsettings.Command().handle(default=None, output="hash", all=False)