Skip to content

Commit

Permalink
Add more base viewsets for import/export
Browse files Browse the repository at this point in the history
* Add base import/export views what only allows users to work with their
 own jobs (`ImportJobForUserViewSet` and `ExportJobForUserViewSet`).
* Small actions definition refactor in
`ExportJobViewSet/ExportJobViewSet` to allow easier overriding.
  • Loading branch information
TheSuperiorStanislav committed Jan 8, 2025
1 parent 8f3b2c1 commit f6187be
Show file tree
Hide file tree
Showing 19 changed files with 382 additions and 55 deletions.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ ENV/
.ruff_cache/

# IDE settings
.vscode/
.vscode/*
!.vscode/recommended_settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/boilerplate-words.txt
!.vscode/project-related-words.txt
!.vscode/cspell.json
.idea/

# Test files
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repos:
stages: [pre-push]
- id: tests
name: run tests
entry: inv pytest.run --params="--cov=."
entry: inv pytest.run --params="--numprocesses auto --create-db --cov=."
language: system
pass_filenames: false
types: [python]
Expand Down
57 changes: 57 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Django",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/test_project/manage.py",
"preLaunchTask": "Launch containers and wait for DB",
"args": [
"runserver_plus",
"localhost:8000",
],
"django": true,
"justMyCode": false
},
{
"name": "Python: Django With SQL Logs",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/test_project/manage.py",
"preLaunchTask": "Launch containers and wait for DB",
"args": [
"runserver_plus",
"localhost:8000",
"--print-sql"
],
"django": true,
"justMyCode": false
},
{
"name": "Python: Celery",
"type": "debugpy",
"request": "launch",
"module": "celery",
"preLaunchTask": "Launch containers and wait for DB",
"args": [
"--app",
"test_project.celery_app.app",
"worker",
"--beat",
"--scheduler=django",
"--loglevel=info",
],
"justMyCode": false
},
{
"name": "Python: Debug Tests",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"purpose": ["debug-test"],
"console": "integratedTerminal",
"justMyCode": false
},
]
}
26 changes: 26 additions & 0 deletions .vscode/recommended_settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"files.exclude": {
"**/__pycache__": true,
"**/.pytest_cache": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/htmlcov": true,
},

"editor.rulers": [79],

"editor.bracketPairColorization.enabled": true,

"python.analysis.typeCheckingMode": "off",

"python.analysis.inlayHints.functionReturnTypes": true,
"mypy.enabled": false,

"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,

"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
15 changes: 15 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Launch containers and wait for DB",
"type": "shell",
"command": "inv django.wait-for-database",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
}
]
}
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
History
=======

UNRELEASED
------------------

* Add base import/export views that only allow users to work with their own jobs (`ImportJobForUserViewSet` and `ExportJobForUserViewSet`).
* Small actions definition refactor in `ExportJobViewSet/ExportJobViewSet` to allow easier overriding.

1.2.0 (2024-12-26)
------------------
* Fix issue with slow export duration (https://github.com/saritasa-nest/django-import-export-extensions/issues/79):
Expand Down
18 changes: 15 additions & 3 deletions import_export_extensions/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
from .serializers.export_job import CreateExportJob, ExportJobSerializer
from .serializers.import_job import CreateImportJob, ImportJobSerializer
from .serializers.progress import ProgressInfoSerializer, ProgressSerializer
from .mixins import LimitQuerySetToCurrentUserMixin
from .serializers import (
CreateExportJob,
CreateImportJob,
ExportJobSerializer,
ImportJobSerializer,
ProgressInfoSerializer,
ProgressSerializer,
)
from .views import (
ExportJobForUserViewSet,
ExportJobViewSet,
ImportJobForUserViewSet,
ImportJobViewSet,
)
10 changes: 10 additions & 0 deletions import_export_extensions/api/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class LimitQuerySetToCurrentUserMixin:
"""Make queryset to return only current user jobs."""

def get_queryset(self):
"""Return user's jobs."""
return (
super()
.get_queryset()
.filter(created_by_id=getattr(self.request.user, "pk", None))
)
13 changes: 11 additions & 2 deletions import_export_extensions/api/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
from .export_job import ExportJobSerializer, get_create_export_job_serializer
from .import_job import ImportJobSerializer, get_create_import_job_serializer
from .export_job import (
CreateExportJob,
ExportJobSerializer,
get_create_export_job_serializer,
)
from .import_job import (
CreateImportJob,
ImportJobSerializer,
get_create_import_job_serializer,
)
from .progress import ProgressInfoSerializer, ProgressSerializer
10 changes: 8 additions & 2 deletions import_export_extensions/api/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
from .export_job import ExportJobViewSet
from .import_job import ImportJobViewSet
from .export_job import (
ExportJobForUserViewSet,
ExportJobViewSet,
)
from .import_job import (
ImportJobForUserViewSet,
ImportJobViewSet,
)
56 changes: 33 additions & 23 deletions import_export_extensions/api/views/export_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import django_filters

from ... import models, resources
from .. import mixins as core_mixins
from .. import serializers


Expand All @@ -37,27 +38,11 @@ def __new__(cls, name, bases, attrs, **kwargs):
attrs,
**kwargs,
)
# Skip if it is a base viewset, since none of needed class attrs are
# specified
if name == "ExportJobViewSet":
# Skip if it is has no resource_class specified
if not hasattr(viewset, "resource_class"):
return viewset

