Django REST Framework Serializing ForeignKey and ManyToManyFields - python

I have the following model, which I want serialize for expose 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 which are Foreign Key I am reading this documentation
My serializers.py 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, due to my model is serialized with HyperlinkedModelSerializer but, I don't have clear or I still ignore how to should I serialize when one field is ForeignKey and ManyToManyField
My urls.py main file in which I include the route's for setup the api url's 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 access to url of my api rest, I get the following message in cli:
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 I get this:
How to can I serialize a ForeignKey and ManyToManyField which happened to me the same situation?
Best Regards

Try changing your serializer to
class RehabilitationSessionSerializer(serializers.HyperlinkedModelSerializer):
patient = serializers.HyperlinkedIdentityField(view_name='patientprofile-detail',)
class Meta:
...
The router automatically creates a detail view with this name for a ViewSet. See docs for parameter view_name:
view_name - The view name that should be used as the target of the relationship. If you're using the standard router classes this will be a string with the format <model_name>-detail.

Related

Could not resolve URL for hyperlinked relationship using view name "rest:campaign-detail"

A newbie here in the world of Django. I am struggling with creating a hyperlink for a nested route.
The error I am getting is:
Could not resolve URL for hyperlinked relationship using view name "rest:campaign-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
Some project setup notes
Using django rest framework
using DRF-extensions to create the routes
Using ModelViewSet
Expected end points:
/accounts/
/accounts/< pk >/
/accounts/< pk >/campaigns/
/accounts/< pk >/campaigns/< pk >/
/accounts/< pk >/campaigns/adgroup/
/accounts/< pk >/campaigns/adgroup/< pk >/
Set a namespace of rest in urls.py
Using HyperlinkedIdentityField to create the hyperlink. It only works with the parent object i.e.
url = serializers.HyperlinkedIdentityField(view_name='rest:account-detail')
However fails with any nested object i.e.
url = serializers.HyperlinkedIdentityField(view_name='rest:campaign-detail')
The model is quiet simple, an Account can have many Campaigns, and a campaign can have many AdGroups. See code below:
models.py
from django.db import models
from django.db.models import Q
from model_utils import Choices
ORDER_COLUMN_CHOICES = Choices(
('0', 'id'),
('1', 'keyword'),
('2', 'status'),
('3', 'match_type'),
)
# Account
class Account(models.Model):
account_name = models.CharField(max_length=128)
def __str__(self):
return self.account_name
# Campaign
class Campaign(models.Model):
class Status(models.TextChoices):
Enabled = "Enabled"
Paused = "Paused"
account = models.ForeignKey(
to=Account, on_delete=models.CASCADE, related_name='campaigns'
)
campaign_name = models.CharField(max_length=128)
status = models.CharField(max_length=21, choices=Status.choices, default=Status.Paused)
def __str__(self):
return self.campaign_name
# AdGroup
class AdGroup(models.Model):
class Status(models.TextChoices):
Enabled = "Enabled"
Paused = "Paused"
campaign = models.ForeignKey(
to=Campaign, on_delete=models.CASCADE, related_name='adgroups'
)
adgroup_name = models.CharField(max_length=128)
status = models.CharField(max_length=21, choices=Status.choices, default=Status.Enabled)
def __str__(self):
return self.adgroup_name
views.py
from rest_framework import viewsets
from .serializers import *
from . import models
from rest_framework_extensions.mixins import NestedViewSetMixin
class AccountViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
serializer_class = AccountSerializer
queryset = models.Account.objects.all()
class CampaignViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
serializer_class = CampaignSerializer
queryset = models.Campaign.objects.all()
class AdGroupViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
serializer_class = AdGroupSerializer
queryset = models.AdGroup.objects.all()
serializers.py
from rest_framework import serializers
from . import models
class CampaignSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="rest:campaign-detail")
class Meta:
model = models.Campaign
fields = '__all__'
class AccountSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='rest:account-detail')
class Meta:
model = models.Account
fields = '__all__'
class AdGroupSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='rest:adgroup-detail')
class Meta:
model = models.AdGroup
fields = '__all__'
I have 2 URL files. The project is named Vanilla and an app named rest where the DRF logic sits.
Vanilla urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('rest.urls', namespace='rest')),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('admin/', admin.site.urls)
]
Rest urls.py
from django.urls import include, path
from . import views
from rest_framework_extensions.routers import ExtendedSimpleRouter
app_name = 'rest'
router = ExtendedSimpleRouter()
(
router.register(r'accounts',
views.AccountViewSet,
basename='account')
.register(r'campaigns',
views.CampaignViewSet,
basename='campaign',
parents_query_lookups=['account__id'])
.register(r'adgroups',
views.AdGroupViewSet,
basename='adgroup',
parents_query_lookups=['campaign__account', 'campaign'])
Thank You!
use hyperlinkedModelSerializer in all of your related serializer. that should work

Keep getting "Could not resolve URL for hyperlinked relationship using view name "songs"

I have the following model class:
# Song Model
class Song(models.Model):
title = models.CharField(max_length=200)
artist = models.CharField(max_length=200)
content = models.TextField()
user = models.ForeignKey('auth.User', related_name='songs')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self): # __unicode__ on Python 2
return self.title + ' ' + self.artist
class Meta:
ordering = ('title',)
My serializers
class SongSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ('id', 'title', 'artist', 'content')
model = Song
class UserSerializer(serializers.ModelSerializer):
songs = serializers.HyperlinkedRelatedField(
many=True, read_only=True,
view_name='songs'
)
class Meta:
model = User
fields = '__all__'
and my views
class SongViewSet(viewsets.ModelViewSet):
queryset = Song.objects.all()
serializer_class = SongSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
I am trying to get the list of songs but I keep getting this error
ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "songs"
This has been working with PrimaryKeyRelatedField
but not as it is now.
routes file for reference:
router = DefaultRouter()
router.register(r'songs', views.SongViewSet)
router.register(r'users', views.UserViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(router.urls)),
]
Well, I was able to make it work with
view_name='song-detail'
If anyone wants to explain the why of how it works, please feel free
I'm total newbie in Django but I've faced with the same issue. I suppose that because of router prefix param is not the view name.
Documentation for the router says:
The example above would generate the following URL patterns:
URL pattern: ^users/$ Name: 'user-list'
URL pattern: ^users/{pk}/$ Name: 'user-detail'
URL pattern: ^accounts/$ Name: 'account-list'
URL pattern: ^accounts/{pk}/$ Name: 'account-detail'
Documentation for the serializer says:
view_name - The view name that should be used as the target of the relationship. If you're using the standard router classes this will be a string with the format
<modelname>-detail. required.
If you are using DefaultRouter
you can try change basename='song'
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('song', views.SongViewset, basename='song')

