Skip to content

Commit

Permalink
Add start command for lower effort setup
Browse files Browse the repository at this point in the history
refs #4
  • Loading branch information
radiac committed May 17, 2024
1 parent aa71ac1 commit 83d01fc
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 36 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ def count(request):
Save that as ``counter.py``, then set up your database and run it locally with:

```sh
nanodjango run counter.py makemigrations counter
nanodjango run counter.py migrate
nanodjango run counter.py createsuperuser
nanodjango run counter.py
nanodjango start counter.py
```

It will create your database in a ``db.sqlite3`` file next to your ``counter.py``, with
the appropriate migrations in ``migrations/``.
the appropriate migrations in ``migrations/``. Alternatively you could run each of these
commands manually with the ``run`` command, eg
``nanodjango run counter.py runserver 0:8000``

Run it in production using WSGI:

Expand All @@ -67,7 +66,7 @@ gunicorn -w 4 counter:app
or automatically convert it to a full Django project:

```sh
nanodjango counter.py convert /path/to/project --name=myproject
nanodjango convert counter.py /path/to/project --name=myproject
```

and with a [couple of extra
Expand Down
13 changes: 13 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
Changelog
=========

0.6.0 - 2024-05-17
------------------

Feature:

* Add ``start`` command to create and initialise the database

Thanks to:

* Chris Beaven (SmileyChris) for suggesting a lower effort start (#4)
* Lincoln Loop for supporting this release


0.5.0 - 2024-05-14
------------------

Expand Down
8 changes: 7 additions & 1 deletion docs/get_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ Create a new file, ``counter.py`` with the following:
CountLog.objects.create()
return f"<p>Number of page loads: {CountLog.objects.count()}</p>"
Now create the migrations, apply them, and run your project:
Now use the ``start`` command to create the migrations, apply them, and run your
project:

.. code-block:: bash
nanodjango start counter.py
or you could run each step manually:

.. code-block:: bash
nanodjango run counter.py makemigrations counter
nanodjango run counter.py migrate
nanodjango run counter.py createsuperuser
Expand Down
2 changes: 1 addition & 1 deletion nanodjango/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .app import Django # noqa


__version__ = "0.5.0"
__version__ = "0.6.0"
86 changes: 58 additions & 28 deletions nanodjango/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from django import setup
from django import urls as django_urls
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.db.models import Model
from django.views import View

from . import app_meta
Expand All @@ -21,7 +23,12 @@
if TYPE_CHECKING:
from pathlib import Path

from django.db.models import Model

def exec_manage(*args):
from django.core.management import execute_from_command_line

args = ["nanodjango"] + list(args)
execute_from_command_line(args)


class Django:
Expand Down Expand Up @@ -110,24 +117,6 @@ def _config(self, _settings):
# Ready for Django's standard setup
setup()

def _prepare(self):
"""
Perform any final setup for this project after it has been imported:
* register the admin site
"""
admin_url = self.settings.ADMIN_URL
if admin_url or self.has_admin:
if admin_url is None:
admin_url = "admin/"
if not isinstance(admin_url, str) or not admin_url.endswith("/"):
raise ConfigurationError(
"settings.ADMIN_URL must be a string path ending in /"
)
urlpatterns.append(
django_urls.path(admin_url.removeprefix("/"), admin.site.urls)
)

def route(self, pattern: str, *, re=False, include=None):
"""
Decorator to add a view to the urls
Expand Down Expand Up @@ -219,13 +208,14 @@ def wrap(model: type[Model]):
# Called without arguments, @admin - call wrapped immediately
return wrap(model)

def run(self, args: list[str] | tuple[str] | None = None):
def _prepare(self):
"""
Run a Django management command, passing all arguments
Perform any final setup for this project after it has been imported:
Defaults to:
runserver 0:8000
* detect if it has been run directly; if so, register it as an app
* register the admin site
"""

# Check if this is being called from click commands or directly
if self.app_name not in sys.modules:
# Hasn't been run through the ``nanodjango`` command
Expand All @@ -239,18 +229,58 @@ def run(self, args: list[str] | tuple[str] | None = None):
# Run directly, so register app module so Django won't try to load it again
sys.modules[self.app_name] = sys.modules["__main__"]

# Register the admin site
admin_url = self.settings.ADMIN_URL
if admin_url or self.has_admin:
if admin_url is None:
admin_url = "admin/"
if not isinstance(admin_url, str) or not admin_url.endswith("/"):
raise ConfigurationError(
"settings.ADMIN_URL must be a string path ending in /"
)
urlpatterns.append(
django_urls.path(admin_url.removeprefix("/"), admin.site.urls)
)

def run(self, args: list[str] | tuple[str] | None = None):
"""
Run a Django management command, passing all arguments
Defaults to:
runserver 0:8000
"""
# Be helpful and check sys.argv for args. This will almost certainly be because
# it's running directly.
if args is None:
args = sys.argv[1:]

self._prepare()
from django.core.management import execute_from_command_line
if args:
exec_manage(*args)
else:
exec_manage("runserver", "0:8000")

def start(self, host: str | None = None):
"""
Perform app setup commands and run the server
"""
# Be helpful and check sys.argv for the host
if host is None:
print(sys.argv)
if len(sys.argv) > 2:
raise UsageError("Usage: start [HOST]")
elif len(sys.argv) == 2:
host = sys.argv[1]
if not host:
host = "0:8000"

if not args:
args = ["runserver", "0:8000"]
args = ["nanodjango"] + list(args)
execute_from_command_line(args)
self._prepare()
exec_manage("makemigrations", self.app_name)
exec_manage("migrate")
User = get_user_model()
if User.objects.count() == 0:
exec_manage("createsuperuser")
exec_manage("runserver", host)

def convert(self, path: Path, name: str):
from .convert import Converter
Expand Down
22 changes: 22 additions & 0 deletions nanodjango/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,31 @@ def cli():
@click.argument("app", type=str, required=True, callback=load_app)
@click.argument("args", type=str, required=False, nargs=-1)
def run(app: Django, args: tuple[str]):
"""
Run a management command.
If no command is specified, it will run runserver 0:8000
"""
app.run(args)


@cli.command()
@click.argument("app", type=str, required=True, callback=load_app)
@click.argument("host", type=str, required=False, default="")
def start(app: Django, host: str):
"""
Start the app on the specified IP and port
This will perform a series of setup commands:
makemigrations <app>
migrate
createsuperuser
runserver HOST
"""
app.start(host)


@cli.command()
@click.argument("app", type=str, required=True, callback=load_app)
@click.argument("path", type=click.Path(), required=True)
Expand Down

0 comments on commit 83d01fc

Please sign in to comment.