plain.admin 0.31.3__py3-none-any.whl → 0.31.5__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/README.md CHANGED
@@ -150,6 +150,37 @@ TODO
150
150
 
151
151
  TODO
152
152
 
153
+ ## List `displays`
154
+
155
+ On admin list views, you can define different `displays` to build predefined views of your data. The display choices will be shown in the UI, and you can use the current `self.display` in your view.
156
+
157
+ ```python
158
+ # app/users/admin.py
159
+ from plain.admin.views import AdminModelListView, register_viewset
160
+
161
+ from .models import User
162
+
163
+
164
+ @register_viewset
165
+ class UserAdmin(AdminViewset):
166
+ class ListView(AdminModelListView):
167
+ model = User
168
+ fields = [
169
+ "id",
170
+ "email",
171
+ "created_at__date",
172
+ ]
173
+ displays = ["Users without email"]
174
+
175
+ def get_objects(self):
176
+ objects = super().get_objects()
177
+
178
+ if self.display == "Users without email":
179
+ objects = objects.filter(email="")
180
+
181
+ return objects
182
+ ```
183
+
153
184
  ## Toolbar
154
185
 
155
186
  TODO
@@ -41,7 +41,7 @@
41
41
  <form method="GET" class="inline-flex space-x-5">
42
42
  {% if displays %}
43
43
  <select data-autosubmit name="display" class="text-sm">
44
- <option value="">Displays</option>
44
+ <option value="">Choose a display</option>
45
45
  {% for display in displays %}
46
46
  <option {% if display == current_display %}selected{% endif %}>{{ display }}</option>
47
47
  {% endfor %}
@@ -89,9 +89,9 @@
89
89
  {% else %}
90
90
  <a
91
91
  data-merge-params
92
- class="font-mono text-xs font-normal"
92
+ class="font-mono text-xs font-normal flex space-x-1 items-center"
93
93
  href="?page=1&order_by={{ '-' if not order_by_direction else '' }}{{ field }}">
94
- {{ field }}
94
+ <span>{{ field }}</span>
95
95
  {% if field == order_by_field %}
96
96
  {% if order_by_direction == "-" %}
97
97
  <span class="text-xs">▲</span>
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  from plain import models
4
+ from plain.exceptions import FieldError
4
5
  from plain.models import Manager, Q
5
6
 
