diff --git a/django_oapif/handler.py b/django_oapif/handler.py index 212ab2fd..58c507de 100644 --- a/django_oapif/handler.py +++ b/django_oapif/handler.py @@ -20,24 +20,38 @@ def delete_model(self, _request: HttpRequest, obj: M) -> tuple[int, dict[str, in return obj.delete() def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: + """Return True if the given request has permission to view objects in the collection, + or a given object if defined. + """ return True def has_add_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: + """Return True if the given request has permission to create objects in the collection, + or a given object if defined. + """ return True def has_change_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: + """Return True if the given request has permission to change objects in the collection, + or a given object if defined. + """ return True def has_delete_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: + """Return True if the given request has permission to delete objects in the collection, + or a given object if defined. + """ return True -class AllowAnyHandler(QueryHandler): +class AllowAny(QueryHandler): + """Allows full access to everyone.""" + pass -class AuthenticatedHandler[M: Model](QueryHandler): - """Allows access only to authenticated users.""" +class IsAuthenticated[M: Model](QueryHandler): + """Allows full access to authenticated users only.""" def has_view_permission(self, request: HttpRequest, _obj: M | None = None) -> bool: return bool(request.user and request.user.is_authenticated) @@ -52,14 +66,16 @@ def has_delete_permission(self, request: HttpRequest, _obj: M | None = None) -> return bool(request.user and request.user.is_authenticated) -class AuthenticatedOrReadOnlyHandler[M: Model](AuthenticatedHandler): - """Allows access only to authenticated users.""" +class IsAuthenticatedOrReadOnly[M: Model](IsAuthenticated): + """Allows full access to authenticated users only, but allows readonly access to everyone.""" def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: return True -class DjangoModelPermissionsHandler[M: Model](QueryHandler): +class DjangoModelPermissions[M: Model](QueryHandler): + """Reuses all Django permissions for a given model.""" + def has_view_permission(self, request: HttpRequest, _obj: M | None = None) -> bool: codename = get_permission_codename("view", self.opts) return request.user.has_perm(f"{self.opts.app_label}.{codename}") @@ -77,6 +93,8 @@ def has_delete_permission(self, request: HttpRequest, _obj: M | None = None) -> return request.user.has_perm(f"{self.opts.app_label}.{codename}") -class DjangoModelPermissionsOrAnonReadOnly[M: Model](DjangoModelPermissionsHandler): +class DjangoModelPermissionsOrAnonReadOnly[M: Model](DjangoModelPermissions): + """Reuses all Django permissions for a given model, but allows readonly access to everyone.""" + def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool: return True diff --git a/docs/content/api.md b/docs/content/api.md index c50394dd..c4212a41 100644 --- a/docs/content/api.md +++ b/docs/content/api.md @@ -5,12 +5,12 @@ members: [register] - + - DjangoModelPermissionsOrAnonReadOnly diff --git a/docs/content/demo.md b/docs/content/demo.md index 390c7db1..f93352fc 100644 --- a/docs/content/demo.md +++ b/docs/content/demo.md @@ -20,8 +20,7 @@ docker compose up --build -d docker compose exec django python manage.py collectstatic --no-input docker compose exec django python manage.py migrate --no-input -# A convenience start-up Django command is there -# to populate the database with testdata +# A convenience start-up Django command is there to populate the database with testdata docker compose exec django python manage.py populate_users docker compose exec django python manage.py populate_data ``` diff --git a/docs/content/index.md b/docs/content/index.md index 887d0702..cca94797 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -6,4 +6,5 @@ hide: # Django-OAPIF *Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint. + It is based on Django Ninja. diff --git a/docs/content/permissions.md b/docs/content/permissions.md index 80bbbb61..6aeb8941 100644 --- a/docs/content/permissions.md +++ b/docs/content/permissions.md @@ -5,21 +5,29 @@ hide: # Custom authentication & permissions -By default the viewsets use [`DjangoModelPermissionsOrAnonReadOnly`] django_oapif.permissions.DjangoModelPermissionsOrAnonReadOnly. - -This can be altered in the DRF settings by adapting `DEFAULT_PERMISSION_CLASSES`. +By default the viewsets use [`DjangoModelPermissionsOrAnonReadOnly`](api/#django_oapif.handler.DjangoModelPermissionsOrAnonReadOnly). You can also add custom permissions when registering their corresponding viewsets, as [`permission_classes`] django_oapif.permissions. Example in `models.py`: ```python +# models.py + from django.contrib.gis.db import models + +class MyModel(models.Model): + ... +``` + + +```python +# ogc.py + +from .models import MyModel from django_oapif import OAPIF -from django_oapif.permissions import OAPIF, DjangoModelPermissionsOrAnonReadOnly +from django_oapif.handler import DjangoModelPermissionsOrAnonReadOnly ogc_api = OAPIF() -@ogc_api.register(auth=DjangoModelPermissionsOrAnonReadOnly) -class MyModel(models.Model): - ... +ogc_api.register(MyModel, handler=DjangoModelPermissionsOrAnonReadOnly) ``` diff --git a/docs/content/quick-start.md b/docs/content/quick-start.md index 44248df1..3ed7b0d7 100644 --- a/docs/content/quick-start.md +++ b/docs/content/quick-start.md @@ -25,28 +25,44 @@ INSTALLED_APPS = [ ] ``` -Add this to your `urls.py` : +## Declare your models: + ```python -urlpatterns += [ - ..., - path("oapif/", include(django_oapif.urls)), - ..., -] +# models.py + +from django.contrib.gis.db import models + +class TestModel(models.Model): + name = models.CharField(max_length=10) + geom = models.PointField(srid=2056) + +class OtherTestModel(models.Model): + id = models.CharField(max_length=10) + geom = models.PolygonField(srid=2056) ``` -## Register your models with the decorator: +## Declare a new OAPIF and register your models: ```python -# models.py +# ogc.py -from django.contrib.gis.db import models +from .models import TestModel from django_oapif import OAPIF ogc_api = OAPIF() +@ogc_api.register(TestModel) +@ogc_api.register(OtherTestModel) +``` -@ogc_api.register() -class TestingDecorator(models.Model): - name = models.CharField(max_length=10) - geom = models.PointField(srid=2056) + +## Add the API to the Django URLs: +```python +# urls.py + +urlpatterns += [ + ..., + path("oapif/", include(django_oapif.urls)), + ..., +] ``` diff --git a/tests/django_oapif_tests/tests/ogc.py b/tests/django_oapif_tests/tests/ogc.py index a120cc6e..f73ae175 100644 --- a/tests/django_oapif_tests/tests/ogc.py +++ b/tests/django_oapif_tests/tests/ogc.py @@ -1,5 +1,5 @@ from django_oapif import OAPIF -from django_oapif.handler import DjangoModelPermissionsHandler +from django_oapif.handler import DjangoModelPermissions, DjangoModelPermissionsOrAnonReadOnly from .models import ( Line_2056_10fields, @@ -70,5 +70,5 @@ "field_int", *[f"field_str_{i}" for i in range(10)], ], - handler=DjangoModelPermissionsHandler, + handler=DjangoModelPermissions, )