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.


Reply via email to