django-REST:嵌套关系与PrimaryKeyRelatedField

时间:2015-11-14 14:26:59

标签: django-rest-framework

如果你有大量数据,使用嵌套关系或PrimaryKeyRelated字段会更好吗?

我有一个关系很深的模特 为简单起见,我没有添加colums。

型号:

Model

USECASE:

  1. 用户创建1个具有2个锻炼和3个锻炼锻炼的锻炼计划。
  2. 用户为每个WorkoutExercise / Exercise创建6个套件。
  3. 用户开始锻炼>创建了新的FinishedWorkout
  4. 用户首先锻炼并输入使用过的重量>创建了FinishedSet的新FinishedWorkoutExercise
  5. 问题:

    我想跟踪每个锻炼计划的进展>锻炼>行使。 因此,随着时间的推移,用户可能已经完成了数十次锻炼,因此如果数据库中已有数百套,那么用户就可以完成。

    如果我现在使用嵌套的关系,我可能会加载大量我不需要的数据。 但是如果我使用PrimaryKeyRelatedFields,我必须分别加载我需要的所有数据,这意味着我的前端会有更多的努力。

    在这种情况下,首选哪种方法?

    修改
    如果我使用PrimaryKeyRelatedFields,我如何区分是否Workoutplan中的锻炼是一个带有主键的数组或带有加载对象的数组?

2 个答案:

答案 0 :(得分:1)

如果您使用PrimaryKeyRelatedField,您将有一个很大的重载来请求前端的必要数据

在您的情况下,我将使用您想要的字段创建特定的序列化程序(使用Meta.fields属性)。因此,您不会加载不必要的数据,并且前端不需要从后端请求更多数据。

如果您需要更多详细信息,我可以编写示例代码。

答案 1 :(得分:0)

我将在一秒钟内讨论有关序列化器的问题,但首先要澄清一下。将重复模型作为锻炼/完成锻炼,设置/完成集,......?

的目的是什么

为什么不......

class Workout(models.Model):
    #...stuff...
    finished = models.DateTimeField(null=True, blank=True)
    #...more stuff...

然后,您可以在锻炼完成后设置完成日期。

现在,关于这个问题。我建议你考虑用户互动。您想要填充前端的哪些部分?数据如何相关以及用户如何访问它?

您应该考虑使用DRF查询哪些参数。您可以发送日期并期望在特定日期完成锻炼:

// This example is done in Angular, but you get the point...
var date= {
    'day':'24',
    'month':'10',
    'year':'2015'
};
API.finishedWorkout.query(date).$promise
.then(function(workouts){
    //...workouts is an array of workout objects...
});

...视图集

class FinishedWorkoutViewset(viewsets.GenericAPIView,mixins.ListModelMixin):
    serializer_class = FinishedWorkOutSerializer
    queryset = Workout.objects.all()
    def list(self, request):
        user = self.request.user
        day = self.data['day'];
        month = self.data['month'];
        year = self.data['year'];
        queryset = self.filter_queryset(self.get_queryset().filter(finished__date=datetime.date(year,month,day)).filter(user=user))
        page = self.paginate_queryset(queryset)
        serializer = self.get_serializer(queryset, many=True)
        return response.Response(serializer.data)

然后您的 FinishedWorkoutSerializer 可以为该特定类型的查询提供您想要的任何字段。

这会为您留下一堆非常具体的网址,但这些网址并不是很好,但您可以使用特定的序列化程序进行这些交互,并且您还可以动态更改过滤器,具体取决于具体内容参数位于self.data

您可能希望根据所调用的方法进行不同的过滤,比如说您只想列出活动练习,但如果用户查询特定练习,您希望他能够访问它(注意Exercise对象应该有一个名为" active")的models.BooleanField属性。

class ExerciseViewset(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
    serializer_class = ExerciseSerializer
    queryset = Exercise.objects.all()
    def list(self, request):
        queryset = self.filter_queryset(self.get_queryset().filter(active=True))
        page = self.paginate_queryset(queryset)
        serializer = self.get_serializer(queryset, many=True)
        return response.Response(serializer.data)

现在,您可以在同一网址上显示不同的对象,具体取决于操作。它更接近您的需求,但您仍然使用相同的序列化程序,因此如果您需要retrieve()上的大型嵌套对象,您还会得到一堆当你list()

为了保持列表简短和嵌套细节,您需要使用不同的序列化程序。

我们假设您只想发送练习'列出时会pkname属性,但无论何时查询练习,您都不会发送所有相关的" Set"在" WorkoutSets" ...

数组中排序的对象
# Taken from an SO answer on an old question...
class MultiSerializerViewSet(viewsets.GenericViewSet):
    serializers = { 
        'default': None,
    }
    def get_serializer_class(self):
        return self.serializers.get(self.action, self.serializers['default'])


class ExerciseViewset(MultiSerializerViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
    queryset = Exercise.objects.all()
    serializers = {
        'default': SimpleExerciseSerializer,
        'retrieve': DetailedExerciseSerializer
    }

然后你的serializers.py看起来有点像......

#------------------Exercise
#--------------------------Simple List
class SimpleExerciseSerializer(serializers.ModelSerializer):
    class Meta:
        model Exercise
        fields = ('pk','name')

#--------------------------Detailed Retrieve
class ExerciseWorkoutExerciseSetSerializer(serializers.ModelSerializer):
    class Meta:
        model Set
        fields = ('pk','name','description')

class ExerciseWorkoutExerciseSerializer(serializers.ModelSerializer):
    set_set = ExerciseWorkoutExerciseSetSerializer(many=True)
    class Meta:
        model WorkoutExercise
        fields = ('pk','set_set')

class DetailedExerciseSerializer(serializers.ModelSerializer):
    workoutExercise_set = exerciseWorkoutExerciseSerializer(many=True)
    class Meta:
        model Exercise
        fields = ('pk','name','workoutExercise_set')

我只是抛弃在您的模型中可能没有意义的用例和属性,但我希望这有用。

P.S。;看看我到底是如何得到Java的:p" ExcerciseServiceExcersiceBeanWorkoutFactoryFactoryFactory"