Skip to content

Commit

Permalink
Merge branch 'carltongibson:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuadavidthomas authored Feb 5, 2024
2 parents ee07b35 + 99b04c5 commit e8a913e
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 2 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ Version numbers correspond to git tags. Please use the compare view on GitHub
for full details. Until we're further along, I will just note the highlights
here:

24.2
====

* Added the ``mktemplate`` management command to create an override template from the
the active default templates for the specified model and CRUD action.

See ``./manage.py mktemplate --help`` for full details.

24.1
====

Expand Down
19 changes: 19 additions & 0 deletions docs/source/templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ Templates
You can override these templates by creating your own, either individually or as
a whole.

If you want to override a single template for your model, you can run the ``mktemplate``
management command:

.. code-block:: shell
python manage.py mktemplate myapp.MyModel --list
You pass your model in the ``app_name.ModelName`` format, and then an option for the
CRUD template you want to override. The specified template will be copied to your app's
``templates``, using your active neapolitan default templates, and having the correct
name applied.

For example, the above command will copy the active ``neapoltian/object_list.html`` template to your app's
``templates/myapp/mymodel_list.html``, where it will be picked up by a ``CRUDView`` for
``MyModel`` when serving the list view.

See ``python manage.py mktemplate --help`` for full details.


.. admonition:: Under construction 🚧

The templates are still being developed. If a change in a release affects
Expand Down
2 changes: 1 addition & 1 deletion src/neapolitan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ class BookmarkView(CRUDView):
Let's go! 🚀
"""

__version__ = "24.1"
__version__ = "24.2"
Empty file.
Empty file.
109 changes: 109 additions & 0 deletions src/neapolitan/management/commands/mktemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import shutil
from pathlib import Path

from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError
from django.template.loader import TemplateDoesNotExist, get_template
from django.template.engine import Engine

class Command(BaseCommand):
help = "Bootstrap a CRUD template for a model, copying from the active neapolitan default templates."

def add_arguments(self, parser):
parser.add_argument(
"model",
type=str,
help="The <app_name.ModelName> to bootstrap a template for.",
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"-l",
"--list",
action="store_const",
const="list",
dest="role",
help="List role",
)
group.add_argument(
"-d",
"--detail",
action="store_const",
const="detail",
dest="role",
help="Detail role",
)
group.add_argument(
"-c",
"--create",
action="store_const",
const="form",
dest="role",
help="Create role",
)
group.add_argument(
"-u",
"--update",
action="store_const",
const="form",
dest="role",
help="Update role",
)
group.add_argument(
"-f",
"--form",
action="store_const",
const="form",
dest="role",
help="Form role",
)
group.add_argument(
"--delete",
action="store_const",
const="delete",
dest="role",
help="Delete role",
)

def handle(self, *args, **options):
model = options["model"]
role = options["role"]

if role == "list":
suffix = "_list.html"
elif role == "detail":
suffix = "_detail.html"
elif role == "form":
suffix = "_form.html"
elif role == "delete":
suffix = "_confirm_delete.html"

app_name, model_name = model.split(".")
template_name = f"{app_name}/{model_name.lower()}{suffix}"
neapolitan_template_name = f"neapolitan/object{suffix}"

# Check if the template already exists.
try:
get_template(template_name)
except TemplateDoesNotExist:
# Get the filesystem path of neapolitan's object template.
neapolitan_template = get_template(neapolitan_template_name)
neapolitan_template_path = neapolitan_template.origin.name

# Find target directory.
# 1. If f"{app_name}/templates" exists, use that.
# 2. Otherwise, use first project level template dir.
target_dir = f"{app_name}/templates"
if not Path(target_dir).exists():
try:
target_dir = Engine.get_default().template_dirs[0]
except (ImproperlyConfigured, IndexError):
raise CommandError(
"No app or project level template dir found."
)
# Copy the neapolitan template to the target directory with template_name.
shutil.copyfile(neapolitan_template_path, f"{target_dir}/{template_name}")
else:
self.stdout.write(
f"Template {template_name} already exists. Remove it manually if you want to regenerate it."
)
raise CommandError("Template already exists.")
2 changes: 2 additions & 0 deletions tests/templates/tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
17 changes: 16 additions & 1 deletion tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os

from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse
from django.utils.html import escape

from neapolitan.views import CRUDView

from .models import Bookmark
Expand Down Expand Up @@ -126,3 +128,16 @@ def test_filter(self):
self.assertContains(response, self.homepage.title)
self.assertNotContains(response, self.github.title)
self.assertNotContains(response, self.fosstodon.title)


class MktemplateCommandTest(TestCase):
def test_mktemplate_command(self):
# Run the command
call_command('mktemplate', 'tests.Bookmark', '--list')

# Check if the file was created
file_path = 'tests/templates/tests/bookmark_list.html'
self.assertTrue(os.path.isfile(file_path))

# Remove the created file
os.remove(file_path)

0 comments on commit e8a913e

Please sign in to comment.