Skip to content
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
fc6b89c
Use ninja instead of DRF
oreilles May 27, 2025
c005f47
Rename ogc to api
oreilles May 27, 2025
01bb81a
Refactor
oreilles May 27, 2025
eb764cf
Add crs and bbox-crs query params support
oreilles May 27, 2025
b4b5e15
Add features pagination
oreilles May 27, 2025
9f5dbd4
Don't transform geometry if requested in same CRS
oreilles May 28, 2025
e07cb1f
Add prev link before next
oreilles May 28, 2025
0d4720d
Fix collection links
oreilles May 28, 2025
2e07845
Use decorated pattern
oreilles May 28, 2025
e2685f9
Add SecretLayer back and use django permissions
oreilles May 30, 2025
309c102
Update conformance Dockerfile
oreilles Jun 2, 2025
edc16c4
Fix
oreilles Jun 2, 2025
65118b0
Allow geometry input with custom CRS
oreilles Jun 3, 2025
66b399c
Add bbox to FeatureCollection
oreilles Jun 4, 2025
3154855
Update conformance
oreilles Jun 4, 2025
82666a1
Add itemType to collections
oreilles Jun 4, 2025
cb72d8a
Update conformances
oreilles Jun 5, 2025
9568842
Create one router per collection and modularize authorization
oreilles Jun 6, 2025
ad30d5b
Validate schemas for each collections
oreilles Jun 6, 2025
c2456e1
Prevent invalid properties
oreilles Jun 6, 2025
258ced5
Fix collections bbox
oreilles Jun 13, 2025
d86ef81
Add ninja to installed apps
oreilles Jun 13, 2025
6c9c083
Remove unused dependencies
oreilles Jun 13, 2025
017af4f
Update docs
oreilles Jun 13, 2025
0283365
Remove obsolete drf code
oreilles Jun 13, 2025
9b3593c
Mark geometry field as optional if nullable in database
oreilles Jun 13, 2025
7499ace
Update tests
oreilles Jun 13, 2025
5edcdda
Update README
oreilles Jun 13, 2025
4a79f81
Make FeatureCollection bbox optional in case no features are returned
oreilles Jun 16, 2025
82e79e4
Add OPTIONS endpoints and update tests
oreilles Jun 16, 2025
c14be1c
Add collections schema endpoint
oreilles Jun 16, 2025
d19704c
Return correct code and header for POST on items
oreilles Jun 17, 2025
d2c236f
Use BasicAuth and DjangoAuth by default
oreilles Jun 17, 2025
c916e91
Remove unused OPTION body
oreilles Jun 17, 2025
ee8171c
Update QGIS integration test
oreilles Jun 17, 2025
d1ab8fd
Update API tests
oreilles Jun 17, 2025
e9d25ec
Update compose files
oreilles Jun 17, 2025
7c8a14d
Update CI
oreilles Jun 17, 2025
4081b30
Run pre-commit formatting
oreilles Jul 10, 2025
e74c4fd
Add missing exports in __init__.py
oreilles Jul 10, 2025
d724a37
Update `geometry_field` in `register`
oreilles Jul 10, 2025
56b9af2
Fix exclude
oreilles Jul 29, 2025
16163e9
Fix MultiPolygon schema
oreilles Jul 29, 2025
677e566
Fix request with default properties_fields
oreilles Jul 29, 2025
b7a1c10
Fix results handling on collections without or with empty geometries
oreilles Jul 29, 2025
dd7d705
Fix pydantic validation for relational models
oreilles Jul 29, 2025
7ed9edc
Fix pydantic validation for relational models
oreilles Jul 29, 2025
6db122e
Remove re-export of permissions from module root
oreilles Jul 30, 2025
d86851d
Add schema page to collection resources
oreilles Jul 30, 2025
3ddbec9
Don't use alias for properties json schema
oreilles Jul 30, 2025
be23c26
Patch json schema with nullable fields
oreilles Jul 30, 2025
289ef48
Prevent unbound Geom type alias
oreilles Jul 30, 2025
c3e9041
Prevent KeyError in json schema generation
oreilles Jul 30, 2025
ff7603a
Prevent KeyError in json schema generation
oreilles Jul 30, 2025
5115f39
Fix json schema generation
oreilles Jul 30, 2025
6f4bf77
Add unique `operation_id`s
oreilles Aug 7, 2025
9a49359
Allow `NinjaAPI` kwargs in OAPIF constructor
oreilles Aug 7, 2025
e3d9739
Handle setting empty geometry with PUT
oreilles Aug 11, 2025
f9700c6
Add object-level permissions and custom query handler
oreilles Aug 14, 2025
afc663f
Replace decorator design by declarative design
oreilles Aug 15, 2025
751ee86
Fix geojson types
oreilles Aug 20, 2025
344a5f5
Fix geometry format name
oreilles Aug 20, 2025
85d637c
Fix geojson generic schema
oreilles Aug 21, 2025
84031e7
Fix unused query param warning
oreilles Aug 22, 2025
20928a7
Add foreign key handling
oreilles Aug 25, 2025
ee1b449
Fix null foreign key handling
oreilles Aug 25, 2025
c8bf807
Allow providing own NinjaApi
oreilles Aug 26, 2025
9e37865
Merge permissions and handler
oreilles Aug 27, 2025
e66a55e
Don't drop primary key field in properties by default
oreilles Sep 23, 2025
3325a08
Set proper type for `item_id`
oreilles Sep 25, 2025
94cbc5a
Fix exclude fields definition
oreilles Sep 25, 2025
785da0c
Revert previous change
oreilles Sep 25, 2025
8a9af07
Set proper type for `item_id` in options_item
oreilles Sep 25, 2025
b92d583
Don't drop primary key field in properties by default
oreilles Sep 25, 2025
4d33c10
Fix exclude fields definition
oreilles Sep 25, 2025
e86b126
Cleanup include fields declaration
oreilles Sep 25, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ jobs:

