django-bananas 2.3__py3-none-any.whl → 2.4__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.
- bananas/__init__.py +1 -1
- bananas/admin/api/views.py +3 -3
- bananas/admin/extension.py +6 -8
- bananas/drf/fencing.py +4 -6
- bananas/environment.py +23 -38
- bananas/models.py +1 -2
- bananas/query.py +13 -10
- bananas/secrets.py +2 -4
- bananas/static/admin/bananas/css/bananas.css +48 -28
- bananas/static/admin/bananas/css/banansive.css +17 -4
- bananas/templates/admin/base_site.html +4 -1
- bananas/url.py +1 -0
- {django_bananas-2.3.dist-info → django_bananas-2.4.dist-info}/METADATA +5 -2
- {django_bananas-2.3.dist-info → django_bananas-2.4.dist-info}/RECORD +17 -17
- {django_bananas-2.3.dist-info → django_bananas-2.4.dist-info}/WHEEL +1 -1
- {django_bananas-2.3.dist-info → django_bananas-2.4.dist-info/licenses}/LICENSE +0 -0
- {django_bananas-2.3.dist-info → django_bananas-2.4.dist-info}/top_level.txt +0 -0
bananas/__init__.py
CHANGED
bananas/admin/api/views.py
CHANGED
|
@@ -53,7 +53,7 @@ class LoginAPI(BananasAdminAPI):
|
|
|
53
53
|
login_form = AuthenticationForm(request, data=request.data)
|
|
54
54
|
|
|
55
55
|
if not login_form.is_valid():
|
|
56
|
-
raise serializers.ValidationError(login_form.errors)
|
|
56
|
+
raise serializers.ValidationError(login_form.errors)
|
|
57
57
|
|
|
58
58
|
auth_login(request, login_form.get_user())
|
|
59
59
|
|
|
@@ -111,10 +111,10 @@ class ChangePasswordAPI(BananasAdminAPI):
|
|
|
111
111
|
password_form = PasswordChangeForm(request.user, data=request.data)
|
|
112
112
|
|
|
113
113
|
if not password_form.is_valid():
|
|
114
|
-
raise serializers.ValidationError(password_form.errors)
|
|
114
|
+
raise serializers.ValidationError(password_form.errors)
|
|
115
115
|
|
|
116
116
|
password_form.save()
|
|
117
|
-
update_session_auth_hash(request, password_form.user)
|
|
117
|
+
update_session_auth_hash(request, password_form.user)
|
|
118
118
|
|
|
119
119
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
120
120
|
|
bananas/admin/extension.py
CHANGED
|
@@ -133,12 +133,12 @@ class ModelAdminView(ModelAdmin):
|
|
|
133
133
|
return perm
|
|
134
134
|
|
|
135
135
|
def has_module_permission(self, request: HttpRequest) -> bool:
|
|
136
|
-
return request.user.has_perm(self.access_permission)
|
|
136
|
+
return bool(request.user.has_perm(self.access_permission))
|
|
137
137
|
|
|
138
138
|
def has_change_permission(
|
|
139
139
|
self, request: HttpRequest, obj: Optional[MT] = None
|
|
140
140
|
) -> bool:
|
|
141
|
-
return request.user.has_perm(self.access_permission)
|
|
141
|
+
return bool(request.user.has_perm(self.access_permission))
|
|
142
142
|
|
|
143
143
|
# TODO: Remove obj?
|
|
144
144
|
def has_add_permission(
|
|
@@ -175,8 +175,7 @@ def register(
|
|
|
175
175
|
*,
|
|
176
176
|
admin_site: Optional[AdminSite] = None,
|
|
177
177
|
admin_class: Type[ModelAdmin] = ModelAdminView,
|
|
178
|
-
) -> Type["AdminView"]:
|
|
179
|
-
...
|
|
178
|
+
) -> Type["AdminView"]: ...
|
|
180
179
|
|
|
181
180
|
|
|
182
181
|
# Call with parenthesis: @register()
|
|
@@ -186,8 +185,7 @@ def register(
|
|
|
186
185
|
*,
|
|
187
186
|
admin_site: Optional[AdminSite] = None,
|
|
188
187
|
admin_class: Type[ModelAdmin] = ModelAdminView,
|
|
189
|
-
) -> Callable[[Type["AdminView"]], Type["AdminView"]]:
|
|
190
|
-
...
|
|
188
|
+
) -> Callable[[Type["AdminView"]], Type["AdminView"]]: ...
|
|
191
189
|
|
|
192
190
|
|
|
193
191
|
def register(
|
|
@@ -236,7 +234,7 @@ def register(
|
|
|
236
234
|
inner_view.verbose_name = verbose_name
|
|
237
235
|
|
|
238
236
|
access_perm_codename = "can_access_" + model_name.lower()
|
|
239
|
-
access_perm_name = _("Can access {verbose_name}").format(
|
|
237
|
+
access_perm_name = str(_("Can access {verbose_name}")).format(
|
|
240
238
|
verbose_name=verbose_name
|
|
241
239
|
)
|
|
242
240
|
# The first permission here is expected to be
|
|
@@ -387,7 +385,7 @@ class AdminView(View):
|
|
|
387
385
|
|
|
388
386
|
def has_permission(self, perm: str) -> bool:
|
|
389
387
|
perm = self.get_permission(perm)
|
|
390
|
-
return self.request.user.has_perm(perm)
|
|
388
|
+
return bool(self.request.user.has_perm(perm))
|
|
391
389
|
|
|
392
390
|
def has_access(self) -> bool:
|
|
393
391
|
assert self.admin is not None
|
bananas/drf/fencing.py
CHANGED
|
@@ -106,15 +106,13 @@ _MT_co = TypeVar("_MT_co", bound=Model, covariant=True)
|
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
class UsesQuerySet(Protocol[_MT_co]):
|
|
109
|
-
def get_queryset(self) -> "QuerySet[_MT_co]":
|
|
110
|
-
...
|
|
109
|
+
def get_queryset(self) -> "QuerySet[_MT_co]": ...
|
|
111
110
|
|
|
112
111
|
|
|
113
112
|
class FencedUpdateModelMixin(UpdateModelMixin, abc.ABC):
|
|
114
113
|
@property
|
|
115
114
|
@abc.abstractmethod
|
|
116
|
-
def fence(self) -> Fence:
|
|
117
|
-
...
|
|
115
|
+
def fence(self) -> Fence: ...
|
|
118
116
|
|
|
119
117
|
# django-restframework uses an "advanced self-type" on self in
|
|
120
118
|
# perform_update() which subtly breaks subclassing. We try to remedy this by
|
|
@@ -197,7 +195,7 @@ T = TypeVar("T")
|
|
|
197
195
|
|
|
198
196
|
|
|
199
197
|
def as_set(
|
|
200
|
-
fn: Callable[[InstanceType], Optional[T]]
|
|
198
|
+
fn: Callable[[InstanceType], Optional[T]],
|
|
201
199
|
) -> Callable[[InstanceType], Optional[FrozenSet[T]]]:
|
|
202
200
|
@wraps(fn)
|
|
203
201
|
def wrapper(instance: InstanceType) -> Optional[FrozenSet[T]]:
|
|
@@ -208,7 +206,7 @@ def as_set(
|
|
|
208
206
|
|
|
209
207
|
|
|
210
208
|
def allow_if_match(
|
|
211
|
-
version_getter: Callable[[InstanceType], Optional[str]]
|
|
209
|
+
version_getter: Callable[[InstanceType], Optional[str]],
|
|
212
210
|
) -> Fence[InstanceType, FrozenSet[str]]:
|
|
213
211
|
return Fence(
|
|
214
212
|
get_token=header_etag_parser("If-Match"),
|
bananas/environment.py
CHANGED
|
@@ -27,8 +27,7 @@ __all__ = ["env", "parse_bool", "parse_int", "parse_tuple", "parse_list", "parse
|
|
|
27
27
|
log = logging.getLogger(__name__)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class Undefined:
|
|
31
|
-
...
|
|
30
|
+
class Undefined: ...
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
UNDEFINED: Final = Undefined()
|
|
@@ -95,12 +94,10 @@ Q = TypeVar("Q", covariant=True)
|
|
|
95
94
|
|
|
96
95
|
|
|
97
96
|
class _Instantiable(Protocol[Q]):
|
|
98
|
-
def __init__(self, value: Iterable[Q]) -> None:
|
|
99
|
-
...
|
|
97
|
+
def __init__(self, value: Iterable[Q]) -> None: ...
|
|
100
98
|
|
|
101
99
|
|
|
102
|
-
class _InstantiableIterable(Iterable[Q], _Instantiable[Q], Generic[Q]):
|
|
103
|
-
...
|
|
100
|
+
class _InstantiableIterable(Iterable[Q], _Instantiable[Q], Generic[Q]): ...
|
|
104
101
|
|
|
105
102
|
|
|
106
103
|
T = TypeVar("T", bound=_InstantiableIterable)
|
|
@@ -128,23 +125,19 @@ P = TypeVar("P", bound=Union[Builtin, tuple, list, set])
|
|
|
128
125
|
|
|
129
126
|
|
|
130
127
|
@overload
|
|
131
|
-
def get_parser(typ: Type[B]) -> Callable[[str], B]:
|
|
132
|
-
...
|
|
128
|
+
def get_parser(typ: Type[B]) -> Callable[[str], B]: ...
|
|
133
129
|
|
|
134
130
|
|
|
135
131
|
@overload
|
|
136
|
-
def get_parser(typ: Type[tuple]) -> Callable[[str], Tuple[str, ...]]:
|
|
137
|
-
...
|
|
132
|
+
def get_parser(typ: Type[tuple]) -> Callable[[str], Tuple[str, ...]]: ...
|
|
138
133
|
|
|
139
134
|
|
|
140
135
|
@overload
|
|
141
|
-
def get_parser(typ: Type[list]) -> Callable[[str], List[str]]:
|
|
142
|
-
...
|
|
136
|
+
def get_parser(typ: Type[list]) -> Callable[[str], List[str]]: ...
|
|
143
137
|
|
|
144
138
|
|
|
145
139
|
@overload
|
|
146
|
-
def get_parser(typ: Type[set]) -> Callable[[str], Set[str]]:
|
|
147
|
-
...
|
|
140
|
+
def get_parser(typ: Type[set]) -> Callable[[str], Set[str]]: ...
|
|
148
141
|
|
|
149
142
|
|
|
150
143
|
def get_parser(typ: Type[P]) -> Callable[[str], P]:
|
|
@@ -238,12 +231,12 @@ class EnvironWrapper:
|
|
|
238
231
|
get: Callable[..., str]
|
|
239
232
|
|
|
240
233
|
@overload
|
|
241
|
-
def parse(
|
|
242
|
-
|
|
234
|
+
def parse(
|
|
235
|
+
self, parser: Callable[[str], S], key: str, default: None
|
|
236
|
+
) -> Optional[S]: ...
|
|
243
237
|
|
|
244
238
|
@overload
|
|
245
|
-
def parse(self, parser: Callable[[str], S], key: str, default: S) -> S:
|
|
246
|
-
...
|
|
239
|
+
def parse(self, parser: Callable[[str], S], key: str, default: S) -> S: ...
|
|
247
240
|
|
|
248
241
|
def parse(
|
|
249
242
|
self, parser: Callable[[str], S], key: str, default: Optional[S] = None
|
|
@@ -258,56 +251,48 @@ class EnvironWrapper:
|
|
|
258
251
|
return default
|
|
259
252
|
|
|
260
253
|
@overload
|
|
261
|
-
def get_bool(self, key: str, default: U) -> Union[bool, U]:
|
|
262
|
-
...
|
|
254
|
+
def get_bool(self, key: str, default: U) -> Union[bool, U]: ...
|
|
263
255
|
|
|
264
256
|
@overload
|
|
265
|
-
def get_bool(self, key: str, default: None = None) -> Optional[bool]:
|
|
266
|
-
...
|
|
257
|
+
def get_bool(self, key: str, default: None = None) -> Optional[bool]: ...
|
|
267
258
|
|
|
268
259
|
def get_bool(self, key: str, default: object = None) -> object:
|
|
269
260
|
return self.parse(parse_bool, key, default=default)
|
|
270
261
|
|
|
271
262
|
@overload
|
|
272
|
-
def get_int(self, key: str, default: U) -> Union[int, U]:
|
|
273
|
-
...
|
|
263
|
+
def get_int(self, key: str, default: U) -> Union[int, U]: ...
|
|
274
264
|
|
|
275
265
|
@overload
|
|
276
|
-
def get_int(self, key: str, default: None = None) -> Optional[int]:
|
|
277
|
-
...
|
|
266
|
+
def get_int(self, key: str, default: None = None) -> Optional[int]: ...
|
|
278
267
|
|
|
279
268
|
def get_int(self, key: str, default: object = None) -> object:
|
|
280
269
|
return self.parse(parse_int, key, default=default)
|
|
281
270
|
|
|
282
271
|
@overload
|
|
283
|
-
def get_tuple(self, key: str, default: U) -> Union[Tuple[str, ...], U]:
|
|
284
|
-
...
|
|
272
|
+
def get_tuple(self, key: str, default: U) -> Union[Tuple[str, ...], U]: ...
|
|
285
273
|
|
|
286
274
|
@overload
|
|
287
|
-
def get_tuple(
|
|
288
|
-
|
|
275
|
+
def get_tuple(
|
|
276
|
+
self, key: str, default: None = None
|
|
277
|
+
) -> Optional[Tuple[str, ...]]: ...
|
|
289
278
|
|
|
290
279
|
def get_tuple(self, key: str, default: object = None) -> object:
|
|
291
280
|
return self.parse(parse_tuple, key, default=default)
|
|
292
281
|
|
|
293
282
|
@overload
|
|
294
|
-
def get_list(self, key: str, default: U) -> Union[List[str], U]:
|
|
295
|
-
...
|
|
283
|
+
def get_list(self, key: str, default: U) -> Union[List[str], U]: ...
|
|
296
284
|
|
|
297
285
|
@overload
|
|
298
|
-
def get_list(self, key: str, default: None = None) -> Optional[List[str]]:
|
|
299
|
-
...
|
|
286
|
+
def get_list(self, key: str, default: None = None) -> Optional[List[str]]: ...
|
|
300
287
|
|
|
301
288
|
def get_list(self, key: str, default: object = None) -> object:
|
|
302
289
|
return self.parse(parse_list, key, default=default)
|
|
303
290
|
|
|
304
291
|
@overload
|
|
305
|
-
def get_set(self, key: str, default: U) -> Union[Set[str], U]:
|
|
306
|
-
...
|
|
292
|
+
def get_set(self, key: str, default: U) -> Union[Set[str], U]: ...
|
|
307
293
|
|
|
308
294
|
@overload
|
|
309
|
-
def get_set(self, key: str, default: None = None) -> Optional[Set[str]]:
|
|
310
|
-
...
|
|
295
|
+
def get_set(self, key: str, default: None = None) -> Optional[Set[str]]: ...
|
|
311
296
|
|
|
312
297
|
def get_set(self, key: str, default: object = None) -> object:
|
|
313
298
|
return self.parse(parse_set, key, default=default)
|
bananas/models.py
CHANGED
bananas/query.py
CHANGED
|
@@ -38,12 +38,16 @@ class ModelDictIterable:
|
|
|
38
38
|
query = queryset.query
|
|
39
39
|
compiler = query.get_compiler(queryset.db)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
if hasattr(query, "selected") and query.selected:
|
|
42
|
+
names = list(query.selected)
|
|
43
|
+
else:
|
|
44
|
+
extra_names: List[str] = list(query.extra_select)
|
|
45
|
+
field_names: List[str] = list(query.values_select)
|
|
46
|
+
annotation_names: List[str] = list(query.annotation_select)
|
|
47
|
+
|
|
48
|
+
# Modified super(); rename fields given in queryset.values() kwargs
|
|
49
|
+
names = extra_names + field_names + annotation_names
|
|
44
50
|
|
|
45
|
-
# Modified super(); rename fields given in queryset.values() kwargs
|
|
46
|
-
names = extra_names + field_names + annotation_names
|
|
47
51
|
if self.named_fields:
|
|
48
52
|
names = self.rename_fields(names)
|
|
49
53
|
|
|
@@ -62,8 +66,7 @@ _MT_co = TypeVar("_MT_co", bound=Model, covariant=True)
|
|
|
62
66
|
class IsQuerySet(Protocol[_MT_co]):
|
|
63
67
|
def values(
|
|
64
68
|
self, *fields: Union[str, Combinable], **expressions: Any
|
|
65
|
-
) -> "_QuerySet[_MT_co, ModelDict]":
|
|
66
|
-
...
|
|
69
|
+
) -> "_QuerySet[_MT_co, ModelDict]": ...
|
|
67
70
|
|
|
68
71
|
|
|
69
72
|
class ModelDictQuerySetMixin:
|
|
@@ -74,7 +77,7 @@ class ModelDictQuerySetMixin:
|
|
|
74
77
|
fields += tuple(named_fields.values())
|
|
75
78
|
|
|
76
79
|
clone = self.values(*fields)
|
|
77
|
-
clone._iterable_class = ModelDictIterable
|
|
80
|
+
clone._iterable_class = ModelDictIterable
|
|
78
81
|
|
|
79
82
|
# QuerySet._hints is a dict object used by db router
|
|
80
83
|
# to aid deciding which db should get a request. Currently
|
|
@@ -82,7 +85,7 @@ class ModelDictQuerySetMixin:
|
|
|
82
85
|
# fine to set a custom key on this dict as it's a guaranteed
|
|
83
86
|
# way that it'll be returned with the QuerySet instance
|
|
84
87
|
# while leaving the queryset intact
|
|
85
|
-
clone._add_hints(**{"_named_fields": named_fields})
|
|
88
|
+
clone._add_hints(**{"_named_fields": named_fields})
|
|
86
89
|
|
|
87
90
|
return clone
|
|
88
91
|
|
|
@@ -92,7 +95,7 @@ _MT = TypeVar("_MT", bound=Model)
|
|
|
92
95
|
|
|
93
96
|
if TYPE_CHECKING:
|
|
94
97
|
|
|
95
|
-
class ModelDictQuerySet(
|
|
98
|
+
class ModelDictQuerySet(
|
|
96
99
|
ModelDictQuerySetMixin,
|
|
97
100
|
QuerySet[_MT],
|
|
98
101
|
IsQuerySet[_MT],
|
bananas/secrets.py
CHANGED
|
@@ -9,13 +9,11 @@ BANANAS_SECRETS_DIR_ENV_KEY: Final = "BANANAS_SECRETS_DIR"
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@overload
|
|
12
|
-
def get_secret(secret_name: str, default: str) -> str:
|
|
13
|
-
...
|
|
12
|
+
def get_secret(secret_name: str, default: str) -> str: ...
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
@overload
|
|
17
|
-
def get_secret(secret_name: str) -> Optional[str]:
|
|
18
|
-
...
|
|
16
|
+
def get_secret(secret_name: str) -> Optional[str]: ...
|
|
19
17
|
|
|
20
18
|
|
|
21
19
|
def get_secret(secret_name: str, default: Optional[str] = None) -> Optional[str]:
|
|
@@ -75,6 +75,7 @@ body:not(.login) #header {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
#branding {
|
|
78
|
+
width: 100%;
|
|
78
79
|
float: none;
|
|
79
80
|
}
|
|
80
81
|
|
|
@@ -113,6 +114,7 @@ body.login #branding a:active {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
#branding a.logo {
|
|
117
|
+
width: 100%;
|
|
116
118
|
padding-top: 15px;
|
|
117
119
|
line-height: calc(70px - 15px * 2) /* fallback */;
|
|
118
120
|
line-height: var(--topbar-height-inner);
|
|
@@ -144,8 +146,8 @@ body.login #branding a:active {
|
|
|
144
146
|
height: 100%;
|
|
145
147
|
}
|
|
146
148
|
#header .searchable nav {
|
|
147
|
-
height: calc(100% - 45px) /* fallback */;
|
|
148
|
-
height: calc(100% - var(--searchbar-height));
|
|
149
|
+
height: calc(100% - 45px - 15px) /* fallback */;
|
|
150
|
+
height: calc(100% - var(--searchbar-height) - 15px);
|
|
149
151
|
}
|
|
150
152
|
#header nav > ul {
|
|
151
153
|
overflow-y: auto;
|
|
@@ -171,7 +173,7 @@ body.login #branding a:active {
|
|
|
171
173
|
margin-top: 15px;
|
|
172
174
|
}
|
|
173
175
|
#header .filtered-results nav > ul:first-of-type > li:first-of-type {
|
|
174
|
-
margin-top:
|
|
176
|
+
margin-top: 15px;
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
#header nav > ul > li > a,
|
|
@@ -381,6 +383,11 @@ body.popup #title .back-arrow {
|
|
|
381
383
|
background-color: transparent;
|
|
382
384
|
}
|
|
383
385
|
|
|
386
|
+
#content h2:first-of-type {
|
|
387
|
+
font-size: 22px;
|
|
388
|
+
margin-top: 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
384
391
|
#content .object-tools {
|
|
385
392
|
position: fixed;
|
|
386
393
|
z-index: 3000;
|
|
@@ -527,6 +534,7 @@ input[type="button"]:disabled,
|
|
|
527
534
|
.timelist a:hover,
|
|
528
535
|
.timelist a:focus,
|
|
529
536
|
.calendar td.selected a {
|
|
537
|
+
color: white;
|
|
530
538
|
background-color: #417690 /* fallback */;
|
|
531
539
|
background-color: var(--theme-color);
|
|
532
540
|
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.25)) /* fallback */;
|
|
@@ -572,6 +580,8 @@ textarea:disabled {
|
|
|
572
580
|
background: none;
|
|
573
581
|
color: #333;
|
|
574
582
|
font-size: 22px;
|
|
583
|
+
border: 0;
|
|
584
|
+
text-transform: unset;
|
|
575
585
|
}
|
|
576
586
|
|
|
577
587
|
.module > h2 a.section:link,
|
|
@@ -604,26 +614,6 @@ a.active.selector-clearall:hover {
|
|
|
604
614
|
/* TODO: selector-icons.svg is hardcoded blue on hover */
|
|
605
615
|
}
|
|
606
616
|
|
|
607
|
-
@supports (background-blend-mode: overlay) {
|
|
608
|
-
.datetimeshortcuts .date-icon,
|
|
609
|
-
.datetimeshortcuts .clock-icon {
|
|
610
|
-
background: none;
|
|
611
|
-
background-color: #447e9b /* fallback */;
|
|
612
|
-
background-color: var(--secondary-color);
|
|
613
|
-
background-blend-mode: overlay;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
.datetimeshortcuts .date-icon {
|
|
617
|
-
-webkit-mask-image: url(../../img/icon-calendar.svg);
|
|
618
|
-
mask-image: url(../../img/icon-calendar.svg);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
.datetimeshortcuts .clock-icon {
|
|
622
|
-
-webkit-mask-image: url(../../img/icon-clock.svg);
|
|
623
|
-
mask-image: url(../../img/icon-clock.svg);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
617
|
.calendar caption,
|
|
628
618
|
.calendarbox h2 {
|
|
629
619
|
color: white;
|
|
@@ -634,12 +624,15 @@ a.active.selector-clearall:hover {
|
|
|
634
624
|
filter: invert(1);
|
|
635
625
|
}
|
|
636
626
|
|
|
637
|
-
.
|
|
638
|
-
|
|
627
|
+
.calendar-cancel a {
|
|
628
|
+
color: var(--button-fg);
|
|
639
629
|
}
|
|
640
630
|
|
|
641
|
-
.
|
|
642
|
-
|
|
631
|
+
.clockbox h2 {
|
|
632
|
+
font-size: 1.1em;
|
|
633
|
+
color: white;
|
|
634
|
+
background: var(--theme-color);
|
|
635
|
+
background-image: var(--bg-lighten-25);
|
|
643
636
|
}
|
|
644
637
|
|
|
645
638
|
#header #changelist-filter,
|
|
@@ -685,6 +678,21 @@ a.active.selector-clearall:hover {
|
|
|
685
678
|
margin: 0;
|
|
686
679
|
}
|
|
687
680
|
|
|
681
|
+
#header #changelist-filter h3 .viewlink {
|
|
682
|
+
background: none;
|
|
683
|
+
}
|
|
684
|
+
#header #changelist-filter h3 .viewlink:hover {
|
|
685
|
+
color: var(--lighten-75);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
#changelist-filter #changelist-filter-extra-actions {
|
|
689
|
+
border-bottom: 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
#changelist-filter details[open] > summary::before {
|
|
693
|
+
color: var(--lighten-25);
|
|
694
|
+
}
|
|
695
|
+
|
|
688
696
|
#header #object-tools h2 {
|
|
689
697
|
margin: 0;
|
|
690
698
|
}
|
|
@@ -735,6 +743,11 @@ body:not(.popup) #changelist #toolbar {
|
|
|
735
743
|
margin-right: 0;
|
|
736
744
|
}
|
|
737
745
|
|
|
746
|
+
.nav-global #toolbar {
|
|
747
|
+
border: 0;
|
|
748
|
+
margin-bottom: 25px;
|
|
749
|
+
}
|
|
750
|
+
|
|
738
751
|
.nav-global #toolbar form {
|
|
739
752
|
display: block;
|
|
740
753
|
}
|
|
@@ -764,7 +777,9 @@ body:not(.popup) #changelist #toolbar {
|
|
|
764
777
|
background-image: var(--bg-lighten-33);
|
|
765
778
|
color: #fff;
|
|
766
779
|
line-height: 25px;
|
|
767
|
-
height:
|
|
780
|
+
height: 45px;
|
|
781
|
+
padding: 10px 0;
|
|
782
|
+
box-sizing: border-box;
|
|
768
783
|
}
|
|
769
784
|
|
|
770
785
|
#changelist .results ~ .actions {
|
|
@@ -778,6 +793,7 @@ body:not(.popup) #changelist #toolbar {
|
|
|
778
793
|
}
|
|
779
794
|
|
|
780
795
|
#changelist .actions label {
|
|
796
|
+
padding-left: 10px;
|
|
781
797
|
vertical-align: top;
|
|
782
798
|
}
|
|
783
799
|
|
|
@@ -806,3 +822,7 @@ body:not(.popup) #changelist #toolbar {
|
|
|
806
822
|
#changelist .actions span.question {
|
|
807
823
|
margin: 0 0 0 10px;
|
|
808
824
|
}
|
|
825
|
+
|
|
826
|
+
.selector-chosen-title {
|
|
827
|
+
background: var(--theme-color);
|
|
828
|
+
}
|
|
@@ -48,9 +48,8 @@ body:not(.popup) #header #object-tools {
|
|
|
48
48
|
padding: 0;
|
|
49
49
|
border-radius: 0;
|
|
50
50
|
border: 0;
|
|
51
|
-
background:
|
|
52
|
-
background:
|
|
53
|
-
background-image: url(../../img/search.svg), linear-gradient(to bottom, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.15));
|
|
51
|
+
background: white;
|
|
52
|
+
background-image: url(../../img/search.svg);
|
|
54
53
|
background-position: center center;
|
|
55
54
|
background-repeat: no-repeat;
|
|
56
55
|
box-shadow: none;
|
|
@@ -58,7 +57,7 @@ body:not(.popup) #header #object-tools {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
#header #changelist-search input[type="submit"]:hover {
|
|
61
|
-
background-image: url(../../img/search.svg), linear-gradient(to bottom, rgba(0, 0, 0, 0.
|
|
60
|
+
background-image: url(../../img/search.svg), linear-gradient(to bottom, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.15));
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
#changelist-search > div {
|
|
@@ -123,6 +122,12 @@ body:not(.popup) #header #object-tools {
|
|
|
123
122
|
transform: translateX(var(--sidebar-width));
|
|
124
123
|
}
|
|
125
124
|
|
|
125
|
+
html.is-sidebarOpen #header nav {
|
|
126
|
+
position: unset;
|
|
127
|
+
width: var(--sidebar-width);
|
|
128
|
+
transform: translateY(-15px);
|
|
129
|
+
}
|
|
130
|
+
|
|
126
131
|
body:not(.popup) #content .object-tools {
|
|
127
132
|
display: none;
|
|
128
133
|
}
|
|
@@ -140,6 +145,14 @@ body:not(.popup) #header #object-tools {
|
|
|
140
145
|
margin-right: 0;
|
|
141
146
|
}
|
|
142
147
|
|
|
148
|
+
#changelist > .changelist-form-container {
|
|
149
|
+
max-width: 100vw;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
html.is-sidebarOpen #changelist > .changelist-form-container {
|
|
153
|
+
max-width: calc(100vw - var(--sidebar-width));
|
|
154
|
+
}
|
|
155
|
+
|
|
143
156
|
#changelist .actions {
|
|
144
157
|
padding: 10px;
|
|
145
158
|
left: 0;
|
|
@@ -120,7 +120,10 @@
|
|
|
120
120
|
{% endif %}
|
|
121
121
|
|
|
122
122
|
<li>
|
|
123
|
-
<
|
|
123
|
+
<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
|
|
124
|
+
{% csrf_token %}
|
|
125
|
+
</form>
|
|
126
|
+
<a href="" onclick="document.getElementById('logout-form').submit();">
|
|
124
127
|
{% trans 'Log out' %}
|
|
125
128
|
</a>
|
|
126
129
|
</li>
|
bananas/url.py
CHANGED
|
@@ -27,6 +27,7 @@ Currently supported engines are:
|
|
|
27
27
|
You can add your own by running ``register(scheme, module_name)`` before
|
|
28
28
|
parsing.
|
|
29
29
|
"""
|
|
30
|
+
|
|
30
31
|
from typing import Any, Dict, Final, List, Mapping, NamedTuple, Optional, Tuple, Union
|
|
31
32
|
from urllib.parse import parse_qs, unquote_plus, urlsplit
|
|
32
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-bananas
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4
|
|
4
4
|
Summary: Django Bananas - Django extensions the monkey way
|
|
5
5
|
Home-page: https://github.com/5monkeys/django-bananas
|
|
6
6
|
License: MIT License
|
|
@@ -20,6 +20,7 @@ Classifier: Framework :: Django :: 4.1
|
|
|
20
20
|
Classifier: Framework :: Django :: 4.2
|
|
21
21
|
Classifier: Framework :: Django :: 5.0
|
|
22
22
|
Classifier: Framework :: Django :: 5.1
|
|
23
|
+
Classifier: Framework :: Django :: 5.2
|
|
23
24
|
Classifier: Intended Audience :: Developers
|
|
24
25
|
Classifier: License :: OSI Approved :: MIT License
|
|
25
26
|
Classifier: Operating System :: OS Independent
|
|
@@ -44,6 +45,7 @@ Requires-Dist: pytest; extra == "dev"
|
|
|
44
45
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
45
46
|
Requires-Dist: pytest-django; extra == "dev"
|
|
46
47
|
Requires-Dist: pre-commit; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
47
49
|
|
|
48
50
|
================================================================================
|
|
49
51
|
:banana: Django Bananas - Django extensions the monkey way
|
|
@@ -85,6 +87,7 @@ Currently tested only for
|
|
|
85
87
|
- Django 4.2 under Python 3.8-3.13
|
|
86
88
|
- Django 5.0 under Python 3.10-3.13
|
|
87
89
|
- Django 5.1 under Python 3.10-3.13
|
|
90
|
+
- Django 5.2 under Python 3.10-3.13
|
|
88
91
|
|
|
89
92
|
Pull requests welcome!
|
|
90
93
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
bananas/__init__.py,sha256=
|
|
2
|
-
bananas/environment.py,sha256=
|
|
1
|
+
bananas/__init__.py,sha256=HBTRN6OCuRJ35rOq9Scw0Q_tH_VrxXXcWvRjUDjLtpc,686
|
|
2
|
+
bananas/environment.py,sha256=1UwUNjSbnLlWh0XN41_AdEc0TVXQbbswkM34KNPzNwk,7744
|
|
3
3
|
bananas/lazy.py,sha256=XFaqACJ6K5s_MZcxK_5OLQU35F7c7jB3KsdAtHuYQ6k,140
|
|
4
|
-
bananas/models.py,sha256=
|
|
4
|
+
bananas/models.py,sha256=upapLi3Zvk073G4oMXCobh7RRXDhBLQ0kjPTWWAeYCM,8797
|
|
5
5
|
bananas/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
bananas/query.py,sha256=
|
|
7
|
-
bananas/secrets.py,sha256=
|
|
6
|
+
bananas/query.py,sha256=vDvbY3_KSri_bUZflmUDb2zo46kuZJpuWVBL6s2lX4Y,3615
|
|
7
|
+
bananas/secrets.py,sha256=WDBZapuPzDGHUwO1J_UjFuoljFkgbLKYy0uFTIl53Fk,1021
|
|
8
8
|
bananas/settings.py,sha256=HGZBxm5BMwHJc9PpQpvkzAuasXvePa-q_4tgeup9DPA,139
|
|
9
|
-
bananas/url.py,sha256=
|
|
9
|
+
bananas/url.py,sha256=63vR_bK0Py9X-8dA7seEPVrQaU8FRSIOLwUb8XIUDAk,7907
|
|
10
10
|
bananas/admin/__init__.py,sha256=0Dzu51GjYjbpnNf5ByaKTx5xiYZ0vxfhW1EssFuiQ90,173
|
|
11
|
-
bananas/admin/extension.py,sha256=
|
|
11
|
+
bananas/admin/extension.py,sha256=pfAjvKrGpnHZoX5YQSXM9-6GojFvLtUkwgN9OhHIekI,13632
|
|
12
12
|
bananas/admin/i18n.py,sha256=WxK_LKnPjoFgoNiNuZdr5wk3LoMXdu7acaIsZNsOxc0,301
|
|
13
13
|
bananas/admin/api/__init__.py,sha256=86dXxHQKnQk-2ZOT2znTBasX-k7nPKlxslwYVZlzu4g,54
|
|
14
14
|
bananas/admin/api/mixins.py,sha256=NroiouccAD6MORoFdS-I3Xnjsmm69_JhdQRT5Roe240,5287
|
|
@@ -17,7 +17,7 @@ bananas/admin/api/router.py,sha256=VWn6O97sbsoPZtKPLPxPDn_raO7Gy8B62N1s7-mGHhU,5
|
|
|
17
17
|
bananas/admin/api/serializers.py,sha256=XziZ1-6h4hn1qgts3qB8W18ctpl6h8vP3dkqa7WxLe8,2964
|
|
18
18
|
bananas/admin/api/urls.py,sha256=fch3C5BhI6d12U9un8I28fFiHZWTWFH5XAhAzXPURxY,398
|
|
19
19
|
bananas/admin/api/versioning.py,sha256=ciyelTzGZtJDPnigRZAJ0Gk1ZHfGxM9FXctS-OoyJ4Y,901
|
|
20
|
-
bananas/admin/api/views.py,sha256=
|
|
20
|
+
bananas/admin/api/views.py,sha256=at_iF0Q860srlsyqjuQq9D274PItTSrXp0uURgegqrE,4169
|
|
21
21
|
bananas/admin/api/schemas/__init__.py,sha256=n4vZ3Sd1H23krxIIUWvktHimmdfh1n0K-J4z2-QsZxI,336
|
|
22
22
|
bananas/admin/api/schemas/base.py,sha256=XFfSQE_VeUraB_LIge1xEtVYOaTGfx6aDq6eahd6D2o,449
|
|
23
23
|
bananas/admin/api/schemas/decorators.py,sha256=Zwp7AwV_6Y0iWVcsO8VjNHHttYMcdGnrfr7ElwEzJzE,492
|
|
@@ -26,22 +26,22 @@ bananas/admin/api/v1_0/__init__.py,sha256=1M909E2Ei8-GI0Xox9t5NfO3xpPG2F13znjlx_
|
|
|
26
26
|
bananas/admin/api/v1_0/urls.py,sha256=8N-fFqycB-SnzTMQQk-TTM1b7GuRGXlPtnRFyv6J2rk,695
|
|
27
27
|
bananas/drf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
bananas/drf/errors.py,sha256=JDZYblAio27rh-nAOtC_4CoVw_lFYIdmmd4bTrgxaQc,427
|
|
29
|
-
bananas/drf/fencing.py,sha256=
|
|
29
|
+
bananas/drf/fencing.py,sha256=XyCNiPqx0eKMk1emjp7kvyvj7P9JvDArIWgKeQrCRD4,7264
|
|
30
30
|
bananas/drf/utils.py,sha256=Cg7oBNNPuFwIHwHldSk3za87oesK6sivCbiRWP4zj0s,1824
|
|
31
31
|
bananas/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
bananas/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
bananas/management/commands/show_urls.py,sha256=X6ipP04XN1pdrT2Ne5ozbMx068SQ8Io-HpO6Xi7v7d0,2220
|
|
34
34
|
bananas/management/commands/syncpermissions.py,sha256=fmsJpikAPAwvK8Yfkj4A53fdUOz-y0rHEF7xsRiy8W0,1565
|
|
35
|
-
bananas/static/admin/bananas/css/bananas.css,sha256=
|
|
36
|
-
bananas/static/admin/bananas/css/banansive.css,sha256=
|
|
35
|
+
bananas/static/admin/bananas/css/bananas.css,sha256=h7IuijjLYePQ5vwMWNP_Piyxn7IDBMWBM5Of0n6knNE,17882
|
|
36
|
+
bananas/static/admin/bananas/css/banansive.css,sha256=P2EuItR7n0l_hn3X0mIMKhj4cQc4hnjg4PvQjyknLBg,3984
|
|
37
37
|
bananas/static/admin/bananas/img/django.svg,sha256=4S1Cjntojkcoj17oTNufVtnlflhoKsRuQ9O7hTqyx6Q,5291
|
|
38
38
|
bananas/static/admin/bananas/img/search.svg,sha256=59UwyGYVJ4EJ26d5AAIdjtCHR3CnO94in6eQIbkYTu8,458
|
|
39
39
|
bananas/static/admin/bananas/js/bananas.js,sha256=1Xv6JEiTZYeaJSTxkKyMLwIRJ40SnRDPi2N-prpWoU0,4596
|
|
40
40
|
bananas/static/admin/css/responsive.css,sha256=UAMABM7h5rRZ8FocJw4bfx36Q5XtIDm4ddXN8lMC4SQ,17894
|
|
41
|
-
bananas/templates/admin/base_site.html,sha256=
|
|
41
|
+
bananas/templates/admin/base_site.html,sha256=RU67gxvx1ozx3-uCyXWf2jC9H6c4adm0BjwmKF8EHEo,7977
|
|
42
42
|
bananas/templates/admin/view.html,sha256=WezwD9qGPch5S29intLVzbY1zVRop4zdIdm4pf7usVw,114
|
|
43
|
-
django_bananas-2.
|
|
44
|
-
django_bananas-2.
|
|
45
|
-
django_bananas-2.
|
|
46
|
-
django_bananas-2.
|
|
47
|
-
django_bananas-2.
|
|
43
|
+
django_bananas-2.4.dist-info/licenses/LICENSE,sha256=ezKzVU8KUSkiuP6I5I1PEfOUn33aerXQ_fU8_4Qavos,1081
|
|
44
|
+
django_bananas-2.4.dist-info/METADATA,sha256=13IP-MgvJRUUqgKRwtv6Lag1f5vhsq4bnzhfCPj2UCM,18680
|
|
45
|
+
django_bananas-2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
46
|
+
django_bananas-2.4.dist-info/top_level.txt,sha256=5JKVcC99qGcHeOm5WigbcrQbMcsWr6N2A0tF9ZoY6j8,8
|
|
47
|
+
django_bananas-2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|