django-content-studio 1.0.0a1__py3-none-any.whl → 1.0.0b1__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.
content_studio/views.py CHANGED
@@ -1,5 +1,7 @@
1
+ from django.conf import settings
1
2
  from django.contrib import admin
2
3
  from django.urls import reverse, NoReverseMatch
4
+ from django.utils.translation import gettext_lazy as _
3
5
  from django.views.generic import TemplateView
4
6
  from rest_framework.decorators import action
5
7
  from rest_framework.permissions import IsAdminUser, AllowAny
@@ -7,9 +9,11 @@ from rest_framework.renderers import JSONRenderer
7
9
  from rest_framework.response import Response
8
10
  from rest_framework.viewsets import ViewSet
9
11
 
10
- from .admin import AdminSerializer, admin_site
12
+ from . import __version__
13
+ from .admin import AdminSerializer, ModelGroup
11
14
  from .models import ModelSerializer
12
- from .serializers import CurrentUserSerializer
15
+ from .serializers import SessionUserSerializer
16
+ from .settings import cs_settings
13
17
 
14
18
 
15
19
  class ContentStudioWebAppView(TemplateView):
@@ -22,7 +26,7 @@ class ContentStudioWebAppView(TemplateView):
22
26
 
23
27
  class AdminApiViewSet(ViewSet):
24
28
  """
25
- Viewset for special admin endpoints.
29
+ View set for content studio admin endpoints.
26
30
  """
27
31
 
28
32
  permission_classes = [IsAdminUser]
@@ -38,8 +42,10 @@ class AdminApiViewSet(ViewSet):
38
42
  """
39
43
  Returns public information about the Content Studio admin.
40
44
  """
45
+ admin_site = cs_settings.ADMIN_SITE
41
46
 
42
47
  data = {
48
+ "version": __version__,
43
49
  "site_header": admin_site.site_header,
44
50
  "site_title": admin_site.site_title,
45
51
  "index_title": admin_site.index_title,
@@ -50,6 +56,17 @@ class AdminApiViewSet(ViewSet):
50
56
  for backend in admin_site.login_backend.active_backends
51
57
  ],
52
58
  "token_backend": admin_site.token_backend.active_backend.get_info(),
59
+ "formats": {
60
+ model_class.__name__: frmt.serialize()
61
+ for model_class, frmt in admin_site.default_format_mapping.items()
62
+ },
63
+ "widgets": get_widgets(),
64
+ "settings": {
65
+ "created_by_attr": cs_settings.CREATED_BY_ATTR,
66
+ "created_at_attr": cs_settings.CREATED_AT_ATTR,
67
+ "edited_by_attr": cs_settings.EDITED_BY_ATTR,
68
+ "edited_at_attr": cs_settings.EDITED_AT_ATTR,
69
+ },
53
70
  }
54
71
 
55
72
  return Response(data=data)
@@ -66,16 +83,29 @@ class AdminApiViewSet(ViewSet):
66
83
  """
67
84
  Returns information about the Django app (models, admin models, admin site, settings, etc.).
68
85
  """
69
- data = {"models": []}
70
- registered_models = admin.site._registry
86
+ admin_site = cs_settings.ADMIN_SITE
87
+ data = {
88
+ "models": get_models(request),
89
+ "model_groups": get_model_groups(),
90
+ "user_model": settings.AUTH_USER_MODEL,
91
+ }
92
+
93
+ media_model = cs_settings.MEDIA_LIBRARY_MODEL
94
+ folder_model = cs_settings.MEDIA_LIBRARY_FOLDER_MODEL
95
+
96
+ data["media_library"] = {
97
+ "enabled": media_model is not None,
98
+ "folders": folder_model is not None,
99
+ "models": {
100
+ "media_model": media_model._meta.label_lower,
101
+ "folder_model": folder_model._meta.label_lower,
102
+ },
103
+ }
71
104
 
