Skip to content

Commit 0a7d716

Browse files
committed
feat: Django 6.0
1 parent 2813932 commit 0a7d716

16 files changed

Lines changed: 2139 additions & 2138 deletions

File tree

.github/workflows/ci-cd.yml

Lines changed: 31 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,60 @@ name: CI/CD
22

33
on: # yamllint disable-line rule:truthy
44
pull_request:
5-
branches:
6-
- main
5+
branches: ["master", "main"]
76
push:
8-
branches:
9-
- main
7+
branches: ["master", "main"]
108
release:
11-
types:
12-
- published
9+
types: [published]
1310

1411
jobs:
1512
lint:
1613
runs-on: ubuntu-latest
1714
steps:
1815
- uses: actions/checkout@v4
1916

20-
- name: Set up Python
21-
uses: actions/setup-python@v5
17+
- name: Install uv
18+
uses: astral-sh/setup-uv@v4
2219
with:
23-
python-version: "3.11"
20+
enable-cache: true
2421

25-
- name: Install Poetry
26-
uses: snok/install-poetry@v1
27-
with:
28-
version: 1.5.1
29-
virtualenvs-create: true
30-
virtualenvs-in-project: true
31-
installer-parallel: true
22+
- name: Set up Python
23+
run: uv python install 3.13
3224

3325
- name: Install dependencies
34-
run: |
35-
python -m pip install --upgrade pip
36-
pip install tox
26+
run: uv sync --all-extras
3727

38-
- name: Black with tox
39-
run: tox -e black
40-
41-
- name: Isort with tox
42-
run: tox -e isort
28+
- name: Ruff with tox
29+
run: uv run tox -e ruff
4330

4431
- name: Pylint with tox
45-
run: tox -e pylint
32+
run: uv run tox -e pylint
4633

4734
test:
4835
runs-on: ubuntu-latest
4936
strategy:
5037
matrix:
51-
python-version: ["3.10", "3.11"]
52-
dj-version: [django42, django50]
38+
python-version: ["3.12", "3.13"]
39+
dj-version: [django52, django60]
5340

5441
steps:
5542
- uses: actions/checkout@v4
5643

57-
- name: Set up Python ${{ matrix.python-version }}
58-
uses: actions/setup-python@v5
44+
- name: Install uv
45+
uses: astral-sh/setup-uv@v4
5946
with:
60-
python-version: ${{ matrix.python-version }}
47+
enable-cache: true
6148

62-
- name: Install Poetry
63-
uses: snok/install-poetry@v1
64-
with:
65-
version: 1.5.1
66-
virtualenvs-create: true
67-
virtualenvs-in-project: true
68-
installer-parallel: true
49+
- name: Set up Python ${{ matrix.python-version }}
50+
run: uv python install ${{ matrix.python-version }}
6951

7052
- name: Install dependencies
71-
run: |
72-
python -m pip install --upgrade pip
73-
pip install tox
53+
run: uv sync --all-extras
7454

