Re: Improving aggregate support (#14030)

2013-12-23 Thread Josh Smeaton
So, after hacking away a little bit yesterday, I came to the same 
conclusion that Anssi did back when the first PR was sent. Namely, that the 
ExpressionNode <-> SQLEvaluator structure seems overly complex, and makes 
it difficult to create custom "ExpressionNodes".

To build a custom Expression, one needs to:

- Subclass ExpressionNode
- Build in SQL into the backend (sometimes)
- Modify SQLEvaluator (or create a new one)
- Modify sql/query to learn about the different types of 
ExpressionNodes

I think it'd be better if SQLEvaluator was dropped altogether, and 
ExpressionNode was given the evaluator responsibilities. In this world, 
creating a custom Expression would look like:

- Subclass ExpressionNode
- The subclass contains SQL for each supported backend (and has 
access to the 'connection' instance)
- sql/query asks ExpressionNode to evaluate itself

Provided that ExpressionNode (or one of its subclasses) had the appropriate 
methods, it would allow library authors to create their own custom 
functions. #11305 (Conditional Aggregates) could then be implemented quite 
easily, without touching the rest of the stack.

So this is what I'm going to attempt first. I'll "merge" the functions of 
SQLEvaluator and ExpressionNode, and see how far along that gets me. If 
that turns out to simplify the handling of F(), then it should be equally 
useful when refactoring Aggregate as an ExpressionNode. As a POC, I'll also 
implement a version of Conditional Aggregates to confirm the API. Now, this 
is definitely more complex than porting nateb's original changes, but it's 
probably going to be a more simplified implementation. As such, my hopes 
for inclusion in 1.7 are probably far-fetched.

Will report back when I've got something a little more concrete to talk 
about.

Regards,

- Josh

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/b4d333f5-0184-4b2b-9946-73b365144306%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Django 1.7 - Named migration directory

2013-12-23 Thread Val Neekman
How did I miss that. Excellent!

Thanks.

Val



On Mon, Dec 23, 2013 at 7:08 PM, Andrew Godwin  wrote:

> There's already a setting for changing an app's migrations directory,
> called MIGRATION_MODULES. The app can just instruct people who want to not
> use PostGIS to set that to a different place if they want to ship the app
> like that, achieving exactly the result you want without an extra setting
> per app :)
>
> Andrew
>
>
> On Mon, Dec 23, 2013 at 11:57 PM, Val Neekman  wrote:
>
>> I am wondering if it would be a good idea for a django app to name its
>> migration directory and if nothing is provided then the name would default
>> to 'migrations'.
>>
>> The case: (let's call it a GEO app)
>>
>> GEO app looks at the setting.py file for a flag called
>> "GEO_USING_GEO_DJANGO".
>>
>> # in models.py
>>
>> if settings.GEO_USING_GEO_DJANGO:
>> from django.contrib.gis.db import models
>> from django.contrib.gis.geos import Point
>> else:
>> from django.db import models
>>
>> class Location(models.Model):
>>
>> if settings.GEO_USING_GEO_DJANGO:
>> point = models.PointField(_('Point'), default='POINT(0.0 0.0)')
>> else:
>> # lat/lng of location
>> lat = models.FloatField(_('Latitude'), default=0.0)
>> lng = models.FloatField(_('Longitude'), default=0.0)
>>
>> objects = models.GeoManager() if settings.GEO_USING_GEO_DJANGO else
>> models.Manager()
>>
>> The above may not be the best practice here, but it works really well for
>> people who don't want to install PostGIS and all the required software. All
>> they have to do is to set GEO_USING_GEO_DJANGO = False and use geopy for
>> lat/lng calculation.
>>
>> This is to support both geoDjango and non-geoDjango users, and from the
>> same source code.
>>
>> Now, all works great till the first *migration*. Then game is over and
>> the maintainer has to either stick with the geoDjango or the non-geoDjango
>> version as far as the migration goes.
>>
>> *Only if the app could set its own migration directory!*
>>
>> in settings.py
>> -
>> If GEO_USING_GEO_DJANGO:
>>GEO_MIGRATION_DIR_NAME = 'migrations_geo'
>>
>> The maintainer of the app would set GEO_USING_GEO_DJANGO=True and run
>>  schemamigration, then set GEO_USING_GEO_DJANGO=False and rerun the
>> schemamigration again. We'd end up with two distinct migration directories.
>>
>> The end users just have to set GEO_USING_GEO_DJANGO=True/False.
>>
>> I know this is not an ideal solution, but if avoiding duplicate versions
>> of the same application was the final goal, this would achieve it.
>>
>> This would complicate things for sure, but is there an easier way to
>> avoid the duplicate source code?
>>
>> Thanks,
>>
>> Val
>>
>>
>>
>>
>>
>>
>>
>>
>>  --
>> 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 django-developers+unsubscr...@googlegroups.com.
>> To post to this group, send email to django-developers@googlegroups.com.
>> Visit this group at http://groups.google.com/group/django-developers.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/django-developers/CADy_H8ESPLR24Y7tD6xNxSS-%2BkT%3Dwc2a-60boN%3DAZ5Aphms3KA%40mail.gmail.com
>> .
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>
>  --
> 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 django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-developers.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/CAFwN1uphseP5Q2AsTXdKmTyrM%3Dct60NpkttRhTnKvGp5Vx63tQ%40mail.gmail.com
> .
> For more options, visit https://groups.google.com/groups/opt_out.
>

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CADy_H8EgnFJ6EaFqHaDzDc8ax0uHfSZUHb3w%3Dpc9qcWzG5YJkA%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Django 1.7 - Named migration directory

2013-12-23 Thread Andrew Godwin
There's already a setting for changing an app's migrations directory,
called MIGRATION_MODULES. The app can just instruct people who want to not
use PostGIS to set that to a different place if they want to ship the app
like that, achieving exactly the result you want without an extra setting
per app :)