- name: Failure logs
if: failure()
run: docker-compose logs
run: docker compose logs
2 changes: 1 addition & 1 deletion .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@ jobs:

- name: Failure logs
if: failure()
run: docker-compose logs
run: docker compose logs
14 changes: 7 additions & 7 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ jobs:
key: ${{ secrets.KEY }}
script: |
cd /home/ubuntu/django-ogcapif
docker-compose down -v --remove-orphans
docker compose down -v --remove-orphans
git checkout main && git pull -f
docker-compose up -d --build --force-recreate
docker-compose exec django python manage.py migrate --no-input
docker-compose exec django python manage.py collectstatic --no-input
docker-compose exec django python manage.py populate_users
docker-compose exec django python manage.py populate_data
docker compose up -d --build --force-recreate
docker compose exec django python manage.py migrate --no-input
docker compose exec django python manage.py collectstatic --no-input
docker compose exec django python manage.py populate_users
docker compose exec django python manage.py populate_data

- name: Failure logs
if: failure()
run: docker-compose logs django_oapif_tests
run: docker compose logs django_oapif_tests
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ jobs:

- name: Failure logs
if: failure()
run: docker-compose logs
run: docker compose logs
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Django-OAPIF

*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint.
It is based on Django REST Framework.
It is based on Django Ninja.

https://opengisch.github.io/django-oapif
2 changes: 1 addition & 1 deletion django_oapif/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Django-OAPIF

*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint.
It is based on Django REST Framework.
It is based on Django Ninja.
10 changes: 10 additions & 0 deletions django_oapif/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
from . import permissions
from .api import OAPIF
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we stick only to one type of imports? I see almost everywhere else we use absolute imports, which are slightly more preferrable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I think we can even remove the re-export of permissions from here.


try:
from .__version__ import __version__, __version_tuple__
except ImportError:
__version__ = "0.0.0.dev"
__version_tuple__ = (0, 0, 0, "dev")

__all__ = [
"permissions",
"OAPIF",
"__version__",
"__version_tuple__",
]
83 changes: 83 additions & 0 deletions django_oapif/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from typing import Any, Optional

from django.conf import settings
from django.contrib.auth import authenticate
from django.db import models
from django.http import HttpRequest
from ninja import NinjaAPI
from ninja.security import APIKeyCookie, HttpBasicAuth

from django_oapif.collections import (
OAPIFCollectionEntry,
create_collection_router,
create_collections_router,
)
from django_oapif.conformance import create_conformance_router
from django_oapif.permissions import (
BasePermission,
DjangoModelPermissionsOrAnonReadOnly,
)
from django_oapif.root import create_root_router


class BasicAuth(HttpBasicAuth):
def authenticate(self, request, username, password):
if user := authenticate(request, username=username, password=password):
request.user = user
return request.user


class DjangoAuth(APIKeyCookie):
param_name: str = settings.SESSION_COOKIE_NAME

def authenticate(self, request: HttpRequest, key: Optional[str]) -> Optional[Any]:
return request.user


class OAPIF:
"""Ninja API."""

def __init__(self):
self.api = NinjaAPI(auth=[BasicAuth(), DjangoAuth()])
self.collections: dict[str, OAPIFCollectionEntry] = {}
self.api.add_router("/", create_root_router())
self.api.add_router("/conformance", create_conformance_router())
self.api.add_router("/collections", create_collections_router(self.collections))

def register(
self,
/,
id: str | None = None,
title: str | None = None,
description: str | None = None,
geometry_field: str | None = "geom",
properties_fields: list[str] | None = None,
auth: type[BasePermission] = DjangoModelPermissionsOrAnonReadOnly,
):
"""Register a Django model in the API."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you document the arguments? that would answet these basic questions
can we use None for geometry for geometry less layers?
if we give None to the fields, are they all shown?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

geometry_field can be None yes, just updated the hint. If property_fields is None, they will all be included by default. Will add documentation for all this.


def decorator(model_class: models.Model):
collection_id = id or model_class._meta.label_lower
collection_title = id or model_class._meta.label
self.collections[collection_id] = OAPIFCollectionEntry(
model_class=model_class,
id=collection_id,
title=collection_title,
description=description,
geometry_field=geometry_field,
properties_fields=properties_fields,
auth=auth,
)

return model_class

return decorator

def _add_collections_routers(self):
for collection_id, collection_entry in self.collections.items():
self.api.add_router(f"/collections/{collection_id}", create_collection_router(collection_entry))

@property
def urls(self):
self._add_collections_routers()
return self.api.urls
Loading
Loading