I'm create the REST API for space conjunction report, I want the conjunction to be a child of each report.
My models:
from django.db import models
from django.utils import timezone
class Report(models.Model):
class Meta:
managed = False
db_table = 'report'
ordering = ['-id']
predict_start = models.DateTimeField(null=True)
predict_end = models.DateTimeField(null=True)
process_duration = models.IntegerField(default=0, null=True)
create_conjunction_date = models.DateTimeField(null=True)
ephe_filename = models.CharField(max_length=100, null=True)
class Conjunction(models.Model):
class Meta:
managed = False
db_table = 'conjunction'
ordering = ['-conjunction_id']
conjunction_id = models.IntegerField(primary_key=True)
tca = models.DateTimeField(max_length=3, null=True)
missdt = models.FloatField(null=True)
probability = models.FloatField(null=True)
prob_method = models.CharField(max_length=45, null=True)
norad = models.OneToOneField(SatelliteCategory, to_field='norad_cat_id', db_column='norad', null=True, on_delete=models.DO_NOTHING)
doy = models.FloatField(null=True)
ephe_id = models.IntegerField(null=True)
pri_obj = models.IntegerField(null=True)
sec_obj = models.IntegerField(null=True)
report = models.ForeignKey(Report, related_name='conjunctions', null=True, on_delete=models.DO_NOTHING)
probability_foster = models.FloatField(null=True)
probability_patera = models.FloatField(null=True)
probability_alfano = models.FloatField(null=True)
probability_chan = models.FloatField(null=True)
My serializers:
class ConjunctionSerializer(serializers.ModelSerializer):
class Meta:
model = Conjunction
fields = '__all__'
class ReportSerializer(serializers.ModelSerializer):
conjunctions = relations.ResourceRelatedField(many=True, read_only=True)
class Meta:
model = Report
fields = '__all__'
My views:
from rest_framework import permissions
from rest_framework_json_api.views import viewsets
from .serializers import ReportSerializer, ConjunctionSerializer
from .models import Report, Conjunction
class ReportViewSet(viewsets.ModelViewSet):
queryset = Report.objects.all()
serializer_class = ReportSerializer
permission_classes = [permissions.AllowAny]
class ConjunctionViewSet(viewsets.ModelViewSet):
queryset = Conjunction.objects.all()
serializer_class = ConjunctionSerializer
permission_classes = [permissions.AllowAny]
My urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework import routers
from api.views import UserViewSet, GroupViewSet
from shared.views import ReportViewSet, SatelliteCategoryViewSet, ConjunctionViewSet, ReportSentViewSet
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
router.register(r'reports', ReportViewSet)
router.register(r'report_sent', ReportSentViewSet)
router.register(r'satellite_category', SatelliteCategoryViewSet)
router.register(r'conjunctions', ConjunctionViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
When I'm use ResourceRelatedField
the JSON output will be like:
{
"links": {
"first": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=1",
"last": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=84",
"next": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=2",
"prev": null
},
"data": [
{
"type": "Report",
"id": "838",
"attributes": {
"predict_start": "2023-01-26T12:00:00Z",
"predict_end": "2023-02-02T12:00:00Z",
"process_duration": 752,
"create_conjunction_date": "2023-01-26T14:52:45Z",
"ephe_filename": "Filename.txt"
},
"relationships": {
"conjunctions": {
"meta": {
"count": 107
},
"data": [
{
"type": "Conjunction",
"id": "78728"
},
# ... more data ...
{
"type": "Conjunction",
"id": "78622"
}
]
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pages": 84,
"count": 838
}
}
}
But when I'm use HyperlinkedRelatedField
, it gives the empty object of conjunctions
:
{
"links": {
"first": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=1",
"last": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=84",
"next": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=2",
"prev": null
},
"data": [
{
"type": "Report",
"id": "838",
"attributes": {
"predict_start": "2023-01-26T12:00:00Z",
"predict_end": "2023-02-02T12:00:00Z",
"process_duration": 752,
"create_conjunction_date": "2023-01-26T14:52:45Z",
"ephe_filename": "Filename.txt"
},
"relationships": {
"conjunctions": {}
}
},
],
"meta": {
"pagination": {
"page": 1,
"pages": 84,
"count": 838
}
}
}
This is what I'm expect:
{
"links": {
"first": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=1",
"last": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=84",
"next": "http://127.0.0.1:8000/api/reports/?page%5Bnumber%5D=2",
"prev": null
},
"data": [
{
"type": "Report",
"id": "838",
"attributes": {
"predict_start": "2023-01-26T12:00:00Z",
"predict_end": "2023-02-02T12:00:00Z",
"process_duration": 752,
"create_conjunction_date": "2023-01-26T14:52:45Z",
"ephe_filename": "Filename.txt"
},
"relationships": {
"conjunctions": {
# Any links or something.
"data": [{
"type": "Conjunction",
"id": "1",
},{
"type": "Conjunction",
"id": "2",
}],
"links": {
"self": "http://localhost:8000/api/reports/838/relationships/conjunctions/",
"related": "http://localhost:8000/api/reports/838/conjunctions/"
}
}
}
},
],
"meta": {
"pagination": {
"page": 1,
"pages": 84,
"count": 838
}
}
}
I can solve the problem now.
From now, I will using drf-nested-routers
to resolve conjunctions related to report links.
You can install by
pip install drf-nested-routers
It is not needed to add this library in your Django project's settings.py file, as it does not contain any app, signal or model.
In urls.py, add the following routers
from django.urls import path, include
from rest_framework_nested import routers
from api.views import UserViewSet, GroupViewSet
from shared.views import ReportViewSet, SatelliteCategoryViewSet, ConjunctionViewSet, ReportSentViewSet, SatelliteViewSet, DiscosObjectViewSet, CoordinateViewSet, ReportRelationshipView
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
router.register(r'reports', ReportViewSet)
router.register(r'report_sent', ReportSentViewSet)
router.register(r'satellite_categories', SatelliteCategoryViewSet)
router.register(r'conjunctions', ConjunctionViewSet)
router.register(r'satellites', SatelliteViewSet)
router.register(r'discos_objects', DiscosObjectViewSet)
router.register(r'coordinates', CoordinateViewSet)
reports_router = routers.NestedSimpleRouter(router, 'reports', lookup='report')
reports_router.register(r'conjunctions', ConjunctionViewSet, basename='report-conjunctions')
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
] + [
path('api/', include(router.urls)) for router in [router, reports_router]
]
In serializers.py, add HyperlinkedRelatedField
into ReportSerializer
class.
class ReportSerializer(serializers.HyperlinkedModelSerializer):
conjunctions = relations.HyperlinkedRelatedField(
many=True, read_only=True,
related_link_view_name='report-conjunctions-list',
related_link_url_kwarg='report_pk'
)
class Meta:
model = Report
fields = '__all__'
In views.py, from ConjunctionViewSet
class, override get_queryset
method to filter any conjunctions that are related with report.
class ConjunctionViewSet(viewsets.ModelViewSet):
queryset = Conjunction.objects.all()
serializer_class = ConjunctionSerializer
permission_classes = [permissions.AllowAny]
# Add code below
# | | | | | | |
# v v v v v v v
def get_queryset(self):
queryset = super().get_queryset()
report_pk = self.kwargs.get('report_pk')
if report_pk is not None:
queryset = queryset.filter(report__pk=report_pk)
return queryset
The given JSON output result
{
"data": {
"type": "Report",
"id": "838",
"attributes": {
"predict_start": "2023-01-26T12:00:00Z",
"predict_end": "2023-02-02T12:00:00Z",
"process_duration": 752,
"create_conjunction_date": "2023-01-26T14:52:45Z",
"ephe_filename": "Filename.txt"
},
"relationships": {
"conjunctions": {
"links": {
"related": "http://127.0.0.1:8000/api/reports/838/conjunctions/"
}
}
},
"links": {
"self": "http://127.0.0.1:8000/api/reports/838/"
}
}
}