How to deal with URL arguments in Django REST Frameworks

I am new to Django REST Frameworks and despite doing the tutorial twice and reading lots of documentation I can't work out exactly how to handle/access the arguments in a URL in the DRF ViewSet.
My URL looks like this:
/api/v1/user/<user-id>/<age>/update
In my base urls.py I have the urlpattern:
url(r'^api/v1/', include('api.urls'))
In the api/urls.py I have:
from django.conf.urls import url, include
from rest_framework import routers
from api import views
router = routers.DefaultRouter()
router.register(r'user', views.UserViewSet)
urlpatterns = [
url(r'^', include(router.urls))
]
My question is what should my UserViewSet look like to handle the url, extract the user id and age fields, and update the UserDetails model so the given user has the given age?
My model hasn't been created yet but will look something like this:
class UserDetails(models.Model):
user = models.ForeignKey(User)
age = models.BigIntegerField(blank=True, null=True)
In the serializers.py adde the ParameterisedHyperlinkedIdentityField as a serializer.
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
url = ParameterisedHyperlinkedIdentityField(view_name='user-detail', lookup_fields=(('id', 'id'), ('age', 'age')), read_only=True)
class Meta:
model = UserDetails
urls.py
from .viewsets import UserViewSet
user_list = UserViewSet.as_view({'get':'list'})
user_detail = UserViewSet.as_view({'get':'retrieve'})
urlpatterns= [
url(r'^user/(?P<id>\d+)/(?P<age>[-\w\d]+)/update/$', user_detail, name='user-detail')
]
viewsets.py
class UserViewset(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = UserSerializer
queryset = UserDetails.objects.all()

Django Rest Framework: Implementing Many-to-Many relationship in Serializer

I'm using the Django Rest Framework to create a custom API for my Movies model which is defined as follows:
models.py
from django.db import models
class Genres(models.Model):
genre = models.CharField(max_length = 128, unique = True)
class Movies(models.Model):
popularity = models.FloatField()
director = models.CharField(max_length = 128)
genres = models.ManyToManyField(Genres, related_name = 'movies')
imdb_score = models.FloatField()
movie_name = models.CharField(max_length = 500)
Now, in my application, each genre can have multiple movie instances and vice versa. As a result, there is a Many-to-Many relationship between the Genres model and the Movies model. I want to allow the admin to chose multiple genres for a particular movie while posting new Movies instances at the API endpoint.
serializers.py
from shoppy.models import Movies, Genres
from rest_framework import serializers
class GenresSerializer(serializers.ModelSerializer):
class Meta:
model = Genres
class MoviesSerializer(serializers.ModelSerializer):
class Meta:
model = Movies
fields = ('popularity', 'director', 'imdb_score', 'genres',
'movie_name')
views.py
from shoppy.models import Movies
from rest_framework import viewsets
from shoppy.serializers import MoviesSerializer
class MovieViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Movies.objects.all()
serializer_class = MoviesSerializer
urls.py
from django.conf.urls import url, include
from rest_framework import routers
from shoppy import views
router = routers.DefaultRouter()
router.register(r'movies', views.MovieViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
However, I'm encountering the following error:
Exception Type: OperationalError at /movies/
Exception Value: no such table: shoppy_genres
What seems to be wrong with my code?

Django REST: How to use Router in application level urls.py?

My Django project structure:
mysite/
mysite/
...
urls.py
scradeweb/
...
models.py
serializers.py
views.py
urls.py
manage.py
If I use Django REST router in the project level urls.py (mysite/urls.py) like below, everything works fine:
# mysite/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from .settings import USER_CREATED_APPS
from rest_framework.routers import DefaultRouter
from scradeweb import views
router = DefaultRouter()
router.register(r'threads', views.ThreadViewSet, )
router.register(r'posts', views.PostViewSet)
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'scradeweb/', include('scradeweb.urls', namespace='scradeweb')),
url(r'^', include(router.urls)),
)
I like keeping all my application (scradeweb) related code within its
directory, so I move router to scradeweb/urls.py:
# scradeweb/urls.py
from django.conf.urls import url, patterns, include
from scradeweb import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'threads', views.ThreadViewSet, )
router.register(r'posts', views.PostViewSet)
urlpatterns = router.urls
When I go to http://127.0.0.1:8000/scradeweb/posts/, it raises the exception:
ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "thread-detail".
You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
Why does it not work?
Here is my scradeweb/models.py
class Thread(models.Model):
thread_id = models.IntegerField()
sticky = models.NullBooleanField()
prefix = models.CharField(max_length=255)
title = models.CharField(max_length=255)
start_time = models.DateTimeField()
author = models.CharField(max_length=255)
url = models.URLField(unique=True)
forum = models.ForeignKey(Forum, related_name='threads')
class Meta:
ordering = ('start_time', )
class Post(models.Model):
post_id = models.IntegerField()
url = models.URLField(unique=True)
post_number = models.IntegerField()
start_time = models.DateTimeField(blank=True)
last_edited_time = models.DateTimeField(blank=True, null=True)
author = models.CharField(max_length=255, blank=True)
content = models.TextField(blank=True)
thread = models.ForeignKey(Thread, related_name='posts')
class Meta:
ordering = ('post_number', )
scradeweb/serializers.py:
from rest_framework import serializers
from scradeweb.models import Thread, Post
class ThreadSerializer(serializers.HyperlinkedModelSerializer):
posts = \
serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='post-detail',
)
class Meta:
model = Thread
fields = ('pk', 'thread_id', 'title', 'url', 'posts')
read_only_fields = ('thread_id', )
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
scradeweb/views.py:
...
class ThreadViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Thread.objects.all()
serializer_class = ThreadSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
The problem here is that you are using Django REST Framework with a namespace. Many components do not work well with them, which doesn't mean they can't be used, so you need to work around the issues by doing a lot of manual work. The main problem is with hyperlinked relations (Hyperlinked*Fields), because they have to reverse views, and that requires that the namespace is specified.
The first place to start at is the router level, which doesn't currently support namespaces. This is only an issue for the DefaultRouter, as it will build out the index page that contains a list of reversed urls, which will trigger errors right away. This has been fixed in the upcoming release, but for now you're stuck without it. At best (DRF 3.0+), the index page will be completely empty (or point to incorrect urls), in the worst case scenario (DRF 2.x) it will always trigger an internal server error.
The second place to look at is the serializer fields. By default, the HyperlinkedModelSerializer will automatically generate HyperlinkedRelatedField and HyperlinkedIdentityField fields with non-namespaced urls. When you are using namespaces, you have to override all of these automatically generated fields. This generally means you are better off just not using a HyperlinkedModelSerializer, and instead controlling the output with a ModelSerializer.
The first (and easiest) thing to fix is the url field that is automatically generated for all objects. This is automatically generated with view_name set to the detail page (thread-detail in this case), without a namespace. You are going to need to override the view_name and include the namespace in front of it (so scradeweb:thread-detail in this case).
class ThreadSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name="scradeweb:thread-detail",
)
You are also going to need to override all of the automatically generated related fields. This is slightly more difficult for writable fields, but I would always recommend looking at repr(MySerializer()) in those cases to see the arguments for the automatically generated fields. For the case of read-only fields, it's a matter of just copying over the default arguments and changing the view_name again.
class ThreadSerializer(serializers.ModelSerializer):
posts = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='scradeweb:post-detail',
)
This will need to be done for all fields, and it doesn't look like Django REST Framework will be adding the ability to do it automatically in the near future. While this will not be enjoyable at first, it will give you a great opportunity to appreciate everything DRF previously did for you automatically.
I have had the same kind of issue and fix it just change the "HyperlinkedModelSerializer" to "ModelSerializer".
I am just following the rest framework tutorial and got stucked in this part
class TodoSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TodoList
fields = '__all__'
to:
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = TodoList
fields = '__all__'

Categories