Andrew


On Mon, Dec 23, 2013 at 11:57 PM, Val Neekman  wrote:

> I am wondering if it would be a good idea for a django app to name its
> migration directory and if nothing is provided then the name would default
> to 'migrations'.
>
> The case: (let's call it a GEO app)
>
> GEO app looks at the setting.py file for a flag called
> "GEO_USING_GEO_DJANGO".
>
> # in models.py
>
> if settings.GEO_USING_GEO_DJANGO:
> from django.contrib.gis.db import models
> from django.contrib.gis.geos import Point
> else:
> from django.db import models
>
> class Location(models.Model):
>
> if settings.GEO_USING_GEO_DJANGO:
> point = models.PointField(_('Point'), default='POINT(0.0 0.0)')
> else:
> # lat/lng of location
> lat = models.FloatField(_('Latitude'), default=0.0)
> lng = models.FloatField(_('Longitude'), default=0.0)
>
> objects = models.GeoManager() if settings.GEO_USING_GEO_DJANGO else
> models.Manager()
>
> The above may not be the best practice here, but it works really well for
> people who don't want to install PostGIS and all the required software. All
> they have to do is to set GEO_USING_GEO_DJANGO = False and use geopy for
> lat/lng calculation.
>
> This is to support both geoDjango and non-geoDjango users, and from the
> same source code.
>
> Now, all works great till the first *migration*. Then game is over and
> the maintainer has to either stick with the geoDjango or the non-geoDjango
> version as far as the migration goes.
>
> *Only if the app could set its own migration directory!*
>
> in settings.py
> -
> If GEO_USING_GEO_DJANGO:
>GEO_MIGRATION_DIR_NAME = 'migrations_geo'
>
> The maintainer of the app would set GEO_USING_GEO_DJANGO=True and run
>  schemamigration, then set GEO_USING_GEO_DJANGO=False and rerun the
> schemamigration again. We'd end up with two distinct migration directories.
>
> The end users just have to set GEO_USING_GEO_DJANGO=True/False.
>
> I know this is not an ideal solution, but if avoiding duplicate versions
> of the same application was the final goal, this would achieve it.
>
> This would complicate things for sure, but is there an easier way to avoid
> the duplicate source code?
>
> Thanks,
>
> Val
>
>
>
>
>
>
>
>
>  --
> 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 django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-developers.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/CADy_H8ESPLR24Y7tD6xNxSS-%2BkT%3Dwc2a-60boN%3DAZ5Aphms3KA%40mail.gmail.com
> .
> For more options, visit https://groups.google.com/groups/opt_out.
>

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CAFwN1uphseP5Q2AsTXdKmTyrM%3Dct60NpkttRhTnKvGp5Vx63tQ%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.


Django 1.7 - Named migration directory

2013-12-23 Thread Val Neekman
I am wondering if it would be a good idea for a django app to name its
migration directory and if nothing is provided then the name would default
to 'migrations'.

The case: (let's call it a GEO app)

GEO app looks at the setting.py file for a flag called
"GEO_USING_GEO_DJANGO".

# in models.py

if settings.GEO_USING_GEO_DJANGO:
from django.contrib.gis.db import models
from django.contrib.gis.geos import Point
else:
from django.db import models

class Location(models.Model):

if settings.GEO_USING_GEO_DJANGO:
point = models.PointField(_('Point'), default='POINT(0.0 0.0)')
else:
# lat/lng of location
lat = models.FloatField(_('Latitude'), default=0.0)
lng = models.FloatField(_('Longitude'), default=0.0)

objects = models.GeoManager() if settings.GEO_USING_GEO_DJANGO else
models.Manager()

The above may not be the best practice here, but it works really well for
people who don't want to install PostGIS and all the required software. All
they have to do is to set GEO_USING_GEO_DJANGO = False and use geopy for
lat/lng calculation.

This is to support both geoDjango and non-geoDjango users, and from the
same source code.

Now, all works great till the first *migration*. Then game is over and the
maintainer has to either stick with the geoDjango or the non-geoDjango
version as far as the migration goes.

*Only if the app could set its own migration directory!*

in settings.py
-
If GEO_USING_GEO_DJANGO:
   GEO_MIGRATION_DIR_NAME = 'migrations_geo'

The maintainer of the app would set GEO_USING_GEO_DJANGO=True and run
 schemamigration, then set GEO_USING_GEO_DJANGO=False and rerun the
schemamigration again. We'd end up with two distinct migration directories.

The end users just have to set GEO_USING_GEO_DJANGO=True/False.

I know this is not an ideal solution, but if avoiding duplicate versions of
the same application was the final goal, this would achieve it.

This would complicate things for sure, but is there an easier way to avoid
the duplicate source code?

Thanks,

Val

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CADy_H8ESPLR24Y7tD6xNxSS-%2BkT%3Dwc2a-60boN%3DAZ5Aphms3KA%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.


Re: App-loading reloaded

2013-12-23 Thread Andrew Godwin
Thanks for your work on this, Aymeric - the reduced scope has really done
well, I think.

Couple of comments (I agree that generally the list of work is accurate):

 - There's already a MIGRATION_MODULES setting which lets you set the path
to a migration module by app label. We probably just need to rename it for
consistency, no need to implement it!
 - I'm not sure that installed_apps is a good name to have to replace
app_cache, but I also think that having "cache" in the name is useless. I
don't mind just calling it "apps".
 - App labels need to be unique, and we should enforce this. It's always
been the case that they had to be, and we've never checked properly. Too
much stuff relies on their uniqueness.

The big question is, of course, how feasible is it that this will all* land
by the alpha? It's January 20th - less than a month away - and while I'd
like to see this land in 1.7, if it misses that date it's out; the release
is already complex enough as it is to have a behind-time complex feature
drag it back further, and now your dedicated time on this is over it's
going to move slower.

Andrew


* By "all", I mean "enough of it that it works, doesn't break anything, and
is an obvious improvement"


On Mon, Dec 23, 2013 at 9:50 PM, Aymeric Augustin <
aymeric.augus...@polytechnique.org> wrote:

> The time frame I had allocated to this project expires tomorrow. With
> reference to my original list, I’ve reached goals 1, 2, 6 and 7, with the
> caveat that I still have to write documentation.
>
> I ran out of time before looking seriously at goal 3, but it appears to be
> within reach at this point. The basic ideas are:
> - to populate the app cache “early”, for instance in
> get_wsgi_application() and execute_from_command_line(),
> - to call a conventional method on the app config, say startup(), as soon
> as the app cache is populated.
> Apps that need to execute code at startup would put it in
> AppConfig.startup(). This solution doesn’t use signals, as there is no
> earlier opportunity to register them.
>
> I know that I didn’t reach the standards of communication and quality
> expected in the Django community while I was working on this project. I
> didn’t try to have a discussion on this mailing list. I asked a few core
> devs who worked on app-loading in the past to look at my code and
> ruthlessly pushed it as soon as I got their feedback. Given the staggering
> amount of hard problems I was trudging though, that’s all I could manage,
> and it was a conscious choice. Now that master has almost reached a stable
> state, I will do my best to gather feedback and fine tune the code until
> the 1.7 release.
>
> I’ve mentioned a few times that I was maintaining a list of future tasks I
> was leaving aside while I was focusing on more pressing issues. Here is it.
>
>
> ** Must do **
>
> * Fix the test suite. There are still a few failures, apparently caused by
> leaking app cache state.
>
> * Write documentation, especially explain the app[s].py conventions and
> why `for app in INSTALLED_APPS` doesn't work any more.
>
> * "UserWarning: No fixture named 'comment_tests' found." when running the
> test suite.
>
> * Review previous patches. Extract relevant tests and docs, if any.
>
> * Review past discussions once again.
>
> * Close #3951 and file individual tickets for remaining tasks. This
> massive ticket is a nuisance.
>
>
> ** Cleanup / small tasks **
>
> * Rename AppCache to InstalledApps? Then we should also replace app_cache
> by installed_apps. Originally proposed by Arthur Koziel. The parallel with
> INSTALLED_APPS is interesting but it may also cause confusion. I would also
> consider simply Apps and apps, although `from django.apps import apps`
> doesn't look that good. This must be done by 1.7 alpha if we want to do it.
>
> * Review all uses of get_app_configs() and determine if the results may be
> cached. If so, add a receiver for the setting_changed signal to reset the
> cache when INSTALLED_APPS changes.
>
> * Replace TransRealMixin by suitable receivers for the setting_changed
> signal.
>
> * Refactor AppCommand to be able to handle apps without a models module.
>
> * Adapt the migrations code:
> - Refactor signals to stop relying on the models module.
> - Introduce MIGRATIONS_MODULE_NAME for consistency with
> MODELS_MODULE_NAME.
>
> * Refactor get_models and get_migratable_models. They can probably be
> implemented more straightforwardly.
>
> * Invalidate _get_models_cache appropriately or remove it entirely. This
> doesn't look like a performance-sensitive area.
>
> * Add a comment to explain how postponing works in populate_models(), or
> remove it if possible. See
> https://github.com/django/django/pull/2089#discussion_r8516536.
>
> * populate_models() isn't exactly idempotent. A recursive invocation may
> return while models aren't fully populated, and it won't set
> _models_loaded. This could probably be an issue in some cases, but it would
> be moot if we got rid of the pos

Re: App-loading reloaded

2013-12-23 Thread Aymeric Augustin
The time frame I had allocated to this project expires tomorrow. With reference 
to my original list, I’ve reached goals 1, 2, 6 and 7, with the caveat that I 
still have to write documentation.

I ran out of time before looking seriously at goal 3, but it appears to be 
within reach at this point. The basic ideas are:
- to populate the app cache “early”, for instance in get_wsgi_application() and 
execute_from_command_line(),
- to call a conventional method on the app config, say startup(), as soon as 
the app cache is populated.
Apps that need to execute code at startup would put it in AppConfig.startup(). 
This solution doesn’t use signals, as there is no earlier opportunity to 
register them.

I know that I didn’t reach the standards of communication and quality expected 
in the Django community while I was working on this project. I didn’t try to 
have a discussion on this mailing list. I asked a few core devs who worked on 
app-loading in the past to look at my code and ruthlessly pushed it as soon as 
I got their feedback. Given the staggering amount of hard problems I was 
trudging though, that’s all I could manage, and it was a conscious choice. Now 
that master has almost reached a stable state, I will do my best to gather 
feedback and fine tune the code until the 1.7 release.

I’ve mentioned a few times that I was maintaining a list of future tasks I was 
leaving aside while I was focusing on more pressing issues. Here is it.


** Must do **

* Fix the test suite. There are still a few failures, apparently caused by 
leaking app cache state.

* Write documentation, especially explain the app[s].py conventions and why 
`for app in INSTALLED_APPS` doesn't work any more.

* "UserWarning: No fixture named 'comment_tests' found." when running the test 
suite.

* Review previous patches. Extract relevant tests and docs, if any.

* Review past discussions once again.

* Close #3951 and file individual tickets for remaining tasks. This massive 
ticket is a nuisance.


** Cleanup / small tasks **

* Rename AppCache to InstalledApps? Then we should also replace app_cache by 
installed_apps. Originally proposed by Arthur Koziel. The parallel with 
INSTALLED_APPS is interesting but it may also cause confusion. I would also 
consider simply Apps and apps, although `from django.apps import apps` doesn't 
look that good. This must be done by 1.7 alpha if we want to do it.

* Review all uses of get_app_configs() and determine if the results may be 
cached. If so, add a receiver for the setting_changed signal to reset the cache 
when INSTALLED_APPS changes.

* Replace TransRealMixin by suitable receivers for the setting_changed signal.

* Refactor AppCommand to be able to handle apps without a models module.

* Adapt the migrations code:
- Refactor signals to stop relying on the models module.
- Introduce MIGRATIONS_MODULE_NAME for consistency with MODELS_MODULE_NAME.

* Refactor get_models and get_migratable_models. They can probably be 
implemented more straightforwardly.

* Invalidate _get_models_cache appropriately or remove it entirely. This 
doesn't look like a performance-sensitive area.

* Add a comment to explain how postponing works in populate_models(), or remove 
it if possible. See 
https://github.com/django/django/pull/2089#discussion_r8516536.

* populate_models() isn't exactly idempotent. A recursive invocation may return 
while models aren't fully populated, and it won't set _models_loaded. This 
could probably be an issue in some cases, but it would be moot if we got rid of 
the postponing.

* Consider eliminating the check for duplicate imports of models in 
register_model(), which has become unnecessary with the new project layout 
introduced in 1.4. It would probably be safer to do it through a deprecation 
path.

* Review the implementation of dumpdata. It looks like it could be simplified 
by using AppConfigs.

* Review how much django.apps and django.conf import — they should stay lean to 
avoid import loops.


** Further work / large tasks **

(Many of these are open questions that could be debated on this mailing list.)

1) Provide an API to run code at startup, along the lines described above.

2) Some fields should be moved from Model._meta to Model._meta.app_config, 
which should be set as part of the app cache population process.