72
- for model, admin_class in registered_models.items():
73
- data["models"].append(
74
- {
75
- **ModelSerializer(model).serialize(),
76
- "admin": AdminSerializer(admin_class).serialize(request),
77
- }
78
- )
105
+ if admin_site.dashboard:
106
+ data["dashboard"] = admin_site.dashboard.serialize()
107
+ else:
108
+ data["dashboard"] = {"widgets": []}
79
109
 
80
110
  return Response(data=data)
81
111
 
@@ -84,7 +114,64 @@ class AdminApiViewSet(ViewSet):
84
114
  """
85
115
  Returns information about the current user.
86
116
  """
87
- return Response(CurrentUserSerializer(request.user).data)
117
+ return Response(SessionUserSerializer(request.user).data)
118
+
119
+
120
+ def get_models(request):
121
+ models = []
122
+ registered_models = admin.site._registry
123
+
124
+ for model, admin_class in registered_models.items():
125
+ models.append(
126
+ {
127
+ **ModelSerializer(model).serialize(),
128
+ "admin": AdminSerializer(admin_class).serialize(request),
129
+ }
130
+ )
131
+ for inline in admin_class.inlines:
132
+ if inline.model not in registered_models:
133
+ models.append(
134
+ {
135
+ **ModelSerializer(inline.model).serialize(),
136
+ }
137
+ )
138
+
139
+ return models
140
+
141
+
142
+ def get_model_groups():
143
+ admin_site = cs_settings.ADMIN_SITE
144
+
145
+ default_group = [
146
+ ModelGroup(
147
+ label=_("Content"),
148
+ name="default",
149
+ icon=None,
150
+ models=[model for model, admin_model in admin.site._registry.items()],
151
+ )
152
+ ]
153
+ # Get custom model groups or use the default one
154
+ model_groups = getattr(admin_site, "model_groups", None) or default_group
155
+
156
+ return [
157
+ {
158
+ "name": group.name,
159
+ "icon": group.icon,
160
+ "color": group.color,
161
+ "label": group.label,
162
+ "models": [m._meta.label_lower for m in group.models],
163
+ }
164
+ for group in model_groups
165
+ ]
166
+
167
+
168
+ def get_widgets():
169
+ admin_site = cs_settings.ADMIN_SITE
170
+
171
+ return {
172
+ (m if isinstance(m, str) else m.__name__): widget.serialize()
173
+ for m, widget in admin_site.default_widget_mapping.items()
174
+ }
88
175
 
89
176
 
90
177
  def get_health_check_path():
@@ -1,58 +1,32 @@
1
1
  from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
2
2
  from django.contrib.contenttypes.models import ContentType
3
- from rest_framework.exceptions import MethodNotAllowed, NotFound
3
+ from rest_framework.exceptions import NotFound
4
4
  from rest_framework.filters import SearchFilter, OrderingFilter
5
- from rest_framework.pagination import PageNumberPagination
6
5
  from rest_framework.parsers import JSONParser
7
6
  from rest_framework.permissions import DjangoModelPermissions
8
7
  from rest_framework.renderers import JSONRenderer
9
8
  from rest_framework.viewsets import ModelViewSet
10
9
 
11
- from .admin import admin_site
10
+ from .filters import LookupFilter
12
11
  from .settings import cs_settings
13
12
 
14
13
 
15
14
  class BaseModelViewSet(ModelViewSet):
16
15
  lookup_field = "id"
16
+ is_singleton = False
17
17
  parser_classes = [JSONParser]
18
18
  renderer_classes = [JSONRenderer]
19
19
  permission_classes = [DjangoModelPermissions]
20
- pagination_class = [PageNumberPagination]
21
- filter_backends = [SearchFilter, OrderingFilter]
20
+ filter_backends = [SearchFilter, OrderingFilter, LookupFilter]
22
21
 
23
- def __init__(self):
22
+ def __init__(self, *args, **kwargs):
24
23
  super(BaseModelViewSet, self).__init__()
