Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions django_react_admin/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from rest_framework import serializers


class ActionSerializer(serializers.Serializer):
id = serializers.JSONField()
160 changes: 98 additions & 62 deletions django_react_admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@
from rest_framework.reverse import reverse_lazy
from rest_framework.routers import DefaultRouter
from rest_framework.serializers import ModelSerializer
from rest_framework.exceptions import APIException
from rest_framework import status
import urllib.parse
import json
from .serializers import ActionSerializer


router = DefaultRouter()
actions_urlpatterns = []
r = Request(HttpRequest())
r.user = get_user_model()(is_superuser=True)

Expand All @@ -24,16 +31,52 @@ class CustomPageNumberPagination(PageNumberPagination):
page_size_query_param = 'page_size' # items per page


def get_serializer_class(model, model_admin):
class MethodNotAllowed(APIException):
status_code = status.HTTP_403_FORBIDDEN
default_detail = {'error': True, 'message': 'method not allowed'}
default_code = 'method_not_allowed'


class IsAllowMethod(permissions.BasePermission):
def has_permission(self, request, view):
if hasattr(view.model_admin, "get_allowed_request_methods") and request.method in view.model_admin.get_allowed_request_methods(request):
return True
raise MethodNotAllowed()

class IsAllowAction(permissions.BasePermission):
def has_permission(self, request, view):
if hasattr(view.model_admin, "get_allowed_actions") and view.action.__name__ in view.model_admin.get_allowed_actions(request):
return True
raise MethodNotAllowed()


def to_representation(self, instance):
data = super(type(self), self).to_representation(instance)
for field in data:
if instance._meta.get_field(field).get_internal_type() in ("FileField", "ImageField"):
if data[field]:
data.update({field: urllib.parse.urlparse(data[field]).path})

return data

def get_serializer_class(self):
params = {
"to_representation": to_representation
}

meta_props = {
"model": model,
"fields": list(model_admin.get_fields(r)),
"read_only_fields": model_admin.readonly_fields
"model": self.model,
"fields": list(self.model_admin.get_fields(self.request)),
"read_only_fields": self.model_admin.get_readonly_fields(self.request)
}

return type(
f"{model.__name__}Serializer",
(ModelSerializer,),
{"Meta": type("Meta", (), meta_props)},
{
**params,
"Meta": type("Meta", (), meta_props)
}
)

def model_views_set_list(self, request, *args, **kwargs):
Expand Down Expand Up @@ -68,103 +111,96 @@ def get_filterset_fields(model_admin):

return filterset_fields

def get_info(model_admin):
def get_info(self):
def info(*args):
basic_params = {
"fields": list(model_admin.get_fields(r)),
"list_display": list(model_admin.get_list_display(r)),
"ordering_fields": list(model_admin.get_sortable_by(r)),
"filterset_fields": get_filterset_fields(model_admin),
"fields": list(self.model_admin.get_fields(self.request)),
"list_display": list(self.model_admin.get_list_display(self.request)),
"ordering_fields": list(self.model_admin.get_sortable_by(self.request)),
"filterset_fields": get_filterset_fields(self.model_admin)
}

form = [
dict(name=name, **field.widget.__dict__)
for name, field in model_admin.get_form(r)().fields.items()
for name, field in self.model_admin.get_form(self.request)().fields.items()
if not hasattr(field.widget, "widget")
]
return Response(
dict(form=form, **basic_params),
dict(form=form, **basic_params)
)

return info

def get_queryset(self):
queryset = self.model_admin.get_queryset(self.request)

return queryset

if not hasattr(model, 'objects'):
continue # Use case: dramatiq.models.Task

queryset = model.objects.all()
if model_admin.list_select_related:
queryset = queryset.select_related(*model_admin.list_select_related)