3) Make it possible to change the label in AppConfig. That's half of the 
original feature request in #3591 (the other half being verbose_name). Requires 
2 — and then lots of work.

4) Make it possible to change various prefixes (admin permissions, database 
tables, dumpdata/loaddata). That was part of Arthur Koziel's GSoC proposal. 
Requires 2 — and then some work.

5) Investigate whether app labels currently need to be unique (the docs say so, 
but the code doesn’t enforce it). See 
https://groups.google.com/d/msg/django-developers/gzBWU_fUdgc/zlTTkKx-s5QJ. 
Consider enforcing unicity. Easier to pull off after 3.

6) Store application-specific 

Re: App-loading reloaded - custom app names in the admin

2013-12-23 Thread Aymeric Augustin
After the merge, some Selenium tests (which I don’t run locally in general) 
failed on Jenkins. That’s a test isolation issue: an AppDirectoriesFinder 
instance kept a cache of an incomplete list of applications, preventing later 
tests from finding static files correctly.

I wanted to review the methods that add and remove apps to the app cache anyway 
— that’s the “part of the patch I don’t like much” I was talking about in my 
first email. I’ve sent a new pull request for this purpose: 
https://github.com/django/django/pull/2103.

It introduces a new API called override_list_settings to alter settings that 
are lists of values. It allows simply using override_settings(INSTALLED_APPS=…) 
or override_list_settings(INSTALLED_APPS=…). django.test now contains some 
references to django.apps but that seems reasonable to be as the app cache is a 
core concept of Django. Overall the result seems much better, both in terms of 
API and implementation.