24
+ admin_site = cs_settings.ADMIN_SITE
25
25
 
26
26
  self.authentication_classes = [
27
- admin_site.auth_backend.active_backend.authentication_class
27
+ admin_site.token_backend.active_backend.authentication_class
28
28
  ]
29
29
 
30
- def retrieve(self, request, *args, **kwargs):
31
- """
32
- Disable this endpoint for singletons. They should use
33
- the list endpoint instead.
34
- """
35
- if self.is_singleton:
36
- raise MethodNotAllowed(
37
- method="GET",
38
- detail="Singleton objects do not support the retrieve endpoint.",
39
- )
40
-
41
- return super().retrieve(request, *args, **kwargs)
42
-
43
- def update(self, request, *args, **kwargs):
44
- """
45
- We overwrite the update method to support singletons. If a singleton
46
- doesn't exist it will be created.
47
- """
48
- if self.is_singleton:
49
- try:
50
- super().update(request, *args, **kwargs)
51
- except NotFound:
52
- self.create(request, *args, **kwargs)
53
-
54
- return super().update(request, *args, **kwargs)
55
-
56
30
  def list(self, request, *args, **kwargs):
57
31
  """
58
32
  We overwrite the list method to support singletons. If a singleton
@@ -66,8 +40,8 @@ class BaseModelViewSet(ModelViewSet):
66
40
  def perform_create(self, serializer):
67
41
  instance = serializer.save()
68
42
 
69
- if hasattr(instance, cs_settings.CREATED_BY):
70
- setattr(instance, cs_settings.CREATED_BY, self.request.user)
43
+ if hasattr(instance, cs_settings.CREATED_BY_ATTR):
44
+ setattr(instance, cs_settings.CREATED_BY_ATTR, self.request.user)
71
45
  instance.save()
72
46
 
73
47
  content_type = ContentType.objects.get_for_model(instance)
@@ -83,8 +57,8 @@ class BaseModelViewSet(ModelViewSet):
83
57
  def perform_update(self, serializer):
84
58
  instance = serializer.save()
85
59
 
86
- if hasattr(instance, cs_settings.EDITED_BY):
87
- setattr(instance, cs_settings.EDITED_BY, self.request.user)
60
+ if hasattr(instance, cs_settings.EDITED_BY_ATTR):
61
+ setattr(instance, cs_settings.EDITED_BY_ATTR, self.request.user)
88
62
  instance.save()
89
63
 
90
64
  content_type = ContentType.objects.get_for_model(instance)
@@ -116,13 +90,11 @@ class BaseModelViewSet(ModelViewSet):
116
90
  If a singleton doesn't exist it will raise a NotFound exception.
117
91
  """
118
92
  if self.is_singleton:
119
- try:
120
- return self.get_queryset().get()
121
- except self.queryset.model.DoesNotExist:
93
+ singleton = self.get_queryset().first()
94
+
95
+ if singleton:
96
+ return singleton
97
+ else:
122
98
  raise NotFound()
123
99
 
124
100
  return super().get_object()
125
-
126
- @property
127
- def is_singleton(self):
128
- return getattr(self.get_queryset().model, "is_singleton", False)
content_studio/widgets.py CHANGED
@@ -1,30 +1,83 @@
1
- class InputWidget:
1
+ ###
2
+ # Widgets determine what input field is used for a certain
3
+ # model field. Each Django model field has a default widget,
4
+ # but this can be overridden in the admin model.
5
+ ###
6
+
7
+
8
+ class BaseWidget:
9
+ @classmethod
10
+ def serialize(cls):
11
+ return {"name": cls.__name__}
12
+
13
+
14
+ class InputWidget(BaseWidget):
15
+ pass
16
+
17
+
18
+ class TextAreaWidget(BaseWidget):
19
+ pass
20
+
21
+
22
+ class SwitchWidget(BaseWidget):
23
+ pass
24
+
25
+
26
+ class CheckboxWidget(BaseWidget):
27
+ pass
28
+
29
+
30
+ class DateWidget(BaseWidget):
31
+ pass
32
+
33
+
34
+ class DateTimeWidget(BaseWidget):
35
+ pass
36
+
37
+
38
+ class TimeWidget(BaseWidget):
39
+ pass
40
+
41
+
42
+ class RichTextWidget(BaseWidget):
43
+ pass
44
+
45
+
46
+ class RadioButtonWidget(BaseWidget):
47
+ pass
48
+
49
+
50
+ class SelectWidget(BaseWidget):
51
+ pass
52
+
53
+
54
+ class MultiSelectWidget(BaseWidget):
2
55
  pass
