Related
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/"
}
}
}
Iam using Django with Restframework.
Iam trying to get a json output from serializers in a nested manner with key as one of the fields from model. I acheived the nested JSON but the json key for them is troubling me.
Here is my code and expected results out of it:
Models.py
class Tags(models.Model):
tagId = models.CharField(primary_key=True, max_length=100, default=1)
section = models.CharField(max_length=100,default=1)
def __str__(self):
return self.section
class TagItem(models.Model):
section= models.ForeignKey(Tags, on_delete=models.CASCADE,default=1,related_name="items")
select = models.BooleanField(default=False)
name = models.CharField(max_length=50)
def __str__(self):
return self.name
serializers.py
class TagItemModelSerializer(serializers.ModelSerializer):
class Meta:
model = TagItem
fields = '__all__'
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
class Meta:
model = Tags
fields = ['pk','section', 'items']
Expected output:
{
"crafts" : { //craft is comming from Tag model's "section"
"id": 1,
"section": "crafts",
"items": [
{
"id": "10",
"select": false,
"name": "Wood",
"category": "crafts"
},
]
},
"states" : {
"id": 2,
"section": "states",
"items": [
{
"id": "20",
"select": false,
"name": "Andhra Pradesh",
"category": "states"
}
]
},
"others" : {
"id": 3,
"section": "others",
"items": [
{
"id": "30",
"select": false,
"name": "Volunteer",
"category": "others"
}
]
}
}
Current output:
[ //cant get the key of Tag model's "section"
{
"pk": "1",
"section": "states",
"items": [
{
"id": 6,
"select": false,
"name": "Assam",
"section": "1"
}
]
},
{
"pk": "2",
"section": "others",
"items": [
{
"id": 12,
"select": false,
"name": "Artisan",
"section": "2"
}
]
},
{
"pk": "3",
"section": "crafts",
"items": [
{
"id": 9,
"select": false,
"name": "Metal",
"section": "3"
}
]
}
]
At your case, you would like to have a custom representation I think?
You can adjust it with overriding to_representation()
I provide two approaches:
serializer -> the better way but not quite the output you prefer
view
class TagModelView(views.APIView):
def get(self, request):
qs = Tags.objects.all()
serializer = TagModelSerializer(qs, many=True)
return Response(data=serializer.data)
serializer
class TagItemModelSerializer(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
class Meta:
model = TagItem
fields = ['id', 'select', 'name', 'category']
def get_category(self, instance):
return instance.section.section
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
class Meta:
model = Tags
fields = ['pk', 'section', 'items']
def to_representation(self, instance):
container = super().to_representation(instance)
return {container.get('section'): container}
Making representation in your view -> output you would like to get
view
def get(self, request):
qs = Tags.objects.all()
serializer = TagModelSerializer(qs, many=True)
container = dict()
for element in serializer.data:
container.update(**element)
return Response(data=container)
serializer
No change to my proposal obove.
update
I saw that your primary key of Tags is tagId and you are using fields = ['pk', 'section', 'items'] but you would like to have id as a key-name.
Relabeling can help you.
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
id = serializers.CharField(source='tagId')
class Meta:
model = Tags
fields = ['id', 'section', 'items']
I want to create a seralizer with 3 models and many to many relationship:
# models.py
class Permission(models.Model):
permission_name = models.CharField(max_length=20)
class Feature(models.Model):
feature_name = models.CharField(max_length=40)
class Role(models.Model):
name = models.CharField(max_length=40)
permissions = models.ManyToManyField(Permission, through='RolePermission')
features = models.ManyToManyField(Feature, through='RolePermission')
class RolePermission(models.Model):
role = models.ForeignKey(Role, on_delete=models.CASCADE)
permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
feature = models.ForeignKey(Feature, on_delete=models.CASCADE)
Sample data:
PERMISSION:
1 - add
2 - edit
3 - remove
4 - view
FEATURE:
1 - user
2 - device
ROLE:
1 - ROLE1
2 - ROLE2
data i want to response from view:
[
{
"id": 1,
"name": "ROLE1",
"features": [
{
"id": 1,
"name": "user",
"permissions": [
{
"id": 1,
"name": "add"
},
{
"id": 2,
"name": "edit"
},
{
"id": 3,
"name": "remove"
},
{
"id": 4,
"name": "view"
}
]
},
{
"id": 2,
"name": "device",
"permissions": [
{
"id": 4,
"name": "view"
}
]
}
]
},
{
"id": 2,
"name": "ROLE2",
"features": [
{
"id": 1,
"name": "user",
"permissions": [
{
"id": 4,
"name": "view"
}
]
},
{
"id": 2,
"name": "device",
"permissions": [
{
"id": 4,
"name": "view"
}
]
}
]
}
]
But i cant write the serializer correctly?
Any help is appreciated.
I guess you should use this code, maybe there is different field_names
class PermisionSerializer(serializers.ModelSerializer):
class Meta:
model = Permission
fields = ('name', )
class FeatureSerializer(serializers.ModelSerializer):
permissions = PermisionSerializer(many=True)
class Meta:
model = Feature
fields = ('name', 'permissions', )
class RoleSerializer(serializers.ModelSerializer):
features = FeatureSerializer(many=True)
class Meta:
model = Role
fields = ('name', 'features', )
I am new to Django rest framework.i am trying to get child model records to the parent model as a field so that all the RefreshmentImage models records are available in games_sports list.i have posted sample code.
model.py
class Refreshment(models.Model):
title = models.CharField(max_length=200, unique=True)
type = models.CharField(max_length=200)
charges = models.DecimalField(max_digits=12, decimal_places=2, help_text="Charges per hour")
class RefreshmentImage(models.Model):
refreshment = models.ForeignKey(Refreshment, on_delete=models.CASCADE)
image = models.FileField(upload_to="refreshment_image/", null=True, blank=True)
serializers.py
class EntertainmentSerializer(serializers.ModelSerializer):
class Meta:
model = Refreshment
fields = '__all__'
class RefreshmentImageSerializer(serializers.ModelSerializer):
refreshment = EntertainmentSerializer(read_only=True, many=True)
class Meta:
model = RefreshmentImage
fields = '__all__'
views.py
def all_games_sports(request):
entertainment = Refreshment.objects.all()
serialize = EntertainmentSerializer(instance=entertainment,many=True)
serial = RefreshmentImageSerializer(instance=entertainment,many=True)
main = {'status': True, 'code': CODE_SUCCESSFUL, 'msg': SUCCESS, 'games_sports': serialize.data,'image':serial.data}
return HttpResponse(json.dumps(main), content_type='application/json')
what i got is like:
games_sports": [
{
"id": 1,
"title": "yyy",
"type": 1,
"charges": "500.00",
},
{
"id": 2,
"title": "xxxxx",
"type": "something",
"charges": "501.00",
}
*******
],
"image": [
{
"id": 1,
"image": null,
"date_created": "2019-03-03T08:16:15.538024+05:30"
},
**********
]
i want it to be:
games_sports": [
{
"id": 1,
"title": "yyy",
"type": 1,
"charges": "500.00",
"image": [
{
"id": 1,
"image": image_path,
"date_created": "2019-03-03T08:16:15.538024+05:30"
},
}
***********
],
Try this snippet
#serializers.py
"""I've re-arranged the order of 'RefreshmentImageSerializer' serializer and 'EntertainmentSerializer' serializer"""
class RefreshmentImageSerializer(serializers.ModelSerializer):
class Meta:
model = RefreshmentImage
fields = '__all__'
class EntertainmentSerializer(serializers.ModelSerializer):
image = RefreshmentImageSerializer(many=True, source='refreshmentimage_set')
class Meta:
model = Refreshment
fields = '__all__'
# views.py
"""Added DRF stuffs such as 'api_view' and 'Response'"""
from rest_framework.decorators import api_view
#api_view()
def all_games_sports(request):
entertainment = Refreshment.objects.all()
serialize = EntertainmentSerializer(instance=entertainment, many=True)
main = {'status': True, 'code': "CODE_SUCCESSFUL", 'msg': "SUCCESS", 'games_sports': serialize.data}
return Response(main)
{
"status": true,
"code": "CODE_SUCCESSFUL",
"msg": "SUCCESS",
"games_sports": [
{
"id": 1,
"image": [
{
"id": 1,
"image": null,
"refreshment": 1
},
{
"id": 3,
"image": "refreshment_image/jpg-icon.png",
"refreshment": 1
}
],
"title": "t1",
"type": "tt1",
"charges": "123.00"
},
{
"id": 2,
"image": [
{
"id": 2,
"image": "refreshment_image/asd.jpg",
"refreshment": 2
}
],
"title": "t2",
"type": "tt2",
"charges": "321.00"
},
{
"id": 3,
"image": [
{
"id": 4,
"image": "refreshment_image/Screenshot_from_2018-10-26_16-32-41.png",
"refreshment": 3
},
{
"id": 5,
"image": "refreshment_image/twitter.png",
"refreshment": 3
}
],
"title": "t3",
"type": "tt3",
"charges": "754.00"
}
]
}
What I've done here?
re-arranged the order of serializer to avoid not defined errors
added a new field in EntertainmentSerializer class to show the images associated with the Refreshment object
In views.py I've added DRF pieces of stuff, which is more suitable
References
#api_view() decorator
DRF's Response() class
DRF Nested Serializers
The source keyword argument
Hope this helps!!
I have model Product:
class Product(TranslatableModel):
name = models.CharField(max_length=255, unique=True)
translations = TranslatedFields(
description=models.TextField(),
)
and in administration on product detail I have tabs with languages. For example tabs EN, CZ, each includes disctiption. So PUT request looks like:
{
'product': '1',
'id': 1,
'name': 'Name',
'translations': {
'cz': {'desctiption': 'Description cz'},
'en': {'desctiption': 'Description en'}
}
}
I founded in django-hvad TranslationsMixin that allows me to do that request.
in serializers I have:
class ProductTranslationSerializer(serializers.ModelSerializer):
class Meta:
exclude = ['description']
class ProductSerializer(TranslationsMixin, serializers.ModelSerializer):
class Meta:
model = Product
translations_serializer = ProductTranslationSerializer
fields = (
'name',
'description',
)
Question is how will looks ModelViewSet for this request? Can I choose language like 'language_code=en', filter that query and get something like:
[
{
id: 1
name: "name"
descritpion: "descritpion"
},
....
]
Thank you!
the most likely way i achieve is:
models.py
class Product(TranslatableModel):
category = models.ForeignKey('product.ProductCategory',
related_name='product_category',
null=True,
on_delete=models.SET_NULL,
verbose_name=u'category')
cover = models.ImageField(upload_to=product_cover,
null=True,
verbose_name=u'cover')
translations = TranslatedFields(
title=models.CharField(max_length=100,
null=True,
verbose_name=u'title'),
summary=models.TextField(null=True,
verbose_name=u'summary'),
model=models.CharField(max_length=255,
null=True,
blank=True,
verbose_name=u'model'),
price=models.DecimalField(default=0.00,
max_digits=10,
decimal_places=2,
blank=True,
validators=[MinValueValidator(0)],
verbose_name=u'price'),
content=models.TextField(verbose_name=u'content'),
publish_time=models.DateTimeField(default=timezone.now,
verbose_name=u'发布publish_time')
)
view_times = models.IntegerField(default=0,
verbose_name=u'view_times ')
views.py
class ProductViewSet(ModelViewSet):
serializer_class = ProductListSerializer
def get_queryset(self):
if 'language_code' in self.request.GET:
language_code = self.request.GET.get('language_code')
queryset = Product.objects.language(language_code).order_by('-id')
else:
queryset = Product.objects.language().order_by('-id')
return queryset
serializers.py
class ProductCategorySerializer(TranslatableModelSerializer):
class Meta:
model = ProductCategory
fields = '__all__'
class ProductListSerializer(TranslatableModelSerializer):
category = ProductCategorySerializer(read_only=True)
class Meta:
model = Product
exclude = ['is_abandon', 'content', ]
urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'product', ProductViewSet, base_name='api-product')
...
result:
http://192.168.1.108/api/product/?language_code=zh-hans you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 08:56:28",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "标题",
"summary": "简介",
"model": null,
"price": "90.00",
"publish_time": "2017-08-18 08:53:00",
"language_code": "zh-hans"
}
]
}
http://192.168.1.108/api/product/?language_code=en you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
this way fk will not change language,if you want fk change language too,use:
urls.py
urlpatterns += i18n_patterns(
'''
url(r'^api/', include(router.urls)),
prefix_default_language=False,
)
change language by
http://192.168.1.108/zh-hans/api/product/
to
http://192.168.1.108/en/api/product/
your will get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "test",
"language_code": "en"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
Answer from owner of django-hvad github repo #spectras:
If I understand well, you want to use a different serialized form for the collection endpoint and the item endpoint. That is, GET-1, PUT, POST would use the translations mixin, while GET-many would not.
You need two have two different serializers for this:
The one you created
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
Given that, you can add to your ModelViewSet a method that dynamically chooses the serializer class depending on request type:
class SomeModelViewSet(ModelViewSet):
# serializer_class = not needed, we use the method instead
def get_serializer_class(self):
if self.action == 'list':
return ProductItemSerializer
return ProductSerializer
An alternate method, perhaps easier to use on the Javascript side, would be to make 2 viewsets for the Product model, a read-write one being translation aware (using ProductSerializer) and a read-only one not translation aware, using (ProductItemSerializer).
In the same vein, I usually only have translation-unaware serializers and add a /api/product/details/ endpoint that is translation-aware. This way, I only have to handle the complexity of a dict object when going into edit/detailed mode on the client side.