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

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?

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

Django REST Framework - Filtering against the URL

I am using the Django REST Framework toolkit with Django 1.11 and I am trying to filter the results against the url. Here is my setup:
models.py:
from django.db import models
class Package(models.Model):
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.name
serializers.py:
from rest_framework import serializers
from .models import Package
class PackageSerializer(serializers.ModelSerializer):
class Meta:
model = Package
fields = ('name',)
views.py:
from rest_framework import viewsets
from .models import Package
from .serializers import PackageSerializer
class PackageViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = PackageSerializer
queryset = Package.objects.all()
urls.py
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'package', views.PackageViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/v1/', include(router.urls)),
]
Currently when I use this I can filter the results by the id field:
http://127.0.0.1:8000/api/v1/package/1/
I am hoping to filter the results by the name field of my package model instead by using this:
http://127.0.0.1:8000/api/v1/package/basic/
How would I be able to accomplish this?
Set the lookup_field attribute in the viewset, see the documentation.
class PackageViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = PackageSerializer
queryset = Package.objects.all()
lookup_field = 'name'
Use filter_fields in views.
filter_fields = ('name',)
lookup field is used to set lookup by default it would be model pk
if you wish to make your URL,
my_url/filter_field/
set lookup_field = "name"
if you want search from URL like ,
my_url/?name=something
you need to set filter_fields in views.

Django REST Framework Serializing ForeignKey and ManyToManyFields

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.

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()

Get a specific object in Django Rest Framework

I want to get only one element in a JSON object, from Django Rest Framework.
I got the next code Serializer:
class OneUserSerializer(serializers.ModelSerializer):
class Meta:
model = Usuario
fields = ('usuario', 'nombre', 'apellidos', 'mensajes')
The View Set:
class OneUserViewSet(viewsets.ModelViewSet):
queryset = Usuario.objects.filter(id = 'Here is the PARAMETER')
serializer_class = OneUserSerializer
The url:
router.register(r'user', views.OneUserViewSet)
I want to get only one object 'Usuario' passing the id from the url for example url/id_user
If you're using SimpleRouter, then urls will be generated for you:
class OneUserViewSet(viewsets.ModelViewSet):
queryset = Usuario.objects.all()
serializer_class = OneUserSerializer
urls.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'user', views.OneUserViewSet)
urlpatterns = router.urls
This will generate the following url (among others):
^users/{pk}/$

Categories