3
56
 
4
57
 
5
- class TextAreaWidget:
58
+ class URLPathWidget(BaseWidget):
6
59
  pass
7
60
 
8
61
 
9
- class BooleanWidget:
62
+ class SlugWidget(BaseWidget):
10
63
  pass
11
64
 
12
65
 
13
- class TagWidget:
66
+ class ForeignKeyWidget(BaseWidget):
14
67
  pass
15
68
 
16
69
 
17
- class RichTextWidget:
70
+ class ManyToManyWidget(BaseWidget):
18
71
  pass
19
72
 
20
73
 
21
- class MultipleChoiceWidget:
74
+ class JSONSchemaWidget(BaseWidget):
22
75
  pass
23
76
 
24
77
 
25
- class URLPathWidget:
78
+ class JSONWidget(BaseWidget):
26
79
  pass
27
80
 
28
81
 
29
- class SlugWidget:
82
+ class MediaWidget(BaseWidget):
30
83
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-content-studio
3
- Version: 1.0.0a1
3
+ Version: 1.0.0b1
4
4
  Summary: Modern & flexible Django admin
5
5
  License: MIT
6
6
  Author: Leon van der Grient
@@ -23,8 +23,6 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  Django Content Studio is a modern, flexible alternative to the Django admin.
25
25
 
26
- > This package is still under development
27
-
28
26
  ## 🚀 Quick Start
29
27
 
30
28
  ### Installation