params = {
"queryset": queryset,
"model": model,
"model_admin": model_admin,
"get_queryset": get_queryset,
"filter_backends": [DjangoFilterBackend, OrderingFilter, SearchFilter],
"info": action(methods=["get"], detail=False)(get_info(model_admin)),
"serializer_class": get_serializer_class(model, model_admin),
"info": get_info,
"get_serializer_class": get_serializer_class,
"basename": model._meta.model_name,
"request": r,
"fields": list(model_admin.get_fields(r)),
"filterset_class": getattr(model_admin, 'filterset_class', None),
"list_display": list(model_admin.get_list_display(r)),
"ordering_fields": list(model_admin.get_sortable_by(r)),
"filterset_fields": get_filterset_fields(model_admin),
"search_fields": list(model_admin.get_search_fields(r)),
"permission_classes": getattr(
model_admin, 'permission_classes',
[permissions.IsAdminUser, permissions.DjangoModelPermissions]
[permissions.IsAuthenticated, IsAllowMethod]
),
"pagination_class": CustomPageNumberPagination,
"list": model_views_set_list
}
viewset = type(f"{model.__name__}ViewSet", (viewsets.ModelViewSet,), params)
router.register(
f"{model._meta.app_label}/{model._meta.model_name}", viewset
f"{model._meta.app_label}/{model._meta.model_name}", viewset, model._meta.model_name
)
viewpath = f"{model._meta.app_label}/{model._meta.model_name}"
# urlpatterns.append(
# path(
# r"html/{}/".format(viewpath),
# TemplateView.as_view(
# template_name="django_react_admin/list.html",
# extra_context={
# "app": model._meta.app_label,
# "model": model._meta.model_name,
# "path": reverse_lazy(model._meta.model_name+"-list"),
# },
# ),
# )
# )
# urlpatterns.append(
# path(
# r"html/{}/add/".format(viewpath),
# TemplateView.as_view(
# template_name="django_react_admin/edit.html",
# extra_context={
# "create": True,
# "app": model._meta.app_label,
# "model": model._meta.model_name,
# "path": reverse_lazy(model._meta.model_name + "-list"),
# },
# ),
# )
# )
# urlpatterns.append(
# path(
# r"html/{}/<pk>/".format(viewpath),
# TemplateView.as_view(
# template_name="django_react_admin/edit.html",
# extra_context={
# "create": False,
# "app": model._meta.app_label,
# "model": model._meta.model_name,
# "path": reverse_lazy(model._meta.model_name + "-list"),
# },
# ),
# )
# )

if model_admin.actions:
for action in model_admin.actions:
action_title = action.__name__.replace("_", " ").title().replace(" ", "")

def method_post(self, request):
serializer = self.serializer_class(data=json.loads(request.body))
if serializer.is_valid():
queryset = self.model_admin.get_queryset(request).filter(id__in=serializer.validated_data["id"])
self.action(request, queryset)

return Response("ok")
else:
return Response(serializer.errors)

params = {
"permission_classes": [permissions.IsAuthenticated, IsAllowAction],
"serializer_class": ActionSerializer,
"model_admin": model_admin,
"action": action,
"post": method_post,
}
apiview = type(f"{model.__name__}Action{action_title}APIView", (views.APIView,), params)

actions_urlpatterns.append(path(
f"{model._meta.app_label}/{model._meta.model_name}/{action.__name__}/",
apiview.as_view(),
name=f"{model._meta.model_name}-{action.__name__}"
))

class Index(views.APIView):
def get(self, request):
res = admin.site.get_app_list(request)
# return Response([m['admin_url'].replace(reverse('admin:index'), '') for app in res for m in app['models']])
for app in res:
app['app_url'] = app['app_url'].replace(reverse('admin:index'), '')
for m in app['models']:
Expand All @@ -176,4 +212,4 @@ def get(self, request):
return Response(res)


urlpatterns = [path('', Index.as_view(), name='react_admin_index')] + router.urls
urlpatterns = [path('', Index.as_view(), name='react_admin_index')] + actions_urlpatterns + router.urls