Default automatic imports in the shell

I’m attempting to make a point about the future of automatically imported libraries and the difficulty of removing them. For example, if we automatically import X library and people start writing scripts to do:

python manage.py shell -c "X.do_something()"

If we were to remove import X, those scripts will then break unless they are adjusted to be:

python manage.py shell -c "import X; X.do_something()"

Does my concern make more sense now?

Ah. Yea ok. Hmm… maybe don’t do these automatic import for the -c flag? It’s a bit extreme, but it can hit the sweet spot of adding a lot for the interactive use easily, without having that problem?

Thank you for following up on this discussion.

I really believe we should take a more conservative approach with default imports. Allowing each individual’s view of what’s needed to shape the default namespace could lead to unnecessary bloat.

For example, a typical Django app serving a web project may not need Path or json, and including dependencies like numpy and tqdm feels like a stretchy strech :person_cartwheeling: (for example, I’ve never used numpy, and I’m not even sure what tqdm is!).

The point I’m trying to make is that individual needs or personal experiences shouldn’t define what’s considered “default.” Many dependencies are highly specific to the type of service being built: for instance, an API-driven web service might require json, while a service using HTML or HTMX may never need it. Similarly, Path is essential when working with file systems (e.g., django-storages), but the average Django service likely won’t need it.

My reading of this post so far is that we would be including:

  • from django.conf import settings
  • from django.db import connection, reset_queries
  • django.db.models and django.db.models.functions in some form, I’m not a fan of star imports
  • potentially datetime specifics (date, datetime, timedelta)
2 Likes

I think the most important thing is to have a default place to put these things that is set up by default and well documented. That’s going to massively improve a lot of peoples lives just because they suddenly am aware that they CAN improve their dev setup.

1 Like

I would advocate for django.utils.timezone over date, datetime and timedelta. I tend to use timezone.now() way more often than others.

1 Like

@CodenameTim I agree with you, and upon reflection yesterday, I was thinking that we should not autoimport “python” imports, we should limit to Django imports (otherwise discussing python imports open a can of worms, IMHO). django.utils.timezone was also suggested by Sarah, so I’m inclined to include it. Summing up:

  • from django.conf import settings
  • from django.db import connection, reset_queries
  • django.db.models and django.db.models.functions TBD
  • from django.utils import timezone
3 Likes

Since we have the ability to allow folks to customize this to their
own needs, it feels prudent to be very strict on what goes in by
default.

Your are talking about the PYTHONSTARTUP environment variable and
~/.pythonrc.py, right?

Natalia writes:

I really believe we should take a more conservative approach with
default imports.

FWIW, I’m still not sure if it is already decided to have default imports in django itself? I’m still a bit undecided here to whether I would favour this or not.

This would replicate something that is already available from the django_extensions package (using its shell_plus command). And I think many people, like Ken [1], expressed the habit of always installing the django_extensions package with a new project.

So why replicate something that most people will likely have anyway when setting up a new project? And it maybe could lead to problems with the existing django_extensions package.

A seasoned django developer will either have django_extensions installed anyway or it’s just a quick “pip install django_extensions + edit of the settings file” away.

For beginners django could make the recommendation in the docs and tutorials to always install the django_extensions?!

On the other hand django_extensions is an external package. I think it’s neither part of Django Commons[2] nor JazzBand[3]. Work on the package has stalled for quite some time now and according to the maintainer trbs it is unclear “when work will pick up in a significant way” [4].

And in another discussion [5] it seems to be already decided, that a management command like show_urls should be part of django itself, which is also in django_extensions.

I guess in the end I tend to lean towards having “default imports” and “show_urls” in django itself, considering the indefinite hiatus of the django_extensions package.

[1] Should the show_urls command be part of Django's standard library? - #5 by KenWhitesell
[2] Django Commons - A home for community-maintained Django packages · Better Simple
[3] https://jazzband.co/
[4] Moving django-extensions forward · Issue #1892 · django-extensions/django-extensions · GitHub
[5] Should the show_urls command be part of Django's standard library?

2 Likes

This is a good summary but I want to add one thing:

Yes, I install django_extensions on every project, but I wish I didn’t have to. I don’t need 90% of what it offers. Really the shell imports are the only thing I’ve ever really used in the last… many years. It seems the other most common command, as you mention, is to list URLs, and this is also coming… So for me (and I assume others) I’ll be glad to have one fewer dependency.

2 Likes

@e11bits Thank you for your post, it’s well written and it asks great questions in a constructive manner :star2:

I think the key point is what @tom said: hopefully we can include in Django core the parts of django_extensions that are most used, to solve the need for the “common case”. So far, those seems to be the default imports and the listing of the URLs. The original proposal/idea was listed here:

https://code.djangoproject.com/wiki/SummerOfCode2024#Auto-importingshell

2 Likes

No, I meant to refer to how this would be built within Django. I would assume it’d be extensible. Though you’re right there may be ways to do it already today.

I’m much more concerned about making sure we’ve provided good hooks to have default imports than that we’ve thought of even the common cases. If we have a good hook, then the specific things we choose become an implementation detail, and one that folks are very likely to wish to customize to their own particular tastes.

I agree that any default set should be carefully curated, because this will be as much of a public interface as most anything else is. I’m in favor of avoiding auto-imports in python manage.py shell -c, but I think that we won’t to break blog posts and examples that rely on auto-imports.

For my tastes, I’d prefer auto-imports not be printed out by default, but would be happy for them in verbose mode. Probably controversially, and further motivating my push for hooks to customize the behavior, I’d prefer that things not be namespaced, so individual models, fields, ORM functions, etc should not be namespaced in auto-imports (e.g. DateTimeField instead of models.DateTimeField). It’s worth noting that settings is a necessary exception, because it’s actually an object that proxies settings and isn’t referencing the model directly.

The PR already includes documentation for how to customize automatic imports with a command subclass. See: Fixed #35680 -- Added default automatic imports to shell. by salvo-polizzi · Pull Request #18560 · django/django · GitHub

PYTHONSTARTUP only applies to the individual, so it’s not very useful for sharing customizations with a team.

I think this is a fair policy and I like the list you have proposed @nessita.

I personally don’t see a problem with using “star imports” from db.models and db.models.functions, given they define __all__. As long as we load them before model classes, there won’t be conflicts, and the full power of the ORM will be available, including any future functions, expressions, fields, etc.

4 Likes