The challenge of this question is clear as of update 2 which is the current state of the question, you can read it in its entirety to understand its context if you wish
:)
I have the following model that I want to serialize to expose it via REST
class RehabilitationSession(models.Model):
patient = models.ForeignKey('userprofiles.PatientProfile', null=True, blank=True,verbose_name='Paciente', related_name='patientprofile')
slug = models.SlugField(max_length=100, blank=True)
medical = models.ForeignKey('userprofiles.MedicalProfile', null=True, blank=True,
verbose_name='Médico tratante')
therapist = models.ForeignKey('userprofiles.TherapistProfile', null=True, blank=True, verbose_name='Terapeuta')
date_session_begin = models.DateTimeField(default=timezone.now(), verbose_name = 'Fecha de inicio')
upper_extremity = MultiSelectField(
max_length=255,
choices=EXTREMITY_CHOICES,
blank=False,
verbose_name='Extremidad Superior'
)
affected_segment = models.ManyToManyField(AffectedSegment,verbose_name='Segmento afectado')
movement = ChainedManyToManyField(
Movement, #Modelo encadenado
chained_field = 'affected_segment',
chained_model_field = 'corporal_segment_associated',
verbose_name='Movimiento'
)
metrics = models.ManyToManyField(Metric, blank=True, verbose_name='Métrica')
date_session_end = models.DateTimeField(default=timezone.now(), verbose_name = 'Fecha de finalización')
period = models.CharField(max_length=25,blank=True, verbose_name='Tiempo de duración de la sesión')
class Meta:
verbose_name = 'Sesiones de Rehabilitación'
def __str__(self):
return "%s" % self.patient
To serialize the fields that are Foreingkey, I am relying on this REST Framework documentation .
My serializers.py file is this:
from .models import RehabilitationSession
from rest_framework import serializers
class RehabilitationSessionSerializer(serializers.HyperlinkedModelSerializer):
patient = serializers.HyperlinkedIdentityField(view_name='patientprofile',)
class Meta:
model = RehabilitationSession
fields = ('url','id','patient',
'date_session_begin','status','upper_extremity',
'date_session_end', 'period','games','game_levels',
'iterations','observations',)
I am using HyperlinkedIdentityField because my model is serialized with HyperlinkedModelSerializer, but it is not clear to me how I should serialize it according to the options given there. It is said that HyperlinkedIdentityField
it can also be used on an attribute of the object, and the idea is that in the view of my api when the model is serialized, it shows me the url of that field, which is an instance of the PatientProfile model, that is, a record of a patient.
UPDATE
My main file urls.py
where I include the routes to determine the urls is:
from django.conf.urls import url, include #patterns
from django.contrib import admin
from .views import home, home_files
# REST Framework packages
from rest_framework import routers
from userprofiles.views import UserViewSet, GroupViewSet, PatientProfileViewSet
from medical_encounter_information.views import RehabilitationSessionViewSet
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
router.register(r'rehabilitation-session', RehabilitationSessionViewSet)
router.register(r'patientprofile', PatientProfileViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^chaining/', include('smart_selects.urls')),
url(r'^$', home, name='home'),
url(r'^', include('userprofiles.urls')),
#Call the userprofiles/urls.py
url(r'^', include('medical_encounter_information.urls' )),
#Call the medical_encounter_information/urls.py
# which is a regular expression that takes the desired urls and passes as an argument
# the filename, i.e. robots.txt or humans.txt.
url(r'^(?P<filename>(robots.txt)|(humans.txt))$',
home_files, name='home-files'),
#REST Frameworks url's
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
url(r'^api/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
When I try to access the url of my rest api of said model, I get the following message in my console:
File "/home/bgarcial/.virtualenvs/neurorehabilitation_projects_dev/lib/python3.4/site-packages/rest_framework/relations.py", line 355, in to_representation
raise ImproperlyConfigured(msg % self.view_name)
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "patientprofile". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
[08/Mar/2016 16:05:45] "GET /api/rehabilitation-session/ HTTP/1.1" 500 165647
And in my browser this:
How can I serialize a ForeignKey field? I appreciate your help.
UPDATE 2
I wanted to try using nested relationships as suggested in this post, since it would also be good to have in the serialization of my model, in this case rehabilitation session, the fields of the patient of that session, the doctor of that session and the therapist of that session and not their respective urls that take me to their data, for usability purposes and I imagine for reading some third-party application, although I also imagine that the values and keys can also be accessed through the url of the json of each url, right?
Well, right now, I have them like this:
I would like to be able to organize my data as follows:
{
"url": "http://localhost:8000/api/rehabilitation-session/9/",
"id": 9,
"patient": [
{'id' : 1, 'full_name' : 'Andres Enfermizo',
'diagnostic' : 'Diabetes', 'time_of_evolution' : '3 months'
},
"medical": [
{'id' : 1, 'full_name' : 'Doctor House',
'specialty' : 'Epidemiologist', 'xperience' : '23 years'
} ,
]
"therapist": [
{'id' : 1, 'full_name' : 'Christian',
'specialty' : 'Legs', 'xperience' : '13 years'
} ,
]
"affected_segment": [
{'id' : 1, 'affected_segment' : 'shoulder',
'damage' : '30%', 'time_of_retrieve' : '10 months'
} ,
],
}
So according to the documentation, would Nested Relationships or even Custom relational fields work ?
I have done it following the guide for Nested Relationships in this way, for now only for the case of displaying the patient's data in a nested way in the serialization of the rehabilitation session:
Serializing the model PatientProfile
:
class PatientProfileSerializer(serializers.ModelSerializer):
class Meta:
model = PatientProfile
fields = ('url','id','user','full_name','time_of_evolution','diagnostic','marital_status','educational_level','partner_full_name','partner_relationship','partner_phone','partner_gender',
'care_provider',)
The view PatientProfileViewset
that will expose the serialized model
class PatientProfileViewSet(viewsets.ModelViewSet):
queryset = PatientProfile.objects.all()
serializer_class = PatientProfileSerializer
Now I serialize my model RehabilitationSession
:
class RehabilitationSessionSerializer(serializers.ModelSerializer):
patient = PatientProfileSerializer(many=True, read_only=True)
class Meta:
model = RehabilitationSession
fields = ('url','id','patient',
#'affected_segment',
'date_session_begin','status','upper_extremity',
'date_session_end', 'period','games','game_levels',
'iterations','observations',)
The view RehabilitationSessionViewSet
that will expose the serialized model
class RehabilitationSessionViewSet(viewsets.ModelViewSet):
queryset = RehabilitationSession.objects.all()
serializer_class = RehabilitationSessionSerializer
And these are my router's for each serialized model to access them by url in the browser. They are in my main urls.py:
from django.conf.urls import url, include #patterns
from django.contrib import admin
from .views import home, home_files
# REST Framework packages
from rest_framework import routers
from userprofiles.views import (UserViewSet, GroupViewSet, PatientProfileViewSet,)
from medical_encounter_information.views import (RehabilitationSessionViewSet,)
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
router.register(r'rehabilitation-session', RehabilitationSessionViewSet)
router.register(r'patientprofile', PatientProfileViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
...
url(r'^api/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
I get this message when entering the url http://localhost:8000/api/rehabilitation-session/
Any guidance will be highly appreciated. :)
Finally, in the case of obtaining the data in my response nested in the JSON's document(s) that my api generates with
django-rest-framework
, I have opted for the following solution:I return from the beginning to give a description of what has been done, so I will start by showing what my originally defined models are, how I serialized them, how the views or endpoints are generated and finally how the routes (
router's
) are established to expose them typeURLConf
These are my defined models
1.
PatientProfile
model, defineduserprofiles/models.py
2.model
MedicalProfile
defined inuserprofiles/models.py
3.
TherapistProfile
defined inuserprofiles/models.py
serializing
I have the serialization of these models in this way:
userprofiles/serializers.py
The views that expose like
endpoints
the serialized models above are given inuserprofiles/views.py
this form:I present the model
RehabilitationSessions
located in medical_encounter_information/models.py which has three foreign keys to the previous user profilesPatientProfile
,MedicalProfile
andTherapistProfile
The serialization of the fields of the class
RehabilitationSession
is given by the classRehabilitationSessionSerializer
located inmedical_encounter_information/serializers.py
This is where I had my doubts about how to perform serialization and for which I was asking a bit about how relationships are handled in the Django REST Framework .
I found in the documentation how to work serialization in a nested way without the need to resort to extra classes of the Serializer Relationships type .
The key is to add a field called
depth
which recursively allows n levels of depth (I don't really know up to what number of levels) to be able to serialize the JSON subdocuments that we have, in this case the ones that eachForeignKey
of each user profile brought needed in a rehabilitation session (PatientProfile
,MedicalProfile
,TherapistProfile
)Here it says the following:
So my class
RehabilitationSessionSerializer
looks like this:The view
ViewSet
that exposes this serialization is given in this waymedical_encounter_information/views.py
Mis rutas/routers en urls.py para el acceso a los modelos expuestos via rest framework son:
Es entonces que cuando consulto este API en mi navegador, el modelo
RehabilitationSession
que posee las tres claves foráneas a los modelosPatientProfile
,MedicalProfile
yTherapistProfile
; al ser serializado me queda desplegado de la siguiente manera con los documentos de los respectivos perfiles de usuario mencionados, anidados:Es así como pude tener una serialización de varios modelos en una misma vista por asi decirlo.
POr supuesto existen varias clases como las de Serialization relationships que permiten manejar las relaciones entre modelos en django rest framework acorde a lo que necesitemos, si es una url, si es un string, si es un campo de tipo slug o si es un campo tipo PrimaryKey o si es anidado como lo que necesitaba
I also found this example of nested or recursive serialization for fields, but this time it is the same model, interesting and very necessary, for a very common example on the web, which is comments and responses to those same comments.
I also want to thank you very much for the ideas and guidance, they were useful for me to find my solution :D
Use Nested Relations
For example:
where tracks from the Album model is a foreign key to the Track model .
Be careful with nested relationships, they are a performance kill for your API, I talk about it in this post: http://miguelgomez.io/python/optimizador-django-rest-framework/
On the other hand, I would use the class
MethodSerializers
to make the serializers as custom as possible.All the best.