英文:
Django REST Framework JSON API show an empty object of relationships link when use relations.HyperlinkedRelatedField from rest_framework_json_api
问题
I'm create the REST API for space conjunction report, I want the conjunction to be a child of each report.
我的目标是创建用于太空相遇报告的REST API,我希望每个报告都有相遇信息作为子项。
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)
我的数据模型:
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__'
我的序列化器:
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]
我的视图:
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
我的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'))
]
我的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:
当我使用ResourceRelatedField
时,JSON输出将如下所示:
{
"links": {
"first": "http://127.0.0
<details>
<summary>英文:</summary>
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
}
}
}
</details>
# 答案1
**得分**: 0
I can solve the problem now.
From now, I will be using `drf-nested-routers` to resolve conjunctions related to report links.
You can install by
```python
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 to the 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/"
}
}
}
英文:
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/"
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论