I’ll merge that once I get a +1 from another core dev.

-- 
Aymeric.



On 22 déc. 2013, at 17:10, Aymeric Augustin 
 wrote:

> Merged: https://github.com/django/django/compare/7f2485b4d180...17c66e6fe77b
> 
> -- 
> Aymeric.
> 
> 
> 
> On 22 déc. 2013, at 12:14, Aymeric Augustin 
>  wrote:
> 
>> I’ve updated the pull request with these changes as well as a few minor 
>> comments made by reviewers.
>> 
>> https://github.com/django/django/pull/2089
>> 
>> I think it’s worth merging in that state. I still have a list of about 20 
>> things to check, but none of them is likely to change anything fundamental.
>> 
>> -- 
>> Aymeric.
>> 
>> 
>> 
>> On 21 déc. 2013, at 17:01, Aymeric Augustin 
>>  wrote:
>> 
>>> Hello,
>>> 
>>> Based on the feedback I received through several channels (GitHub, IRC, 
>>> private email) I’m planning to make two API changes before merging this 
>>> pull request.
>>> 
>>> 
>>> (1) Remove auto-discovery of AppConfig in application modules
>>> 
>>> I implemented this shim to make it possible to take advantage of app 
>>> configs without changing the format of INSTALLED_APPS. I wanted to increase 
>>> backwards-compatibility and accelerate adoption in pluggable apps. I also 
>>> wanted to keep INSTALLED_APPS short and readable in the common case and 
>>> avoid this pattern:
>>> 
>>> INSTALLED_APPS = (
>>>  ‘django.contrib.admin.app.AdminConfig',
>>>  'django.contrib.auth.app.AuthConfig',
>>>  'django.contrib.contenttypes.app.ContentTypesConfig',
>>>  'django.contrib.sessions.app.SessionsConfig',
>>>  'django.contrib.messages.app.MessagesConfig',
>>>  'django.contrib.staticfiles.app.StaticFilesConfig’,
>>>  # et caetera ad nauseam
>>>  # cold enterprisey thorny feelings 
>>> )
>>> 
>>> Astute readers will note that MIDDLEWARE_CLASSES looks even worse but I’m 
>>> not buying the “make it consistently ugly" argument :-)
>>> 
>>> (Also I haven’t prepared any changes to Django’s contrib apps yet.)
>>> 
>>> However, I can also list a few reasons not to provide this shim:
>>> 
>>> - “There should be one-- and preferably only one --obvious way to do it.”
>>> - “Explicit is better than implicit.”
>>> - Django shouldn't encourage writing code in __init__.py files because it’s 
>>> a common cause of import loops eg. when a submodule attempts to import an 
>>> object defined in a parent package. Strictly speaking, importing the base 
>>> AppConfig class and implementing a subclass is safe: it doesn’t even load 
>>> the Django settings. But it’s still a code smell.
>>> - Python ≥ 3.3 introduces support for namespace packages (PEP 420) by 
>>> making __init__.py files optional. I’m pretty sure someone will find a good 
>>> reason to implement a Django app as a namespace package; then they couldn’t 
>>> take advantage of this convention any more. Besides, skipping __init__.py 
>>> files entirely might become a good practice in the next years.
>>> 
>>> One could also say that the explicit version makes it obvious which 
>>> applications use an application configuration and which don’t, but I don’t 
>>> find the difference between “using the application’s default AppConfig” and 
>>> “using Django’s default AppConfig” relevant.
>>> 
>>> I’m ambivalent about this question. Considering that it can easily be added 
>>> later, I’m leaning towards not including it for now. The reverse would be 
>>> much more complicated. 
>>> 
>>> 
>>> (2) Move the code back into django.apps
>>> 
>>> I originally wrote all the code in django.apps.
>>> 
>>> Then I realized it could cause confusion because “apps” shipped with Django 
>>> are in django.contrib, not in django.apps, and I moved the code to 
>>> django.contrib.apps. One could argue that apps are a core concept.
>>> 
>>> However, there are two strong arguments for moving the code back to its 
>>> original location.
>>> 
>>> - Most APIs intended to be imported by user code live outside of core: 
>>> “django.forms”, “django.db.models”, “django.templates”, “django.views”, 
>>> etc. 
>>> - Most of the code in django.core would b