Inspired by @adamghill
’s recent dj-toml-settings
and webology
’s previous post on renaming django-admin
I did a quick brainstorm of a django-uv
command.
uvx django-uv
As uv
has become more popular, a disconnect has appeared when using uv
and django-admin
.
When bootstrapping a new project, one often wants to run the equivilant of uvx django startproject
but this doesn’t work.
By default uvx
looks for a command of the same name as the package.
The correct command is uvx --from django django-admin
which is helpfully suggested by uv.
We can make a small wrapper package for this so that it works as expected.
[project.scripts]
django-uv = "django_uv:main"
from django.core.management import ManagementUtility
def main(argv=None) -> None:
utility = ManagementUtility(argv)
utility.execute()
With this simple project, we now have a django-uv
command that is equivilant of the django-admin
command, saving a bit of typing.
Reading DJANGO_SETTINGS_MODULE
This helps for our intial project bootstrap, but with an existing project, it would be nice to reference our project.
Taking inspiration from adamghill
, we could read the values out of our pyproject.toml
[tool.django]
settings = 'path.to.our.settings'
and then set enviornment variables as expected.
from django.core.management import ManagementUtility
from pathlib import Path
from tomllib import load
import os
def find_pyproject(path=Path.cwd() / "pyproject.toml") -> dict:
if path.exists:
with path.open("rb") as fp:
data = load(fp)
try:
settings = data["tool"]["django"]
except KeyError:
# No tool section found
return {}
# Populate our settings value
if "settings" in settings:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings["settings"])
def main(argv=None) -> None:
find_pyproject()
utility = ManagementUtility(argv)
utility.execute()
except we can’t quite do that, because our uvx
command is likely running in a separate python environment.
Instead, we wrap it a bit to something like this.
from pathlib import Path
from tomllib import load
from subprocess import call
import os
import sys
def find_pyproject(path=Path.cwd() / "pyproject.toml") -> dict:
if path.exists:
with path.open("rb") as fp:
data = load(fp)
try:
settings = data["tool"]["django"]
except KeyError:
# No tool section found
return {}
else:
return settings
def main(argv=sys.argv) -> None:
settings = find_pyproject()
env = os.environ.copy()
if "settings" in settings:
# Copy our parent environment and populate our settings
env["DJANGO_SETTINGS_MODULE"] = settings["settings"]
# Then call django-admin from the current project
call(["uv", "run", "django-admin"] + argv[1:], env=env)
else:
# Otherwise, if we don't have a setting, we fallback
# to calling uvx provided django-admin directly
from django.core.management import ManagementUtility
utility = ManagementUtility(argv)
utility.execute()
Is this ultimately a good idea or useful? I’m not sure. Was still an interesting, quick experiment though.
Examples: