plain.admin 0.17.0__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/admin/assets/admin/admin.css +20 -0
- plain/admin/cards/base.py +3 -4
- plain/admin/templates/admin/base.html +24 -25
- plain/admin/templates/admin/detail.html +3 -3
- plain/admin/templates/admin/search.html +23 -1
- plain/admin/templates/elements/admin/CheckboxField.html +1 -0
- plain/admin/templates/elements/admin/Help.html +1 -0
- plain/admin/templates/elements/admin/InputField.html +1 -0
- plain/admin/templates/elements/admin/Label.html +3 -2
- plain/admin/templates/elements/admin/SelectField.html +1 -0
- plain/admin/templates/elements/admin/Textarea.html +7 -0
- plain/admin/templates/elements/admin/TextareaField.html +6 -0
- plain/admin/templates/toolbar/toolbar.html +3 -8
- plain/admin/urls.py +0 -2
- plain/admin/views/base.py +2 -20
- plain/admin/views/models.py +17 -18
- plain/admin/views/registry.py +18 -10
- {plain_admin-0.17.0.dist-info → plain_admin-0.19.0.dist-info}/METADATA +1 -1
- {plain_admin-0.17.0.dist-info → plain_admin-0.19.0.dist-info}/RECORD +21 -19
- plain/admin/templates/admin/form.html +0 -13
- {plain_admin-0.17.0.dist-info → plain_admin-0.19.0.dist-info}/WHEEL +0 -0
- {plain_admin-0.17.0.dist-info → plain_admin-0.19.0.dist-info}/licenses/LICENSE +0 -0
@@ -61,6 +61,12 @@ main a:hover {
|
|
61
61
|
border-radius: 6px;
|
62
62
|
transition: background-color 0.2s, border-color 0.2s, transform 0.2s;
|
63
63
|
cursor: pointer;
|
64
|
+
flex-shrink: 0;
|
65
|
+
|
66
|
+
@media (max-width: 640px) {
|
67
|
+
font-size: 12px;
|
68
|
+
padding: 6px 12px;
|
69
|
+
}
|
64
70
|
|
65
71
|
&:hover {
|
66
72
|
background-color: #2a2928;
|
@@ -80,6 +86,20 @@ main a:hover {
|
|
80
86
|
}
|
81
87
|
}
|
82
88
|
|
89
|
+
main button[type="submit"] {
|
90
|
+
background-color: #2563eb;
|
91
|
+
border-color: #2563eb;
|
92
|
+
|
93
|
+
&:hover {
|
94
|
+
background-color: #1d4ed8;
|
95
|
+
border-color: #1d4ed8;
|
96
|
+
}
|
97
|
+
&:focus {
|
98
|
+
outline: 2px solid #2563eb;
|
99
|
+
outline-offset: 2px;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
83
103
|
/* Cards use these? */
|
84
104
|
section {
|
85
105
|
border: rgba(255, 255, 255, 0.1) 1px solid;
|
plain/admin/cards/base.py
CHANGED
@@ -2,7 +2,6 @@ from enum import Enum
|
|
2
2
|
|
3
3
|
from plain.http import HttpRequest
|
4
4
|
from plain.templates import Template
|
5
|
-
from plain.utils.text import slugify
|
6
5
|
from plain.views import View
|
7
6
|
|
8
7
|
|
@@ -22,7 +21,6 @@ class Card:
|
|
22
21
|
title: str
|
23
22
|
|
24
23
|
# Optional fields
|
25
|
-
slug: str = ""
|
26
24
|
description: str = ""
|
27
25
|
text: str = ""
|
28
26
|
link: str = ""
|
@@ -60,8 +58,9 @@ class Card:
|
|
60
58
|
def get_title(self) -> str:
|
61
59
|
return self.title
|
62
60
|
|
63
|
-
|
64
|
-
|
61
|
+
@classmethod
|
62
|
+
def get_slug(cls) -> str:
|
63
|
+
return f"{cls.__module__}.{cls.__name__}".lower().replace(".", "_")
|
65
64
|
|
66
65
|
def get_description(self) -> str:
|
67
66
|
return self.description
|
@@ -22,9 +22,9 @@
|
|
22
22
|
</head>
|
23
23
|
<body class="flex min-h-screen bg-stone-950">
|
24
24
|
|
25
|
-
<nav class="fixed top-0 left-0 right-0 h-14 px-4 py-2 flex items-center justify-evenly text-sm text-white/70">
|
25
|
+
<nav class="fixed top-0 left-0 right-0 h-14 px-4 py-2 flex items-center justify-between sm:justify-evenly text-sm text-white/70 space-x-3">
|
26
26
|
<div class="flex items-center space-x-2">
|
27
|
-
<button type="button" data-toggle="#admin-sidebar,#admin-content" class="
|
27
|
+
<button type="button" data-toggle="#admin-sidebar,#admin-content" class="lg:hidden">
|
28
28
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-5 h-5 bi bi-list" viewBox="0 0 16 16">
|
29
29
|
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/>
|
30
30
|
</svg>
|
@@ -32,11 +32,6 @@
|
|
32
32
|
</button>
|
33
33
|
|
34
34
|
<a class="inline-flex items-center text-stone-300" href="{{ url('admin:index') }}">
|
35
|
-
<svg class="w-5 h-5 mr-2" width="160" height="125" viewBox="0 0 160 125" fill="none" xmlns="http://www.w3.org/2000/svg">
|
36
|
-
<rect x="4.78467" y="4.79785" width="150.978" height="115.404" rx="5" stroke="#ffffff" stroke-width="8"/>
|
37
|
-
<path d="M151.762 60.3705C99.2596 39.3233 80.202 66.8232 8.78467 60.3705V116.2H151.762V60.3705Z" fill="#ffffff"/>
|
38
|
-
<path d="M51.104 8.08887H108.179V10.7668C108.179 12.6998 106.612 14.2668 104.679 14.2668H54.604C52.671 14.2668 51.104 12.6998 51.104 10.7668V8.08887Z" fill="#ffffff" stroke="#ffffff"/>
|
39
|
-
</svg>
|
40
35
|
<span class="text-stone-400">Admin</span>
|
41
36
|
</a>
|
42
37
|
|
@@ -50,14 +45,14 @@
|
|
50
45
|
#}
|
51
46
|
</div>
|
52
47
|
|
53
|
-
<form method="GET" action="{{ url('admin:search') }}" class="flex flex-1 justify-center">
|
54
|
-
<div class="relative max-w-xs">
|
48
|
+
<form method="GET" action="{{ url('admin:search') }}" class="hidden sm:flex flex-1 justify-center">
|
49
|
+
<div class="relative max-w-xs flex flex-1">
|
55
50
|
<label for="query" class="sr-only">Search</label>
|
56
51
|
<input
|
57
52
|
type="text"
|
58
53
|
name="query"
|
59
54
|
id="query"
|
60
|
-
class="block w-full pr-10 pl-10 placeholder:text-center text-sm border-gray-200/10 text-white rounded-md focus:border-blue-500 focus:ring-blue-500 bg-white/5 py-1"
|
55
|
+
class="block w-full pr-10 pl-10 placeholder:text-white/30 placeholder:text-center text-sm border-gray-200/10 text-white rounded-md focus:border-blue-500 focus:ring-blue-500 bg-white/5 py-1"
|
61
56
|
placeholder="Search everything"
|
62
57
|
value="{{ global_search_query|default('') }}"
|
63
58
|
>
|
@@ -99,23 +94,26 @@
|
|
99
94
|
<div class="text-xs tracking-wide text-stone-500">Recent</div>
|
100
95
|
</div> -->
|
101
96
|
</div>
|
102
|
-
<div class="
|
103
|
-
<
|
104
|
-
|
105
|
-
|
106
|
-
<
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
97
|
+
<div class="mt-8 flex flex-col text-sm pb-3 pt-3 text-stone-400 sticky bottom-0 bg-stone-950">
|
98
|
+
<a class="sm:hidden py-1" href="{{ url('admin:search') }}">Global search</a>
|
99
|
+
<div class="flex items-center justify-between">
|
100
|
+
<div class="flex items-center truncate">
|
101
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-6 h-6 mr-1.5 bi bi-person-circle" viewBox="0 0 16 16">
|
102
|
+
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
103
|
+
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
104
|
+
</svg>
|
105
|
+
<span class="truncate">
|
106
|
+
{{ request.user }}
|
107
|
+
</span>
|
108
|
+
</div>
|
109
|
+
<a class="ml-2 hover:text-white hover:underline flex-shrink-0" href="{{ url('logout') }}">Log out</a>
|
111
110
|
</div>
|
112
|
-
<a class="ml-2 hover:text-white hover:underline flex-shrink-0" href="{{ url('logout') }}">Log out</a>
|
113
111
|
</div>
|
114
112
|
</aside>
|
115
113
|
|
116
114
|
<div id="admin-content" data-toggle-class="x" class="absolute top-0 bottom-0 right-0 left-0 lg:left-52 bg-stone-900 border border-white/5 text-white rounded-lg overflow-auto">
|
117
|
-
<div class="flex items-center justify-between px-4 lg:px-8 sticky z-10 top-0 bg-stone-900 border-b border-white/10 py-2 lg:py-3">
|
118
|
-
<div>
|
115
|
+
<div class="flex space-x-3 items-center justify-between px-4 lg:px-8 sticky z-10 top-0 left-0 right-0 bg-stone-900 border-b border-white/10 py-2 lg:py-3">
|
116
|
+
<div class="flex-shrink-0">
|
119
117
|
{% block header %}
|
120
118
|
<div class="flex items-center">
|
121
119
|
{% block image %}
|
@@ -124,7 +122,7 @@
|
|
124
122
|
{% endif %}
|
125
123
|
{% endblock %}
|
126
124
|
<div class="max-w-prose break-all">
|
127
|
-
<h1 class="text-xl text-white/90">
|
125
|
+
<h1 class="sm:text-xl text-white/90">
|
128
126
|
{% block title %}{{ title }}{% endblock %}
|
129
127
|
</h1>
|
130
128
|
{% if description %}<p class="mt-1 text-sm text-gray-500">{{ description }}</p>{% endif %}
|
@@ -132,7 +130,8 @@
|
|
132
130
|
</div>
|
133
131
|
{% endblock %}
|
134
132
|
</div>
|
135
|
-
<div class="flex space-x-2 text-sm actions">
|
133
|
+
<div class="flex space-x-2 text-sm actions overflow-auto p-1">
|
134
|
+
{# Styled with admin.css for easier adding of elements #}
|
136
135
|
{% block actions %}{% endblock %}
|
137
136
|
{% for link, url in links.items() %}
|
138
137
|
<a href="{{ url }}">{{ link }}</a>
|
@@ -150,7 +149,7 @@
|
|
150
149
|
</div>
|
151
150
|
{% endif %}
|
152
151
|
|
153
|
-
<main class="px-4 py-6 lg:px-8 text-white/
|
152
|
+
<main class="px-4 py-6 lg:px-8 text-white/80">{% block content %}{% endblock %}</main>
|
154
153
|
</div>
|
155
154
|
|
156
155
|
</div>
|
@@ -9,10 +9,10 @@
|
|
9
9
|
Toggle raw values
|
10
10
|
</button>
|
11
11
|
|
12
|
-
<dl class="text-sm mt-3 w-full grid grid-cols-[max-content,1fr] gap-y-2 gap-x-8" style="grid-template-columns: max-content 1fr;">
|
12
|
+
<dl class="text-sm mt-3 w-full grid !grid-cols-1 sm:!grid-cols-[max-content,1fr] sm:gap-y-2 gap-x-8" style="grid-template-columns: max-content 1fr;">
|
13
13
|
{% for field in fields %}
|
14
|
-
<dt class="font-
|
15
|
-
<dd class="flex items-center">
|
14
|
+
<dt class="font-medium mt-4 sm:mt-0"><code>{{ field }}</code></dt>
|
15
|
+
<dd class="flex items-center mt-1 sm:mt-0">
|
16
16
|
{% with value=get_field_value(object, field) %}
|
17
17
|
<div class="raw-value hidden"><code>{{ value }}</code></div>
|
18
18
|
<div class="pretty-value">{% include get_field_value_template(object, field, value) with context %}</div>
|
@@ -10,6 +10,28 @@ Search
|
|
10
10
|
|
11
11
|
{% block content %}
|
12
12
|
|
13
|
+
{# Mobile search bar #}
|
14
|
+
<div class="sm:hidden">
|
15
|
+
<form method="GET" action="{{ url('admin:search') }}" class="">
|
16
|
+
<div class="relative">
|
17
|
+
<label for="query" class="sr-only">Search</label>
|
18
|
+
<input
|
19
|
+
type="text"
|
20
|
+
name="query"
|
21
|
+
id="query"
|
22
|
+
class="block w-full pr-10 pl-10 placeholder:text-center border-gray-200/10 text-white rounded-md focus:border-blue-500 focus:ring-blue-500 bg-white/5 py-1"
|
23
|
+
placeholder="Search everything"
|
24
|
+
value="{{ global_search_query|default('') }}"
|
25
|
+
>
|
26
|
+
<div class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none">
|
27
|
+
<svg class="h-3.5 w-3.5 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
28
|
+
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path>
|
29
|
+
</svg>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</form>
|
33
|
+
</div>
|
34
|
+
|
13
35
|
{% if global_search_query %}
|
14
36
|
<div class="*:mt-14 *:empty:mt-0">
|
15
37
|
{% for view in searchable_views %}
|
@@ -21,7 +43,7 @@ Search
|
|
21
43
|
{% endfor %}
|
22
44
|
</div>
|
23
45
|
{% else %}
|
24
|
-
<p class="text-stone-500">Enter a search query in the top bar</p>
|
46
|
+
<p class="text-stone-500 hidden sm:inline">Enter a search query in the top bar</p>
|
25
47
|
{% endif %}
|
26
48
|
|
27
49
|
{% endblock %}
|
@@ -0,0 +1 @@
|
|
1
|
+
<p class="mt-2 text-sm text-gray-500">{{ help }}</p>
|
@@ -1,3 +1,4 @@
|
|
1
|
-
<label for="{{ field.html_id }}" class="block
|
2
|
-
{{ caller() }}
|
1
|
+
<label for="{{ field.html_id }}" class="block flex items-baseline justify-between">
|
2
|
+
<div class="text-sm font-medium">{{ caller() }}</div>
|
3
|
+
{% if not field.field.required %}<div class="text-white/60 text-xs">Optional</div>{% endif %}
|
3
4
|
</label>
|
@@ -1,15 +1,10 @@
|
|
1
1
|
{% if toolbar.should_render() %}
|
2
2
|
{% set exception=toolbar.request_exception() %}
|
3
3
|
<script src="{{ asset('toolbar/toolbar.js') }}"></script>
|
4
|
-
<div id="plaintoolbar" class="print:hidden text-sm py-1.5 text-stone-300
|
5
|
-
<div class="flex justify-between px-
|
4
|
+
<div id="plaintoolbar" class="print:hidden text-sm py-1.5 text-stone-300 fixed bottom-3 mx-3 max-w-full drop-shadow-sm z-30 ring-1 ring-stone-200/5 rounded-2xl lg:flex lg:flex-col -translate-x-1/2 left-1/2 max-h-[90vh] bg-gradient-to-b from-stone-950/90 to-stone-950/95 backdrop-blur-sm">
|
5
|
+
<div class="flex justify-between px-3 mx-auto space-x-4">
|
6
6
|
<div class="flex items-center">
|
7
|
-
<
|
8
|
-
<rect x="4.78467" y="4.79785" width="150.978" height="115.404" rx="5" stroke="#ffffff" stroke-width="8"/>
|
9
|
-
<path d="M151.762 60.3705C99.2596 39.3233 80.202 66.8232 8.78467 60.3705V116.2H151.762V60.3705Z" fill="#ffffff"/>
|
10
|
-
<path d="M51.104 8.08887H108.179V10.7668C108.179 12.6998 106.612 14.2668 104.679 14.2668H54.604C52.671 14.2668 51.104 12.6998 51.104 10.7668V8.08887Z" fill="#ffffff" stroke="#ffffff"/>
|
11
|
-
</svg>
|
12
|
-
<code class="ml-2 text-xs whitespace-nowrap text-mono">{{ toolbar.version }}</code>
|
7
|
+
<code class="ml-1.5 text-xs whitespace-nowrap text-mono">{{ toolbar.version }}</code>
|
13
8
|
|
14
9
|
{% if request.impersonator is defined %}
|
15
10
|
<div class="flex items-center ml-1 font-light">
|
plain/admin/urls.py
CHANGED
@@ -10,7 +10,6 @@ from .views.registry import registry
|
|
10
10
|
class AdminIndexView(AdminView):
|
11
11
|
template_name = "admin/index.html"
|
12
12
|
title = "Dashboard"
|
13
|
-
slug = ""
|
14
13
|
|
15
14
|
def get(self):
|
16
15
|
# Slight hack to redirect to the first view that doesn't
|
@@ -24,7 +23,6 @@ class AdminIndexView(AdminView):
|
|
24
23
|
class AdminSearchView(AdminView):
|
25
24
|
template_name = "admin/search.html"
|
26
25
|
title = "Search"
|
27
|
-
slug = "search"
|
28
26
|
|
29
27
|
def get_template_context(self):
|
30
28
|
context = super().get_template_context()
|
plain/admin/views/base.py
CHANGED
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from plain.auth.views import AuthViewMixin
|
4
4
|
from plain.urls import reverse
|
5
5
|
from plain.utils import timezone
|
6
|
-
from plain.utils.text import slugify
|
7
6
|
from plain.views import (
|
8
7
|
TemplateView,
|
9
8
|
)
|
@@ -22,7 +21,6 @@ class AdminView(AuthViewMixin, TemplateView):
|
|
22
21
|
admin_required = True
|
23
22
|
|
24
23
|
title: str = ""
|
25
|
-
slug: str = ""
|
26
24
|
path: str = ""
|
27
25
|
description: str = ""
|
28
26
|
image: Img | None = None
|
@@ -69,15 +67,7 @@ class AdminView(AuthViewMixin, TemplateView):
|
|
69
67
|
|
70
68
|
@classmethod
|
71
69
|
def get_slug(cls) -> str:
|
72
|
-
|
73
|
-
return cls.slug
|
74
|
-
|
75
|
-
if cls.title:
|
76
|
-
return slugify(cls.title)
|
77
|
-
|
78
|
-
raise NotImplementedError(
|
79
|
-
f"Please set a slug on the {cls} class or implement get_slug()."
|
80
|
-
)
|
70
|
+
return f"{cls.__module__}.{cls.__name__}".lower().replace(".", "_")
|
81
71
|
|
82
72
|
# Can actually use @classmethod, @staticmethod or regular method for these?
|
83
73
|
def get_title(self) -> str:
|
@@ -91,15 +81,7 @@ class AdminView(AuthViewMixin, TemplateView):
|
|
91
81
|
|
92
82
|
@classmethod
|
93
83
|
def get_path(cls) -> str:
|
94
|
-
|
95
|
-
return cls.path
|
96
|
-
|
97
|
-
if slug := cls.get_slug():
|
98
|
-
return slug
|
99
|
-
|
100
|
-
raise NotImplementedError(
|
101
|
-
f"Please set a path on the {cls} class or implement get_slug() or get_path()."
|
102
|
-
)
|
84
|
+
return cls.path
|
103
85
|
|
104
86
|
@classmethod
|
105
87
|
def get_parent_view_classes(cls) -> list["AdminView"]:
|
plain/admin/views/models.py
CHANGED
@@ -68,8 +68,11 @@ class AdminModelListView(AdminListView):
|
|
68
68
|
return cls.model._meta.model_name.capitalize() + "s"
|
69
69
|
|
70
70
|
@classmethod
|
71
|
-
def
|
72
|
-
|
71
|
+
def get_path(cls) -> str:
|
72
|
+
if path := super().get_path():
|
73
|
+
return path
|
74
|
+
|
75
|
+
return f"{cls.model._meta.model_name}/"
|
73
76
|
|
74
77
|
def get_template_context(self):
|
75
78
|
context = super().get_template_context()
|
@@ -137,12 +140,11 @@ class AdminModelDetailView(AdminDetailView):
|
|
137
140
|
def get_title(self) -> str:
|
138
141
|
return str(self.object)
|
139
142
|
|
140
|
-
@classmethod
|
141
|
-
def get_slug(cls) -> str:
|
142
|
-
return f"{cls.model._meta.model_name}_detail"
|
143
|
-
|
144
143
|
@classmethod
|
145
144
|
def get_path(cls) -> str:
|
145
|
+
if path := super().get_path():
|
146
|
+
return path
|
147
|
+
|
146
148
|
return f"{cls.model._meta.model_name}/<int:pk>/"
|
147
149
|
|
148
150
|
def get_fields(self):
|
@@ -182,12 +184,11 @@ class AdminModelCreateView(AdminCreateView):
|
|
182
184
|
|
183
185
|
return f"New {self.model._meta.model_name}"
|
184
186
|
|
185
|
-
@classmethod
|
186
|
-
def get_slug(cls) -> str:
|
187
|
-
return f"{cls.model._meta.model_name}_create"
|
188
|
-
|
189
187
|
@classmethod
|
190
188
|
def get_path(cls) -> str:
|
189
|
+
if path := super().get_path():
|
190
|
+
return path
|
191
|
+
|
191
192
|
return f"{cls.model._meta.model_name}/create/"
|
192
193
|
|
193
194
|
def get_template_names(self):
|
@@ -213,12 +214,11 @@ class AdminModelUpdateView(AdminUpdateView):
|
|
213
214
|
|
214
215
|
return f"Update {self.object}"
|
215
216
|
|
216
|
-
@classmethod
|
217
|
-
def get_slug(cls) -> str:
|
218
|
-
return f"{cls.model._meta.model_name}_update"
|
219
|
-
|
220
217
|
@classmethod
|
221
218
|
def get_path(cls) -> str:
|
219
|
+
if path := super().get_path():
|
220
|
+
return path
|
221
|
+
|
222
222
|
return f"{cls.model._meta.model_name}/<int:pk>/update/"
|
223
223
|
|
224
224
|
def get_object(self):
|
@@ -242,12 +242,11 @@ class AdminModelDeleteView(AdminDeleteView):
|
|
242
242
|
def get_title(self) -> str:
|
243
243
|
return f"Delete {self.object}"
|
244
244
|
|
245
|
-
@classmethod
|
246
|
-
def get_slug(cls) -> str:
|
247
|
-
return f"{cls.model._meta.model_name}_delete"
|
248
|
-
|
249
245
|
@classmethod
|
250
246
|
def get_path(cls) -> str:
|
247
|
+
if path := super().get_path():
|
248
|
+
return path
|
249
|
+
|
251
250
|
return f"{cls.model._meta.model_name}/<int:pk>/delete/"
|
252
251
|
|
253
252
|
def get_object(self):
|
plain/admin/views/registry.py
CHANGED
@@ -67,18 +67,26 @@ class AdminViewRegistry:
|
|
67
67
|
def get_urls(self):
|
68
68
|
urls = []
|
69
69
|
|
70
|
-
paths_seen =
|
71
|
-
|
72
|
-
def add_view_path(view, _path):
|
73
|
-
if _path in paths_seen:
|
74
|
-
raise ValueError(f"Path {_path} already registered")
|
75
|
-
paths_seen.add(_path)
|
76
|
-
if not _path.endswith("/"):
|
77
|
-
_path += "/"
|
78
|
-
urls.append(path(_path, view, name=view.view_name()))
|
70
|
+
paths_seen = {}
|
79
71
|
|
80
72
|
for view in self.registered_views:
|
81
|
-
|
73
|
+
view_path = view.get_path()
|
74
|
+
|
75
|
+
if not view_path:
|
76
|
+
raise ValueError(f"Path for {view} is empty")
|
77
|
+
|
78
|
+
if existing_view := paths_seen.get(view_path, None):
|
79
|
+
raise ValueError(
|
80
|
+
f"Duplicate admin path {view_path}\n{existing_view}\n{view}"
|
81
|
+
)
|
82
|
+
|
83
|
+
paths_seen[view_path] = view
|
84
|
+
|
85
|
+
# Append trailing slashes automatically
|
86
|
+
if not view_path.endswith("/"):
|
87
|
+
view_path += "/"
|
88
|
+
|
89
|
+
urls.append(path(f"p/{view_path}", view, name=view.view_name()))
|
82
90
|
|
83
91
|
return urls
|
84
92
|
|
@@ -6,8 +6,8 @@ plain/admin/default_settings.py,sha256=j7RdgGqksCmCgPO7zCcFiVV9f8yW-EULvqDcFOhQa
|
|
6
6
|
plain/admin/middleware.py,sha256=k3yP1o3CzvLiZZSoxqq-DvAZlp4sICRauaT-kD3FJKM,398
|
7
7
|
plain/admin/templates.py,sha256=jLhJkuvqnPMBQTP-kzojFaqmFi50GZHvrVzuZCLc3rk,836
|
8
8
|
plain/admin/toolbar.py,sha256=dsZa_I-tTbaeOluCbvHGEqy4_Suw6Q_JSrKl8Eu08qY,973
|
9
|
-
plain/admin/urls.py,sha256=
|
10
|
-
plain/admin/assets/admin/admin.css,sha256=
|
9
|
+
plain/admin/urls.py,sha256=Q44_HpivSHJfDQWJLtm_OQVWm0VQlqAg8sa1M1NdxFM,1331
|
10
|
+
plain/admin/assets/admin/admin.css,sha256=qSM3Q99I2YbWxNmRjSORz9yCZ-H7oeGppTFE4QzByvk,2580
|
11
11
|
plain/admin/assets/admin/admin.js,sha256=AWD6UqPxGqJFaUhYTDWe4niTgk0thzU4gRl7qK41KNc,2759
|
12
12
|
plain/admin/assets/admin/chart.js,sha256=GZiCYXjL6SmyuSCGE0Df80QvOUkw6H2YD-zsVID05lo,205089
|
13
13
|
plain/admin/assets/admin/jquery-3.6.1.slim.min.js,sha256=W2eb4M1jdKpuZ_-_KnDgqI9X9SwGLrXtO0dknpNPJyE,72534
|
@@ -16,7 +16,7 @@ plain/admin/assets/admin/popper.min.js,sha256=SgCxkjQZdrt2puqn62YUu9hknpCBGBEAy9
|
|
16
16
|
plain/admin/assets/admin/tippy-bundle.umd.min.js,sha256=oVWBpeGTKMG_iBWGkQF02JnGIMFPYuFqTjUWeJY3pZ0,25668
|
17
17
|
plain/admin/assets/toolbar/toolbar.js,sha256=kRCQ37iQNklzBjjBeHSeBU39mLpQ4Q0pnC3cdbQAy28,1636
|
18
18
|
plain/admin/cards/__init__.py,sha256=8NfWrguyJRriJFUc3_QeGaDILhgeU3d1aXktzIuAR1E,172
|
19
|
-
plain/admin/cards/base.py,sha256=
|
19
|
+
plain/admin/cards/base.py,sha256=g9t-pQq8O8gqMbTVdTujacfDQguFMq_aoRIKZkme_SA,2238
|
20
20
|
plain/admin/cards/charts.py,sha256=fbCypn4_2uhFnNgj7z1T7bhSjQVtlxODnctynI6yrqI,5017
|
21
21
|
plain/admin/cards/tables.py,sha256=lGUBeSaBsNVuzINVH8qU-1XF0PfPY03gcUKtN-462zE,599
|
22
22
|
plain/admin/impersonate/README.md,sha256=GT7ubMxyB2RhUh-gDg_yYqWSm7oMp0hy1LepXyDRMo8,1012
|
@@ -33,14 +33,13 @@ plain/admin/querystats/core.py,sha256=GLhKwWwO2OwN2wneAgfbKRQzIIjZqegZYb1fMVwilj
|
|
33
33
|
plain/admin/querystats/middleware.py,sha256=M1EVdX11H545IdZlppbSIL_h8hzBIrMELrYrcAb4aq0,3192
|
34
34
|
plain/admin/querystats/urls.py,sha256=SHYbWvxjg6kFHR-e6T4vFmh4VqyPG7gYHXxDKdB4qz0,230
|
35
35
|
plain/admin/querystats/views.py,sha256=58UpxaBp_H80Tf7azi4QcphgHbXgP5iqLDf-qZJfzRI,788
|
36
|
-
plain/admin/templates/admin/base.html,sha256=
|
36
|
+
plain/admin/templates/admin/base.html,sha256=pLv6gKOSwTaNumhzl2ThLD--lq3u-aBVnSt64f-lkdI,8307
|
37
37
|
plain/admin/templates/admin/delete.html,sha256=lNuU2G-BR6TH6NUmh7VcvjnEuFeI84rwwk_oO1jkUq0,431
|
38
|
-
plain/admin/templates/admin/detail.html,sha256=
|
39
|
-
plain/admin/templates/admin/form.html,sha256=Cc9zKjYbs_W3TaoO5teLhg35eb47JYYXEJH55wA760E,289
|
38
|
+
plain/admin/templates/admin/detail.html,sha256=NOEUvZlHM5lvpq6ZnfMYS6UUBCMSya4CmAHxZr-joJQ,787
|
40
39
|
plain/admin/templates/admin/index.html,sha256=b65tcZhv9QfvmjePySU7MmzUlpMECIXP8dBH-a3Eyxw,69
|
41
40
|
plain/admin/templates/admin/list.html,sha256=RFtGZf_g_IDiPc8udchgf63mMpfEsvzS4hUKqkvDjoM,8781
|
42
41
|
plain/admin/templates/admin/page.html,sha256=wzRR-JLs8CgCOoB3BMoYWqTMpYM0z4X2qlqdwAe0YjM,67
|
43
|
-
plain/admin/templates/admin/search.html,sha256=
|
42
|
+
plain/admin/templates/admin/search.html,sha256=zfwnXoztAFnj8OmwxJcWaqo-SKCy50bLwfwSrAnAtoQ,1799
|
44
43
|
plain/admin/templates/admin/cards/base.html,sha256=2HRIxvt5Kf0MPVv7XLQZcc7vfz3YR_WLsrVgbQtyN5I,933
|
45
44
|
plain/admin/templates/admin/cards/card.html,sha256=OWR1kF4vKtr06x_Q34Z01UmKEv_Jdq2Ws3v3RARaxCY,263
|
46
45
|
plain/admin/templates/admin/cards/chart.html,sha256=boQRaWXiZvwKkMudT3IDsRvaofv5LHgbSeWr_HEGghg,642
|
@@ -56,25 +55,28 @@ plain/admin/templates/admin/values/list.html,sha256=vqUOuJglFDbOBuKCA11VoIZHWVt_
|
|
56
55
|
plain/admin/templates/admin/values/model.html,sha256=_aX_t2VQYt_bN_jQBky5IDi49cbq7dzBLuisb3BY_is,410
|
57
56
|
plain/admin/templates/admin/values/queryset.html,sha256=YU-mDxHzinWWLUBE-oX3dOMOMyHymzrdeZyxXGMF5Ss,138
|
58
57
|
plain/admin/templates/elements/admin/Checkbox.html,sha256=2hUSWCbazaJKyZdsk2shF0qN6kSeV20HVLdRitC_KfQ,213
|
59
|
-
plain/admin/templates/elements/admin/CheckboxField.html,sha256=
|
58
|
+
plain/admin/templates/elements/admin/CheckboxField.html,sha256=oj8ur5fX5ftOjhHEQ8QcFobnPBjXrEfxFxzVpwU8-nw,274
|
60
59
|
plain/admin/templates/elements/admin/FieldErrors.html,sha256=YO150DwGG8tf8Q4d1Cf59gpchXzF-n8FSse2GqOX3cA,108
|
60
|
+
plain/admin/templates/elements/admin/Help.html,sha256=qivAiGNW97Ht9Vmq09m5bIk1IbMLlYuRIFzClVll5_Y,53
|
61
61
|
plain/admin/templates/elements/admin/Input.html,sha256=7rziKkGDgg-fQ4Yfb_hjR9pOt0DFs8UeXicN6MCoM4s,371
|
62
|
-
plain/admin/templates/elements/admin/InputField.html,sha256=
|
63
|
-
plain/admin/templates/elements/admin/Label.html,sha256=
|
62
|
+
plain/admin/templates/elements/admin/InputField.html,sha256=iZuhlGxWWQwmXsYXjGHF-5V8En24EWa7HGanDRwNvUs,220
|
63
|
+
plain/admin/templates/elements/admin/Label.html,sha256=RVZZjrv4ff_ujSmKu2M7HngIycImGhPXbja-kF03OC8,249
|
64
64
|
plain/admin/templates/elements/admin/Select.html,sha256=CUJD4cHno_bc0g_SQN0IV0a5sZL7Gx4fFB44xBse-Ic,458
|
65
|
-
plain/admin/templates/elements/admin/SelectField.html,sha256=
|
65
|
+
plain/admin/templates/elements/admin/SelectField.html,sha256=P2-vXifOs2-ie20AgLyni6Oy0iz0dAeOxBBGrZV4nIg,221
|
66
66
|
plain/admin/templates/elements/admin/Submit.html,sha256=1Lgn3Du9rXplbM3V12z2JckSaiWPlPGLP48xIZ887AA,150
|
67
|
+
plain/admin/templates/elements/admin/Textarea.html,sha256=zi9A_NAsXdZXpVTAS1KXNjQyhtVrqjyQvTHUPtf64uM,275
|
68
|
+
plain/admin/templates/elements/admin/TextareaField.html,sha256=4IOJapBNEfhUpMkkLW-gliIefZCEMn5aKyW4QagfcNw,223
|
67
69
|
plain/admin/templates/querystats/querystats.html,sha256=CMH3TDBXXxoxrICMIxiLfo4cN7ae9DMCg3WNmZR8M_o,3504
|
68
70
|
plain/admin/templates/querystats/toolbar.html,sha256=dePs614akVWUD8IlgzvQ0TREThv1ttKPj-yOPzJxmXM,3574
|
69
|
-
plain/admin/templates/toolbar/toolbar.html,sha256=
|
71
|
+
plain/admin/templates/toolbar/toolbar.html,sha256=KcGAG6kRmx60wfqEsdD5C4nDMilH-JvPjHoU6EktfaY,5985
|
70
72
|
plain/admin/views/__init__.py,sha256=nF6AENZ3Xxyi08OTRrF6e-HYBkZSFj7XBK2mVzMYqN4,846
|
71
|
-
plain/admin/views/base.py,sha256=
|
72
|
-
plain/admin/views/models.py,sha256=
|
73
|
+
plain/admin/views/base.py,sha256=0EZPhc7h7QKj8-jhZ2pLk1Pe-eTIp4dL5_JwY3F8gY8,3508
|
74
|
+
plain/admin/views/models.py,sha256=M4qoz3IpZuD_9DG25vKUNnfijAJhj33sXSciw2edkGE,7205
|
73
75
|
plain/admin/views/objects.py,sha256=5jipTCJBnypM8nyD9YF-i2MLT9E4aWJbIK2GNMdtel0,11319
|
74
|
-
plain/admin/views/registry.py,sha256=
|
76
|
+
plain/admin/views/registry.py,sha256=Lxib4YSQCMHb_zACnLKymJakV8jCZPWYll7J8-aV9Xw,3712
|
75
77
|
plain/admin/views/types.py,sha256=ONMMdUoapgMoUVYgSIe-4YCdfvaVMQ4jgPWYiMo0pDk,178
|
76
78
|
plain/admin/views/viewsets.py,sha256=dqMlQ6kLn9iqd9BwBWAZT1S271wH1FdfM5HXbOgBMEw,1655
|
77
|
-
plain_admin-0.
|
78
|
-
plain_admin-0.
|
79
|
-
plain_admin-0.
|
80
|
-
plain_admin-0.
|
79
|
+
plain_admin-0.19.0.dist-info/METADATA,sha256=Ji-WLwjWtFpvVTKPI7mEBBwWMQB-hJ1zoZ09B4QAqqg,6821
|
80
|
+
plain_admin-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
81
|
+
plain_admin-0.19.0.dist-info/licenses/LICENSE,sha256=cvKM3OlqHx3ijD6e34zsSUkPvzl-ya3Dd63A6EHL94U,1500
|
82
|
+
plain_admin-0.19.0.dist-info/RECORD,,
|
@@ -1,13 +0,0 @@
|
|
1
|
-
{% extends "admin/base.html" %}
|
2
|
-
|
3
|
-
{% block content %}
|
4
|
-
|
5
|
-
<form method="post">
|
6
|
-
{{ csrf_input }}
|
7
|
-
{% block form_content %}{% endblock %}
|
8
|
-
<button class="px-5 py-2 mt-6 text-white bg-blue-600 rounded hover:bg-blue-700" type="submit">
|
9
|
-
Save
|
10
|
-
</button>
|
11
|
-
</form>
|
12
|
-
|
13
|
-
{% endblock %}
|
File without changes
|
File without changes
|