7555
- name: Test with tox
7656
run: |
7757
PY_VERSION=${{ matrix.python-version }} &&
78-
tox -e py${PY_VERSION//.}-${{ matrix.dj-version }}
58+
uv run tox -e py${PY_VERSION//.}-${{ matrix.dj-version }}
7959
8060
release:
8161
name: Release
@@ -93,34 +73,27 @@ jobs:
9373
steps:
9474
- uses: actions/checkout@v4
9575

96-
- name: Set up Python
97-
uses: actions/setup-python@v5
76+
- name: Install uv
77+
uses: astral-sh/setup-uv@v4
9878
with:
99-
python-version: "3.11"
79+
enable-cache: true
80+
81+
- name: Set up Python
82+
run: uv python install 3.13
10083

10184
- name: Set up Node
10285
uses: actions/setup-node@v4
10386
with:
104-
node-version: 18
105-
106-
- name: Install Poetry
107-
uses: snok/install-poetry@v1
108-
with:
109-
version: 1.5.1
110-
virtualenvs-create: true
111-
virtualenvs-in-project: true
112-
installer-parallel: true
87+
node-version: 22
11388

11489
- name: Install dependencies
115-
run: |
116-
npm ci
90+
run: npm install
11791

11892
- name: Build JS/CSS deps
119-
run: |
120-
npm run build
93+
run: npm run build
12194

12295
- name: Build Package
123-
run: poetry build
96+
run: uv build
12497

12598
- name: Publish package distributions to PyPI
12699
uses: pypa/gh-action-pypi-publish@release/v1

django_admin_shellx/apps.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import importlib
2+
13
from django.apps import AppConfig
24

35

@@ -7,7 +9,6 @@ class DjangoAdminShellX(AppConfig):
79

810
def ready(self):
911
try:
10-
# pylint: disable=unused-import, import-outside-toplevel
11-
import django_admin_shellx.signals
12+
importlib.import_module("django_admin_shellx.signals")
1213
except ImportError:
1314
pass

django_admin_shellx/consumers.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from django.apps import apps
1717
from django.conf import settings
1818
from django.contrib.admin.models import CHANGE, LogEntry
19-
from django.contrib.contenttypes.models import ContentType
2019

2120
from .models import TerminalCommand
2221

@@ -73,8 +72,8 @@ def run_command(self):
7372
self.child_pid = proc.pid
7473
proc.wait()
7574

76-
# Subprocess has finished, close the websocket
77-
# happens when process exits, either via user exiting using exit() or by error
75+
# Subprocess has finished, close the websocket when the process
76+
# exits, either by using exit() or by failing.
7877
self.subprocess = None
7978
self.child_pid = None
8079
if self.connected:
@@ -83,7 +82,7 @@ def run_command(self):
8382

8483
def connect(self):
8584

86-
if not "user" in self.scope:
85+
if "user" not in self.scope:
8786
self.close(4401)
8887
return
8988

@@ -192,13 +191,12 @@ def save_command_history(self, command):
192191
tc.save()
193192

194193
# Create a log entry for the command
195-
LogEntry.objects.log_action(
194+
LogEntry.objects.log_actions(
196195
user_id=self.user.id,
197-
content_type_id=ContentType.objects.get_for_model(tc).pk,
198-
object_id=tc.id,
199-
object_repr=str(tc),
196+
queryset=TerminalCommand.objects.filter(pk=tc.pk),
200197
action_flag=CHANGE,
201198
change_message={"changed": {"name": "action", "object": tc.command}},
199+
single_object=True,
202200
)
203201

204202
def receive(self, text_data=None, bytes_data=None):

django_admin_shellx/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818

1919
class TerminalView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
20-
2120
def test_func(self):
2221
super_user_required = getattr(
2322
settings, "DJANGO_ADMIN_SHELLX_SUPERUSER_ONLY", True
@@ -108,7 +107,8 @@ def toggle_favorite(request, pk):
108107
color = "text-yellow-400" if instance.favorite else ""
109108
return HttpResponse(
110109
format_html( # pyright: ignore [reportArgumentType]
111-
"<div class='tooltip' data-tip='Favorite Command'><i class='fa fa-star {}'></i></div>",
110+
"<div class='tooltip' data-tip='Favorite Command'>"
111+
"<i class='fa fa-star {}'></i></div>",
112112
mark_safe(color),
113113
)
114114
)

django_admin_shellx_custom_admin/admin.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,75 @@
1+
from functools import update_wrapper
2+
13
from django.contrib import admin
2-
from django.urls import reverse
4+
from django.contrib.admin import autodiscover
5+
from django.urls import NoReverseMatch, re_path, reverse, reverse_lazy
6+
7+
8+
class CustomAdminSite(admin.AdminSite):
9+
def _resolve_terminal_admin_url(self, app):
10+
try:
11+
return reverse("admin:django_admin_shellx_terminalcommand_terminal")
12+
except NoReverseMatch:
13+
pass
14+
15+
try:
16+
changelist_url = reverse(
17+
"admin:django_admin_shellx_terminalcommand_changelist"
18+
)
19+
return f"{changelist_url.rstrip('/')}/terminal/"
20+
except NoReverseMatch:
21+
pass
22+
23+
for model in app.get("models", []):
24+
if model.get("object_name") == "TerminalCommand" and model.get("admin_url"):
25+
return f"{model['admin_url'].rstrip('/')}/terminal/"
26+
27+
return None
328

29+
def get_urls(self):
30+
# Ensure admin modules are registered before URL patterns are built.
31+
# Some projects resolve admin URLs very early, which can otherwise
32+
# produce an empty registry and missing model/app routes.
33+
autodiscover()
434

5-
class CustomAdminSite(admin.AdminSite): # pylint: disable=too-few-public-methods
35+
urls = super().get_urls()
36+
37+
def wrap(view, cacheable=False):
38+
def wrapper(*args, **kwargs):
39+
return self.admin_view(view, cacheable)(*args, **kwargs)
40+
41+
wrapper.admin_site = self
42+
wrapper.login_url = reverse_lazy("admin:login", current_app=self.name)
43+
return update_wrapper(wrapper, view)
44+
45+
# Keep a permissive app_list route available to avoid reverse failures
46+
# when admin URLs were resolved before all apps were registered.
47+
if not any(getattr(pattern, "name", None) == "app_list" for pattern in urls):
48+
fallback = re_path(
49+
r"^(?P<app_label>.+)/$", wrap(self.app_index), name="app_list"
50+
)
51+
if self.final_catch_all_view and urls:
52+
urls.insert(-1, fallback)
53+
else:
54+
urls.append(fallback)
55+
56+
return urls
657

758
def get_app_list(self, request, app_label=None):
859
app_list = super().get_app_list(request, app_label)
960

1061
for app in app_list:
1162
if app["app_label"] == "django_admin_shellx":
63+
terminal_url = self._resolve_terminal_admin_url(app)
64+
if not terminal_url:
65+
break
66+
1267
app["models"].insert(
1368
0,
1469
{
1570
"name": "Terminal",
1671
"object_name": "Terminal",
17-
"admin_url": f"{reverse('admin:django_admin_shellx_terminalcommand_changelist')}terminal/",
72+
"admin_url": terminal_url,
1873
"view_only": True,
1974
},
2075
)

manage.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#!/usr/bin/env python
2-
from __future__ import absolute_import, unicode_literals
32

43
import os
54
import sys

0 commit comments

Comments
 (0)