@@ -63,8 +61,8 @@ urlpatterns = [
63
61
 
64
62
  ## 🐛 Issues & Support
65
63
 
66
- - 🐛 **Bug Reports**: [GitHub Issues](https://github.com/StructuralRealist/django-content-studio/issues)
67
- - 💬 **Discussions**: [GitHub Discussions](https://github.com/StructuralRealist/django-content-studio/discussions)
64
+ - 🐛 **Bug Reports**: [GitHub Issues](https://github.com/BitsOfAbstraction/django-content-studio/issues)
65
+ - 💬 **Discussions**: [GitHub Discussions](https://github.com/BitsOfAbstraction/django-content-studio/discussions)
68
66
  - 📧 **Email**: leon@devtastic.io
69
67
 
70
68
  ## 📄 License
@@ -80,7 +78,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
80
78
  ## 🔗 Links
81
79
 
82
80
  - [PyPI Package](https://pypi.org/project/django-content-studio/)
83
- - [GitHub Repository](https://github.com/StructuralRealist/django-content-studio)
81
+ - [GitHub Repository](https://github.com/BitsOfAbstraction/django-content-studio)
84
82
  - [Changelog](CHANGELOG.md)
85
83
 
86
84
  ---
@@ -0,0 +1,29 @@
1
+ content_studio/__init__.py,sha256=-iiOBZCWYYqtuukOLDIAjBLRMgIZR5LlhNFYdjbRxMY,161
2
+ content_studio/admin.py,sha256=cmTXKho64QrMMBoJtdMGMUpDjYORz3JjfutArFCAdyM,10017
3
+ content_studio/apps.py,sha256=cfZvEixECgLN0g2zbu_Y94u5k3QBMyr_bgsgj4qYuoA,3503
4
+ content_studio/dashboard/__init__.py,sha256=1GQcAuM5IlTlvqq6aPYA_QfuiEy_It0qx54VqDMQHCQ,1788
5
+ content_studio/dashboard/activity_log.py,sha256=wTkDiHY4p2ijhcSJ0xPiSk5xxvFQUYjVoI1hKi7bdzE,813
6
+ content_studio/filters.py,sha256=GyglR2E_wswomW7EnfShEXr14zxuRlLAuxrHVO3QQYg,4335
7
+ content_studio/form.py,sha256=8yCas7l8Z3sLE6npo6JnTGyGCxez1v-PBIfbodVWOd0,2733
8
+ content_studio/formats.py,sha256=JzOWrDRCVSnjJI0oX5fyFQU8LC634_jeAe84Widy43U,786
9
+ content_studio/login_backends/__init__.py,sha256=hgPJh9hFobubImfhynaeZ_dku3sjqNQvN5B43TVg7Gw,643
10
+ content_studio/login_backends/username_password.py,sha256=JJbF3oWnhOwLs-WaqclCCH6nAnrhlKbyBUzAJKVtq7A,2561
11
+ content_studio/media_library/serializers.py,sha256=1MPFqbZdE0iTYqZrzzYoT_y4q0ItGTbQ-I7omyojAZo,660
12
+ content_studio/media_library/viewsets.py,sha256=XsQuCrcfZ52xvvsEbHZ2WTI99wf-cheOKfaMJyfAJbo,4318
13
+ content_studio/models.py,sha256=SLcgMIXu4FqwbQJBQ-jLw7_hlGjbSmwHmyKfzDsj-2M,2068
14
+ content_studio/paginators.py,sha256=XrA6ECP2pO75SSj0Mi0Jqj7n_YPuIin-V8_6EOaQj64,589
15
+ content_studio/router.py,sha256=7Up_sipGaUDoY6ElJNRf85ADaYfJCWV4To523L4LGuw,393
16
+ content_studio/serializers.py,sha256=k5QLRiaa85jEiyljz2QdyTFcWY4gT8Lb28dBZsdEcsQ,3674
17
+ content_studio/settings.py,sha256=6U0o-DxwpFQo-1LLumr3U4FCVTHiBzySK7M77HWvpf4,4510
18
+ content_studio/templates/content_studio/index.html,sha256=lRSGmnyEHE_U1evO1O7K8nSGjmTgnR8z2OIZYmi2UwY,675
19
+ content_studio/token_backends/__init__.py,sha256=dO3aWIHXX8He399ZEvlS4fNgyL84OY9TEtkva9b5N5Q,1183
20
+ content_studio/token_backends/jwt.py,sha256=niGCpRqaUVhhS9haXfH1uFlg2v8NLB5IpsJ4N79Q0Gg,1565
21
+ content_studio/urls.py,sha256=EY7lbzC0Q5vLfvqE3rK_4hmDrXhTuxKzYC55cc5tEEo,701
22
+ content_studio/utils.py,sha256=xOolXd9Zmty6B6_2febvM8TmZhZ0JRc2nCLj3VooHkM,1488
23
+ content_studio/views.py,sha256=GbANmQY_VyzQPD4Hw3MEfWF3pOr2G6a61Ktvu2Bw6d8,5370
24
+ content_studio/viewsets.py,sha256=GrgtiK-HlrAfi4ubpLrBADszJMWxAupkBfIoS6OyIeA,3380
25
+ content_studio/widgets.py,sha256=OlFKCAYNhkj2Ww9S_hvQTzUjcSnYXocmi04zusKrOTo,1071
26
+ django_content_studio-1.0.0b1.dist-info/LICENSE,sha256=Wnx2EJhtSNnXE5Qs80i1HTBNFZTi8acEtC5TYqtFlnQ,1075
27
+ django_content_studio-1.0.0b1.dist-info/METADATA,sha256=O7FLotNqu9ekxZ64SBrCYix43ohrFj1n3NgiiCMXt-c,2512
28
+ django_content_studio-1.0.0b1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
29
+ django_content_studio-1.0.0b1.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- class Dashboard:
2
- """
3
- The Dashboard class is used to define the structure of the dashboard
4
- in Django Content Studio.
5
- """
6
-
7
- pass
@@ -1,23 +0,0 @@
1
- content_studio/__init__.py,sha256=9XVz23GA6n6RDutB7K90md10AoDlSqDnilzAo1VCvhc,172
2
- content_studio/admin.py,sha256=H5gx6RKljSITL9UtE3yech97o2ZZhALgS9Mf0yWechA,4812
3
- content_studio/apps.py,sha256=OmOcBLnzUnP5T8oOd1iTrNNnrDRHUsX6WMQQV1IEc00,1880
4
- content_studio/dashboard.py,sha256=ptQ-cYp9ltsWeO3YK7Q_7WGgYwwzuM6zU0rxcuybjw4,146
5
- content_studio/form.py,sha256=-SZZ_OBCgn023GWMJEQqVdyZLA2hqDtDCdrqVLB4mNE,2006
6
- content_studio/login_backends/__init__.py,sha256=hgPJh9hFobubImfhynaeZ_dku3sjqNQvN5B43TVg7Gw,643
7
- content_studio/login_backends/username_password.py,sha256=QRkilK5E2S2o6xfK_jK64OdJ93Ima3lRYnvjncnVaH8,2441
8
- content_studio/models.py,sha256=Kk4Hw3T3impuWZ5Pfq1259KILq3wyqIBsXfA0MyuA3A,2613
9
- content_studio/router.py,sha256=7Up_sipGaUDoY6ElJNRf85ADaYfJCWV4To523L4LGuw,393
10
- content_studio/serializers.py,sha256=uOUXe7aDpgYN6B5rlZsN1_pFSPspV5exg3L7RxsTEhg,393
11
- content_studio/settings.py,sha256=wbwNN_RAk6Sv8hlB02dOveqUCzc-TVThau5rO47ANS0,4306
12
- content_studio/templates/content_studio/index.html,sha256=nzCbBJGhZijNYszIMB-NvCIqCP7KzDbpleKH1dVOw-c,729
13
- content_studio/token_backends/__init__.py,sha256=dO3aWIHXX8He399ZEvlS4fNgyL84OY9TEtkva9b5N5Q,1183
14
- content_studio/token_backends/jwt.py,sha256=Gd0IuhS6rjCzjTqkHWFMSpIt-x-07Jgr59moAVympBw,1667
15
- content_studio/urls.py,sha256=Rkb6QuyAGtn8MqkK7GmyVJXAq-q0mQT3CedhBSaCOr0,367
16
- content_studio/utils.py,sha256=8MibAxBgSZASZjPbPPpW9KplCUSHlM4TbUMkCNh5SDs,707
17
- content_studio/views.py,sha256=FR9CxuYJ-YiSVnxiFEwZlfpWfFSKv1hKtbUB6PC5qDE,2662
18
- content_studio/viewsets.py,sha256=sWbPRXm6UEkKUYPCjALZACWbixwI68BO_WB6Z2pj0Pg,4342
19
- content_studio/widgets.py,sha256=cBGvFrklzDkbwa39jhXc4Jp-kvykahpA01yk307VRXw,254
20
- django_content_studio-1.0.0a1.dist-info/LICENSE,sha256=Wnx2EJhtSNnXE5Qs80i1HTBNFZTi8acEtC5TYqtFlnQ,1075
21
- django_content_studio-1.0.0a1.dist-info/METADATA,sha256=GQq4ncwdxjf3FW2dW_jJUsaie0Lv0EVq0l2qhmNj8e4,2555
22
- django_content_studio-1.0.0a1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
23
- django_content_studio-1.0.0a1.dist-info/RECORD,,