I have a Django App I'm building, which we will call foo.
Because of the way Foo is built, it requires a number of third-party django apps to function. For example, to run Foo an install apps might look like:
INSTALLED_APPS = ('prereq1',prereq2','foo')
In fact, for Foo to even be functional, 'prereq1', prereq2' have to be installed in django. Now, I can add requirements to requirements.txt or setup.py to make sure the libraries are installed when someone goes to install Foo, but I can't figure out if there is a way to have them installed in Django itself.
The reason for this is if someone wants to use Foo, I don't want to include instructions like:
In your INSTALLED_APPS add foo but also add scary_looking_library_name and thing_you_dont_understand.
So is it possible for an app in INSTALLED_APPS to somehow require or inject further apps into that list?
I agree with Daniel Roseman's answer about the system checks framework being an optimal place for these checks. The system checks framework was introduced Django 1.7.
However, assuming you have documentation, you can also document these prerequisites such as Django REST Framework did in their installation instructions.
You can then do something like the below in your code (django-mptt used as an example):
try:
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
except ImportError:
raise ImportError(
'You are using the `foo` app which requires the `django-mptt` module.'
'Be sure to add `mptt` to your INSTALLED_APPS for `foo` to work properly.'
)
This is the method I have seen used in multiple applications. The onus of reading the documentation lies on the developer.
Perhaps this is an unwanted/unnecessary opinion, but the injection of dependencies into the INSTALLED_APPS is nothing that I feel you should be handling with your application.
I typically try to follow the Zen of Python when designing applications:
"Explicit is better than implicit."
Have the developer manually enter the dependencies in INSTALLED_APPS
"If the implementation is hard to explain, it's a bad idea."
Trying to figure out a way to inject the dependencies into the INSTALLED_APPS is hard to explain. If the third-party dependencies are complicated, let the developer decide.
"Simple is better than complex."
It is easier to document the dependencies and require the developer to add them to the INSTALLED_APPS.
"There should be one-- and preferably only one --obvious way to do it."
The common practice is to have the developer add the third party apps to the INSTALLED_APPS - which is why there is no obvious way to do what you want (injection).
If a developer wants to activate an app, they will. As you so eloquently stated in your example, scary_looking_library_name and thing_you_dont_understand is the responsibility of the developer to understand. Choosing to install it for developer is imposing an unnecessary security risk. Let the developer choose to use your application and initialize its dependencies.
I think the system check framework would be a good place for this. You can write a check that verifies the presence of those apps in the settings and raises an error if they are not there.
I prefer to do dependency check, however i think you demand the below code to inject your desired apps into the django site.
you must put it somewhere to execute, and do a check not to re-execute when the dependencies are loaded.
also if you use it, the 'someapp' in settings.INSTALLED_APPS will not work correctly. maybe you need to also alter it.
from django.apps import apps
installed_apps = [app.__name__ for app in apps.get_apps()]
new_installed_app = installed_app + ['your desired app1', 'your desired app2', ...]
apps.set_installed_apps(new_installed_apps)
Related
In Djano, why should I add third-party package names inside the INSTALLED_APPS for some packages such as django-filter, DRF, debug-toolbar etc, while I don't want to add for some packages such as Celery, Requests etc ?
I couldn't figure out why should add them to a specific list, even though they all are similar pip packages and I installed them in the same way. Thanks in advance!
From docs :
Package? App?
A Python package provides a way of grouping related Python code for
easy reuse. A package contains one or more files of Python code (also
known as “modules”).
A package can be imported with import foo.bar or from foo import bar.
For a directory (like polls) to form a package, it must contain a
special file init.py, even if this file is empty.
A Django application is just a Python package that is specifically
intended for use in a Django project. An application may use common
Django conventions, such as having models, tests, urls, and views
submodules.
From above statements we understand that every well-written python code could be a package, now if this package is a bunch of python code in a directory, installing it would mean just copy the directory in your project and import them wherever needed in code.
But now there is this package which was already a django app and maybe used some special advantages of being one. Like exposing a restful API or delivering some sort of static resources or even working with special django-specific classes. these kind's of operation's need your direct permission in settings.py so that your project will know how to deal with this package. or even they may require to include their url path's to your project so that people visiting your site could access them.
I assume all this manual job is an act of security.
You need to add apps with models(non-abstract), templates, templatetags or management commands so that django-admin finds and loads them
I want to make use of Django's modularity and integrate some external apps that I installed using pip. However, I am encountering difficulties in understanding how can I integrate and use or extend their urls, views, models. There isn't much on this subject, I can't figure why.
Let's take the example of changuito-cart:
Do i create a folder named "changuito" in root and create urls/views in here, or should I just create a new app named like it?
In settings.py I added "changuito" to my installed apps list and I got "no module named 'changuito'" error. How do I add it correctly?
What are the basic steps required to integrate it?
After pip installing the app and adding it to your INSTALLED_APPS you should be good to go. There are cases where the name you need to add to your INSTALLED_APPS is different from the package name. Such cases should be apparent from the documentation, otherwise you need to look at the module structure itself. In your case the package is called "django-changuito" and you have to include "changuito" in your INSTALLED_APPS. Be also aware that this particular package depends on MIDDLEWARE_CLASSES settings. Please read the documentation!
Make also sure that the correct virtual environment is activated while installing if you use one.
In general you would not modify the external app itself. This would be quite dangerous since it will start to differ from the upstream and break down the road. If you want to subclass or extend classes of the external app you need to import these classes into your own app before extending or monkey patching. In this case you will create a different class in a different, your app.
Finally, some apps allow for extending templates. Django will search for templates and statics in your project structure first. If you recreate the folder structure of an external app and put new templates in the exact right place. Django will use those.
I've got an issue where there's more than one app trying to override the same management command in a Django project.
Are there sensible ways to deal with this?
Which gets priority - the app the was defined first in INSTALLED_APPS, or the one that was defined last?
Is it possible to effectively subclass the most recently defined management command rather than simply replacing it?
For context I'm trying to get django_pdb (see github) to work more nicely with south and django.contrib.staticfiles.
2.5 years later, but in case someone has the same problem and lands here after a google search, I've made a small django app to deal with that case: django-mcmo ('Management Command Multiple Override'), available on pypi. It has limitations but works as expected.
Works with django 1.4 to 1.8 and py 2 and 3, contributions welcome on bitbucket repo.
Easiest answer I'm aware of is: structure your project so you can change one of them and keep a record of your changes so you can apply it to future releases.
For my projects I like to have:
/myproject
/lib
/app1
/app2
/app3
Then explicitly add /lib to the path in setup.py
import os
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))
import sys
lib_dir = os.path.join(PROJECT_PATH, 'lib')
if lib_dir not in sys.path[:4]:
sys.path.insert(1, os.path.join(PROJECT_PATH, 'lib'))
I'm probably far more likely than average to take an app, install it, then change 10% of it to work exactly how I want.
The advantage of this is: 1) most dependencies ship with the code and are tracked in GIT 2) no chance for a system wide change to unexpectedly cause bugs in an app if you are running multiple apps from the same machine and 3) Easy to change, with revision history, any and everything in the app.
Not having dove too deeply into south's management commands, and never having used django_pdb, your particular problem might not be solved with the "make a local copy and rename one of them" approach, but I share in case it might.
Why aren't they simply directories? Any good advice says to keep as much as possible in the apps and not to couple them to the project. The very ability to import an app as project.application discourages this. Why does django-admin.py create the __init__.py at all? The project is perfectly useful without it. What is the justification?
We have a single project that we "subclass" of sorts for other projects. So we have other projects that import stuff from the main project. I guess for us it provides the common namespace that contains all the other apps.
We could move to a package with all our apps in it separate from the projects i guess. Our system has grown rather than been planned.
So I guess my answer is, it provides a good root namespace. (for our needs) :)
The core of a project is a settings.py and a root urls.py. Both of those are Python modules, thus they need to be importable somehow. You can put the project directory directly on the Python path and thus make them importable as top-level modules, but that's arguably even worse practice. Better to have the project be a package and the settings and urls be modules within it.
There isn't a requirement that apps be inside the project's namespace, to my knowledge. Just that they be on the $PYTHONPATH. As such, they are usable by any other code on the system which shares the same PYTHONPATH.
I think the idea is that you can reuse the applications but you don't need to move them from the project where they were initially created. If the project weren't a package you would need to copy/move the application you want to reuse to a python package. Because of that being the project itself a proper python package you can reuse the applications in it without moving the applications to other place.
After looking at the reusable apps chapter of Practical Django Projects and listening to the DjangoCon (Pycon?) lecture, there seems to be an emphasis on making your apps pluggable by installing them into the Python path, namely site-packages.
What I don't understand is what happens when the version of one of those installed apps changes. If I update one of the apps that's installed to site-packages, then won't that break all my current projects that use it? I never noticed anything in settings.py that let's you specify the version of the app you're importing.
I think in Ruby/Rails, they're able to freeze gems for this sort of situation. But what are we supposed to do in Python/Django?
Having multiple versions of the same package gets messy (setuptools can do it, though).
I've found it cleaner to put each project in its own virtualenv. We use virtualevwrapper to manage the virtualenvs easily, and the --no-site-packages option to make every project really self-contained and portable across machines.
This is the recommended setup for mod_wsgi servers.
You definitely don't want to put your Django apps into site-packages if you have more than one Django site.
The best way, as Ken Arnold answered, is to use Ian Bicking's virtualenv (Virtual Python Environment Builder). This is especially true if you have to run multiple versions of Django.
However, if you can run a single version of Python and Django then it might be a little easier to just install the apps into your project directory. This way if an external app gets updated you can upgrade each of your projects one at a time as you see fit. This is the structure Pinax used for external Django apps at one time, but I think it's using virtualenv + pip (instead of setuptools/distutils) now.
What we do.
We put only "3rd-party" stuff in site-packages. Django, XLRD, PIL, etc.
We keep our overall project structured as a collection of packages and Django projects. Each project is a portion of the overall site. We have two separate behaviors for port 80 and port 443 (SSL).
OverallProject/
aPackage/
anotherPackage/
djangoProject80/
settings.py
logging.ini
app_a_1/
models.py # app a, version 1 schema
app_a_2/
models.py # app a, version 2 schema
app_b_2/
models.py
app_c_1/
models.py
djangoProject443/
test/
tool/
We use a version number as part of the app name. This is the major version number, and is tied to the schema, since "uses-the-same-schema" is one definition of major release compatibility.
You have to migrated the data and prove that things work in the new version. Then you can delete the old version and remove the schema from the database. Migrating the data is challenging because you can't run both apps side-by-side.
Most applications have just one current version installed.