def start(self: "ExportJobViewSet", request: Request):
"""Validate request data and start ExportJob."""
serializer = self.get_serializer(
data=request.data,
filter_kwargs=request.query_params,
)
serializer.is_valid(raise_exception=True)
export_job = serializer.save()
return response.Response(
data=self.get_detail_serializer_class()(
instance=export_job,
).data,
status=status.HTTP_201_CREATED,
)

viewset.start = decorators.action(
decorators.action(
methods=["POST"],
detail=False,
queryset=viewset.resource_class.get_model_queryset(),
Expand All @@ -67,7 +52,11 @@ def start(self: "ExportJobViewSet", request: Request):
filter_backends=[
django_filters.rest_framework.DjangoFilterBackend,
],
)(start)
)(viewset.start)
decorators.action(
methods=["POST"],
detail=True,
)(viewset.cancel)
# Correct specs of drf-spectacular if it is installed
with contextlib.suppress(ImportError):
from drf_spectacular.utils import extend_schema, extend_schema_view
Expand Down Expand Up @@ -110,8 +99,8 @@ class ExportJobViewSet(
permission_classes = (permissions.IsAuthenticated,)
queryset = models.ExportJob.objects.all()
serializer_class = serializers.ExportJobSerializer
resource_class: type[resources.CeleryModelResource] | None = None
filterset_class: django_filters.rest_framework.FilterSet = None
resource_class: type[resources.CeleryModelResource]
filterset_class: django_filters.rest_framework.FilterSet | None = None
search_fields = ("id",)
ordering = (
"id",
Expand Down Expand Up @@ -154,7 +143,21 @@ def get_export_create_serializer_class(self):
self.resource_class,
)

@decorators.action(methods=["POST"], detail=True)
def start(self, request: Request):
"""Validate request data and start ExportJob."""
serializer = self.get_serializer(
data=request.data,
filter_kwargs=request.query_params,
)
serializer.is_valid(raise_exception=True)
export_job = serializer.save()
return response.Response(
data=self.get_detail_serializer_class()(
instance=export_job,
).data,
status=status.HTTP_201_CREATED,
)

def cancel(self, *args, **kwargs):
"""Cancel export job that is in progress."""
job: models.ExportJob = self.get_object()
Expand All @@ -169,3 +172,10 @@ def cancel(self, *args, **kwargs):
status=status.HTTP_200_OK,
data=serializer.data,
)


class ExportJobForUserViewSet(
core_mixins.LimitQuerySetToCurrentUserMixin,
ExportJobViewSet,
):
"""Viewset for providing export feature to users."""
30 changes: 23 additions & 7 deletions import_export_extensions/api/views/import_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
)

from ... import models, resources
from .. import mixins as core_mixins
from .. import serializers


Expand All @@ -32,11 +33,23 @@ def __new__(cls, name, bases, attrs, **kwargs):
attrs,
**kwargs,
)
# Skip if it is a base viewset, since none of needed class attrs are
# specified
if name == "ImportJobViewSet":
# Skip if it is has no resource_class specified
if not hasattr(viewset, "resource_class"):
return viewset

decorators.action(
methods=["POST"],
detail=False,
)(viewset.start)
decorators.action(
methods=["POST"],
detail=True,
)(viewset.confirm)
decorators.action(
methods=["POST"],
detail=True,
)(viewset.cancel)

# Correct specs of drf-spectacular if it is installed
with contextlib.suppress(ImportError):
from drf_spectacular.utils import extend_schema, extend_schema_view
Expand Down Expand Up @@ -89,7 +102,7 @@ class ImportJobViewSet(
permission_classes = (permissions.IsAuthenticated,)
queryset = models.ImportJob.objects.all()
serializer_class = serializers.ImportJobSerializer
resource_class: type[resources.CeleryModelResource] | None = None
resource_class: type[resources.CeleryModelResource]
search_fields = ("id",)
ordering = (
"id",
Expand Down Expand Up @@ -132,7 +145,6 @@ def get_import_create_serializer_class(self):
self.resource_class,
)

@decorators.action(methods=["POST"], detail=False)
def start(self, request, *args, **kwargs):
"""Validate request data and start ImportJob."""
serializer = self.get_serializer(data=request.data)
Expand All @@ -147,7 +159,6 @@ def start(self, request, *args, **kwargs):
status=status.HTTP_201_CREATED,
)

@decorators.action(methods=["POST"], detail=True)
def confirm(self, *args, **kwargs):
"""Confirm import job that has `parsed` status."""
job: models.ImportJob = self.get_object()
Expand All @@ -163,7 +174,6 @@ def confirm(self, *args, **kwargs):
data=serializer.data,
)

@decorators.action(methods=["POST"], detail=True)
def cancel(self, *args, **kwargs):
"""Cancel import job that is in progress."""
job: models.ImportJob = self.get_object()
Expand All @@ -178,3 +188,9 @@ def cancel(self, *args, **kwargs):
status=status.HTTP_200_OK,
data=serializer.data,
)

class ImportJobForUserViewSet(
core_mixins.LimitQuerySetToCurrentUserMixin,
ImportJobViewSet,
):
"""Viewset for providing import feature to users."""
1 change: 1 addition & 0 deletions invocations/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def init(context: invoke.Context, clean: bool = False):
"""Prepare env for working with project."""
saritasa_invocations.print_success("Setting up git config")
saritasa_invocations.git.setup(context)
saritasa_invocations.system.copy_vscode_settings(context)
saritasa_invocations.print_success("Initial assembly of all dependencies")
saritasa_invocations.poetry.install(context)
if clean:
Expand Down
Loading

0 comments on commit f6187be

Please sign in to comment.