Я создаю REST API с помощью Django-rest-framework.
У меня есть модель Users, модель Spots и модель UserSpots.Модель Users и Spots очень просты.Модель UserSpots представляет пользователей, следующих за точками.Модель UserSpots определяется внешними ключами моделей Users и Spots.Однако модель Spots не обязательно вложена в модель Users.Пользователи и споты существуют независимо.Пользователи не могут создавать споты.Есть пользователи, которые не следуют ни за какими спотами, и есть споты, у которых нет фолловеров.
Я успешно создал Просмотры для отображения:
SpotsListView
= список всех мест
/spots/
SpotsIndividualView
= получить одно место
/spots/(spotId)/
UsersListView
= список всех пользователей
/users/
UsersIndividualView
= получить / обновить одного пользователя
/users/(userId)/
UserSpotsListView
= список всех мест, за которыми следует один пользователь
/users/(userId)/favspots/
Вопрос:
Я не могу понять, как написать представление для создания / извлечения / обновления / удаления одного места, за которым следит один пользователь:
UserSpotsIndividualView
/users/(userId)/favspots/(spotId)/
Ниже приведены соответствующие части моего API.Я пробовал десятки разных способов создания UserSpotsIndividualView, поэтому я столкнулся со всевозможными сообщениями об ошибках.Я пропускаю сообщения об ошибках, а также большинство моих попыток краткости.
Заранее благодарю за любую помощь.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE ARE MY MODELS #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy
class Users(AbstractUser):
userId = models.AutoField(primary_key=True) # make the primary key "userId" instead of "id"
username = models.CharField(blank=True, null=True, max_length=255) # override default settings of username being used for authentication
email = models.EmailField(ugettext_lazy('email address'), unique=True)
unitId = models.IntegerField(default=1) # default to US units
birthYear = models.IntegerField(null=True)
genderId = models.IntegerField(null=True)
USERNAME_FIELD = 'email' # sets email field to be used for authentication
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
def __str__(self):
return "{}".format(self.email)
class Spots(models.Model):
"""This class represents the spots model."""
spotId = models.IntegerField(primary_key=True)
spotName = models.CharField(max_length=255)
areaId = models.IntegerField()
latitude = models.FloatField(default=999.999)
longitude = models.FloatField(default=999.999)
def __str__(self):
return self
class UserSpots(models.Model):
userId = models.ForeignKey(Users, db_column='userId', on_delete=models.CASCADE)
spotId = models.ForeignKey(Spots, db_column='spotId', on_delete=models.CASCADE)
displayOrder = models.IntegerField()
class Meta:
unique_together = ('userId', 'spotId', 'displayOrder')
ordering = ['displayOrder']
def __str__(self):
return self
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE ARE MY SERIALIZERS #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
from rest_framework import serializers
from .models import Spots, Users, UserSpots
class SpotsSerializer(serializers.ModelSerializer):
class Meta:
model = Spots
fields = ('spotId', 'spotName', 'areaId')
class UsersSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = ('userId', 'email', 'first_name', 'last_name', 'password', 'unitId', 'birthYear', 'genderId')
extra_kwargs = {'password': {'write_only': True}} # cannot query pw
class UserSpotsSerializer(serializers.ModelSerializer):
class Meta:
model = UserSpots
fields = ('userId', 'spotId', 'displayOrder')
#~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE ARE MY VIEWS #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~#
from rest_framework import generics
from .models import Spots, Users, UserSpots
from .serializers import SpotsSerializer, UsersSerializer, UserSpotsSerializer
class SpotsListView(generics.ListAPIView):
"""This class lists all spots."""
# renderer_classes = (JSONRenderer,) #limit only to JSON - does not allow the browse-able api
serializer_class = SpotsSerializer
queryset = Spots.objects.all()
class SpotsIndividualView(generics.RetrieveAPIView):
"""This class gets individual spots."""
serializer_class = SpotsSerializer
queryset = Spots.objects.all()
class UsersListView(generics.ListAPIView):
"""This class lists all users."""
serializer_class = UsersSerializer
queryset = Users.objects.all()
#permission_classes = (IsAdminList,)
class UsersIndividualView(generics.RetrieveUpdateAPIView):
"""This class READs and UPDATES all individual users."""
serializer_class = UsersSerializer
queryset = Users.objects.all()
lookup_field = 'userId'
#permission_classes = (IsLoggedInUserOrAdmin,)
class UserSpotsListView(generics.ListAPIView):
"""This class lists all spots for a user by specifying the userId."""
serializer_class = UserSpotsSerializer
def get_queryset(self):
userId = self.kwargs['userId']
return UserSpots.objects.filter(userId=userId)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE IS THE VIEW I NEED HELP WITH #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
class UserSpotsIndividualView():
""" PLEASE HELP WITH THIS VIEW """
return what_I_want
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE ARE A COUPLE OF MY ATTEMPTS #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
class UserSpotsIndividualView(generics.RetrieveUpdateAPIView):
"""This class should READ, UPDATE and DELETE individual spots for users."""
serializer_class = UserSpotsSerializer
#queryset = UserSpots.objects.all()
lookup_field = 'userSpot'
def get_queryset(self):
userId = self.kwargs['userId']
spotId = self.kwargs['spotId']
return UserSpots2.objects.filter(userSpot=userSpot, spotId=spotId)
from django.shortcuts import get_object_or_404
class MultipleFieldLookupMixin(object):
"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
# Works with RetrieveAPIView but not with RetrieveUpdateDestroyAPIView
class UserSpotsIndividualView(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPIView):
"""This class should READ, UPDATE and DELETE individual spots for users."""
serializer_class = UserSpotsSerializer
queryset = UserSpots.objects.all()
lookup_fields = ('userId', 'spotId')
#~~~~~~~~~~~~~~~~~~~~~~~~~~#
# HERE ARE MY URLS #
#~~~~~~~~~~~~~~~~~~~~~~~~~~#
from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from .views import SpotsListView, SpotsIndividualView
from .views import UsersListView, UsersIndividualView
from .views import UserSpotsListView, UserSpotsIndividualView
urlpatterns = {
url(r'spots/$', SpotsListView.as_view(), name="SpotsList"),
url(r'spots/(?P<spotId>[0-9]+)/$', SpotsIndividualView.as_view(), name="SpotsDetails"),
url(r'users/$', UsersListView.as_view(), name="UsersList"),
url(r'users/(?P<userId>[0-9]+)/$', UsersIndividualView.as_view(), name="UsersDetails"),
url(r'users/(?P<userId>[0-9]+)/favspot/$', UserSpotsListView.as_view(), name="UserSpotsList"),
url(r'users/(?P<userId>[0-9]+)/favspot/(?P<spotId>[0-9]+)/$', UserSpotsIndividualView.as_view(), name="UserSpotsDetail"), # DOES NOT WORK
}
urlpatterns = format_suffix_patterns(urlpatterns)