Hi.
The motivation to this question is that if I want to implement "copies" of
an app within an website (for instance, a stackoverflow-like where each app
is a Q&A for its own, but with modifications on templates, urls, etc.), as
far as I'm understanding: (1) each "copy" has to change its name, names of
all calls of templates, views, etc due to name collision and (2) each copy
must be an app on its own without any dependencies on other apps with
models/templates/urls, because otherwise the dependent apps would also have
to be "copied", names changed, etc.
In a previous post I suggested generalizing templates and url search to be
used within subapps (apps with folder inside another app), which I found
was a way of allowing what I wanted to do.
There was a criticism that during the discussion that I (mostly) ignored,
but while re-reading it, I understood its meaning and its importance to a
discussion by itself.
The criticism was from Shai Berger, that correctly pointed out that Django
uses unique app names [1], and what I didn't understood at the time was
that this forbids any possibility of using subapps the way I was suggesting
or in any way that might help.
Motivated by that criticism, I want to ask: why apps have to have unique
names?
To try to answer this question on the code, I did a simple search on Django
code. This is far from rigorous, but gives some nice results. The search
was simple: regex of r"settings.INSTALLED_APP" and regex of
r".split('.')[-2]". Afterwards, I read the code to understand which
dependencies it has on the labeling. Here are the results:
## Usage of settings.INSTALLED_APP (regex: settings.INSTALLED_APP)
# To check admin existence, uses full path (import_module)
/django/contrib/admin/__init__.py:24: for app in
settings.INSTALLED_APPS:
# To check specific app existence, uses full path (import_module)
/django/contrib/admin/templatetags/admin_static.py:6: if
'django.contrib.staticfiles' in settings.INSTALLED_APPS:
/django/contrib/comments/__init__.py:16: if comments_app not in
settings.INSTALLED_APPS:
/django/contrib/gis/tests/__init__.py:113: settings.INSTALLED_APPS
= list(self.old_installed) + new_installed
/django/contrib/gis/tests/__init__.py:125: settings.INSTALLED_APPS
= self.old_installed
/django/contrib/messages/tests/base.py:16: 'django.contrib.auth'
not in settings.INSTALLED_APPS,
/django/contrib/messages/tests/base.py:214: lambda
app:app!='django.contrib.messages', settings.INSTALLED_APPS),
/django/contrib/messages/tests/base.py:239: lambda
app:app!='django.contrib.messages', settings.INSTALLED_APPS),
/django/contrib/sitemaps/tests/flatpages.py:8:
@skipUnless("django.contrib.flatpages" in settings.INSTALLED_APPS,
/django/contrib/sitemaps/tests/http.py:88:
@skipUnless("django.contrib.sites" in settings.INSTALLED_APPS,
# for finding static; use full path of the app
/django/contrib/staticfiles/finders.py:122: apps =
settings.INSTALLED_APPS
# for binding commands to the management. Requires unique app to avoid
commands collision.
/django/core/management/__init__.py:101: apps =
settings.INSTALLED_APPS
# for binding commands to the management. Requires unique app to avoid
commands collision.
/django/core/management/__init__.py:319: options +=
[(a.split('.')[-1], 0) for a in settings.INSTALLED_APPS]
# for importing the 'management' module within each installed app, to
register dispatcher events.
/django/core/management/commands/flush.py:35: for app_name in
settings.INSTALLED_APPS:
# for importing the 'management' module within each installed app, to
register dispatcher events (lacks DRY principle: command used is the same
as previous file.)
/django/core/management/commands/syncdb.py:38: for app_name in
settings.INSTALLED_APPS:
# for the 'models' of the db. It uses django.utils.importlib.import_module
on the full app's path. Class uses _label_for for defining a "name" for the
app, which requires unique app
/django/db/models/loading.py:61: for app_name in
settings.INSTALLED_APPS:
/django/db/models/loading.py:143: for app_name in
settings.INSTALLED_APPS:
# To check whether the model is installed. It uses full paths of the package
/django/db/models/options.py:71: self.installed =
re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
# To find templatetags. It stores the full path of the module
/django/template/base.py:1271: for app_module in ['django'] +
list(settings.INSTALLED_APPS):
# To find templates. It stores the full path of the module
/django/template/loaders/app_directories.py:19: for app in
settings.INSTALLED_APPS:
# To find templates. Depends on the pkg_resources.resource_string to tell
wether it returns app name of full app name
/django/template/loaders/eggs.py:23: for app in
settings.INSTALLED_APPS:
# To check specific app existence, uses full path (import_module)
/django/test/client.py:353: if 'django.contrib.sessions' in
settings.INSTALLED_APPS:
/django/test/client.py:501: and 'django.contrib.sessions'
in settings.INSTALLED_APPS:
# for finding "locale/". Why it uses reverse of settings.INSTALLED_APPS ?
Uses full app (import_module) path
/django/utils/translation/trans_real.py:159: for appname in
reversed(settings.INSTALLED_APPS):
# for building the debug page. Not relevant.
/django/views/debug.py:745: {{ settings.INSTALLED_APPS|pprint }}
/django/views/debug.py:936: {{ settings.INSTALLED_APPS|pprint }}
# for translating javascript. Uses full path (import_module)
/django/views/i18n.py:189: packages = [p for p in packages if p ==
'django.conf' or p in settings.INSTALLED_APPS]
## usage of app by app.__name__.split('.')[-2] (regex: split('.')[-2])
# deprecated
/django/core/management/commands/reset.py:31: app_name =
app.__name__.split('.')[-2]
# for listing apps and models for syncdb. It requires unique app name.
/django/core/management/commands/syncdb.py:67:
(app.__name__.split('.')[-2],
# Only used for printing in case verbosity >= 2
/django/core/management/sql.py:184: app_name =
app.__name__.split('.')[-2]
# Used for labeling app "app_label" of model.
# Notice that app_label is also used in '/django/db/models/loading.py'
/django/db/models/base.py:54: kwargs = {"app_label":
model_module.__name__.split('.')[-2]}
/django/db/models/loading.py:77: return
app_mod.__name__.split('.')[-2]
At first glance, I would say that the way django "finds" the app could be
transformed into a function to simplify code; there are a lot of calls
"app_mod.__name__.split('.')[-2]" that could be transformed into a function
e.g. in django.utils.get_app_label(app)', should this deserve a ticket?
Secondly, by the code, the unique app constraint seems to be a design
decision and not something that django's current implementation could not
properly handle.
This leads to the question, what is the reasoning for this design decision?
Thanks,
Jorge
[1] https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.