6
7
  from .objects import (
@@ -92,8 +93,8 @@ class AdminModelListView(AdminListView):
92
93
 
93
94
  def get_objects(self):
94
95
  queryset = self.get_initial_queryset()
95
- queryset = self.order_queryset(queryset)
96
96
  queryset = self.search_queryset(queryset)
97
+ queryset = self.order_queryset(queryset)
97
98
  return queryset
98
99
 
99
100
  def get_initial_queryset(self):
@@ -103,7 +104,23 @@ class AdminModelListView(AdminListView):
103
104
 
104
105
  def order_queryset(self, queryset):
105
106
  if order_by := self.request.query_params.get("order_by"):
106
- queryset = queryset.order_by(order_by)
107
+ try:
108
+ queryset = queryset.order_by(order_by)
109
+ except FieldError:
110
+ # Fallback to sorting in Python if the field is not valid
111
+ # Note this can be an expensive operation!
112
+ if order_by.startswith("-"):
113
+ queryset = sorted(
114
+ queryset,
115
+ key=lambda obj: self.get_field_value(obj, order_by[1:]),
116
+ reverse=False,
117
+ )
118
+ else:
119
+ queryset = sorted(
120
+ queryset,
121
+ key=lambda obj: self.get_field_value(obj, order_by),
122
+ reverse=True,
123
+ )
107
124
  elif self.queryset_order:
108
125
  queryset = queryset.order_by(*self.queryset_order)
109
126
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.admin
3
- Version: 0.31.3
3
+ Version: 0.31.5
4
4
  Summary: Admin dashboard and tools for Plain.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-Expression: BSD-3-Clause
@@ -165,6 +165,37 @@ TODO
165
165
 
166
166
  TODO
167
167
 
168
+ ## List `displays`
169
+
170
+ On admin list views, you can define different `displays` to build predefined views of your data. The display choices will be shown in the UI, and you can use the current `self.display` in your view.
171
+
172
+ ```python
173
+ # app/users/admin.py
174
+ from plain.admin.views import AdminModelListView, register_viewset
175
+
176
+ from .models import User
177
+
178
+
179
+ @register_viewset
180
+ class UserAdmin(AdminViewset):
181
+ class ListView(AdminModelListView):
182
+ model = User
183
+ fields = [
184
+ "id",
185
+ "email",
186
+ "created_at__date",
187
+ ]
188
+ displays = ["Users without email"]
189
+
190
+ def get_objects(self):
191
+ objects = super().get_objects()
192
+
193
+ if self.display == "Users without email":
194
+ objects = objects.filter(email="")
195
+
196
+ return objects
197
+ ```
198
+
168
199
  ## Toolbar
169
200
 
170
201
  TODO
@@ -1,4 +1,4 @@
1
- plain/admin/README.md,sha256=Ro2YkrKS-RXsmFBFN0QUpLh4OHIDvMkVgDIE6Wu4PMQ,3800
1
+ plain/admin/README.md,sha256=w5N8yhHdMgY2RIf4WUvIXKvBrRTHLy4CV0ROtdK2Jiw,4614
2
2
  plain/admin/__init__.py,sha256=bPv9iftT8aLqBH6dDy-HTVXW66dQUhfIiEZ-LIUMC0Y,78
3
3
  plain/admin/config.py,sha256=TDYmJe4UYmKw4bz0x5s9PkDa-X4V-9JoJlka162-J7M,676
4
4
  plain/admin/dates.py,sha256=EEhcQhHt3-k6kE9yvPdH5X6EecmUQ259xywbDBec3Dg,10253
@@ -37,7 +37,7 @@ plain/admin/templates/admin/base.html,sha256=urUvdcgcZZWjevZSvkagXdcrUe5LYUeaWme
37
37
  plain/admin/templates/admin/delete.html,sha256=lNuU2G-BR6TH6NUmh7VcvjnEuFeI84rwwk_oO1jkUq0,431
38
38
  plain/admin/templates/admin/detail.html,sha256=AizpXs6HguFzwbk7JDbH8poJB5dM2CaVVaQ4FThAHaw,730
39
39
  plain/admin/templates/admin/index.html,sha256=b65tcZhv9QfvmjePySU7MmzUlpMECIXP8dBH-a3Eyxw,69
40
- plain/admin/templates/admin/list.html,sha256=tD3CVXl8ghBVavF5_8WG_ekj96FeKt_iq97j35zPkZI,8609
40
+ plain/admin/templates/admin/list.html,sha256=lFqZoZYQXbEfadYI9sgI0KoTkAxD9DR9eNo30madV0o,8658
41
41
  plain/admin/templates/admin/page.html,sha256=wzRR-JLs8CgCOoB3BMoYWqTMpYM0z4X2qlqdwAe0YjM,67
42
42
  plain/admin/templates/admin/search.html,sha256=zfwnXoztAFnj8OmwxJcWaqo-SKCy50bLwfwSrAnAtoQ,1799
43
43
  plain/admin/templates/admin/cards/base.html,sha256=jNw61yM0R40roC8UqGWXFCegObSIV0rbvQ0021gzUi0,913
@@ -74,12 +74,12 @@ plain/admin/templates/toolbar/request.html,sha256=VyxNpEISVYZtGkR4J0XiXkv8d3Ltic
74
74
  plain/admin/templates/toolbar/toolbar.html,sha256=Gow_2-ITxtbkGLgJRRjL169E9TaTKMfsZ26A9g36gkw,6833
75
75
  plain/admin/views/__init__.py,sha256=nF6AENZ3Xxyi08OTRrF6e-HYBkZSFj7XBK2mVzMYqN4,846
76
76
  plain/admin/views/base.py,sha256=S1oaMUXnMOwRozbn2K-tk9tL4BMimemfMagZD9QxrJw,3512
77
- plain/admin/views/models.py,sha256=DAv7YzeSyQHLLAVdUhSPCkmx2B10g5ksAjHm2jrgQfw,5973
77
+ plain/admin/views/models.py,sha256=Ckck8_OAnQ7vvqAa5L65-KmeetAUMJAlLLnKiEK72PM,6690
78
78
  plain/admin/views/objects.py,sha256=eKL8A2B1ZMgTrCbTXnh6vCeju_HObxwetn_xc1vYlfY,11176
79
79
  plain/admin/views/registry.py,sha256=Lxib4YSQCMHb_zACnLKymJakV8jCZPWYll7J8-aV9Xw,3712
80
80
  plain/admin/views/types.py,sha256=ONMMdUoapgMoUVYgSIe-4YCdfvaVMQ4jgPWYiMo0pDk,178
81
81
  plain/admin/views/viewsets.py,sha256=dqMlQ6kLn9iqd9BwBWAZT1S271wH1FdfM5HXbOgBMEw,1655
82
- plain_admin-0.31.3.dist-info/METADATA,sha256=brYep_VwA1Y3LG2kMMzWG9i7XCuSyf5hgmF-pCa6ZQo,4237
83
- plain_admin-0.31.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
- plain_admin-0.31.3.dist-info/licenses/LICENSE,sha256=cvKM3OlqHx3ijD6e34zsSUkPvzl-ya3Dd63A6EHL94U,1500
85
- plain_admin-0.31.3.dist-info/RECORD,,
82
+ plain_admin-0.31.5.dist-info/METADATA,sha256=lSvGvyodAkxgrKZ4IWNV7WRC3y7poyzHVzqTLIWSmjg,5051
83
+ plain_admin-0.31.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
+ plain_admin-0.31.5.dist-info/licenses/LICENSE,sha256=cvKM3OlqHx3ijD6e34zsSUkPvzl-ya3Dd63A6EHL94U,1500
85
+ plain_admin-0.31.5.dist-info/RECORD,,