Django-rest-framework Details Подробности и соответствующий URL для Model с двумя не вложенными внешними ключами - PullRequest
1 голос
/ 02 апреля 2019

Я создаю 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...