Django REST Framework JSON API show an empty object of relationships link when use relations.HyperlinkedRelatedField from rest_framework_json_api

huangapple go评论69阅读模式
英文:

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&#39;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&#39;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&#39;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&#39;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&#39;users&#39;, UserViewSet)
router.register(r&#39;groups&#39;, GroupViewSet)
router.register(r&#39;reports&#39;, ReportViewSet)
router.register(r&#39;report_sent&#39;, ReportSentViewSet)
router.register(r&#39;satellite_categories&#39;, SatelliteCategoryViewSet)
router.register(r&#39;conjunctions&#39;, ConjunctionViewSet)
router.register(r&#39;satellites&#39;, SatelliteViewSet)
router.register(r&#39;discos_objects&#39;, DiscosObjectViewSet)
router.register(r&#39;coordinates&#39;, CoordinateViewSet)

reports_router = routers.NestedSimpleRouter(router, &#39;reports&#39;, lookup=&#39;report&#39;)
reports_router.register(r&#39;conjunctions&#39;, ConjunctionViewSet, basename=&#39;report-conjunctions&#39;)

urlpatterns = [
  path(&#39;admin/&#39;, admin.site.urls),
  path(&#39;api-auth/&#39;, include(&#39;rest_framework.urls&#39;, namespace=&#39;rest_framework&#39;))
] + [
  path(&#39;api/&#39;, 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=&#39;report-conjunctions-list&#39;,
    related_link_url_kwarg=&#39;report_pk&#39;
  )

  class Meta:
    model = Report
    fields = &#39;__all__&#39;

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(&#39;report_pk&#39;)
    if report_pk is not None:
      queryset = queryset.filter(report__pk=report_pk)

    return queryset

The given JSON output result

{
    &quot;data&quot;: {
        &quot;type&quot;: &quot;Report&quot;,
        &quot;id&quot;: &quot;838&quot;,
        &quot;attributes&quot;: {
            &quot;predict_start&quot;: &quot;2023-01-26T12:00:00Z&quot;,
            &quot;predict_end&quot;: &quot;2023-02-02T12:00:00Z&quot;,
            &quot;process_duration&quot;: 752,
            &quot;create_conjunction_date&quot;: &quot;2023-01-26T14:52:45Z&quot;,
            &quot;ephe_filename&quot;: &quot;Filename.txt&quot;
        },
        &quot;relationships&quot;: {
            &quot;conjunctions&quot;: {
                &quot;links&quot;: {
                    &quot;related&quot;: &quot;http://127.0.0.1:8000/api/reports/838/conjunctions/&quot;
                }
            }
        },
        &quot;links&quot;: {
            &quot;self&quot;: &quot;http://127.0.0.1:8000/api/reports/838/&quot;
        }
    }
}

huangapple
  • 本文由 发表于 2023年2月6日 17:34:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定