Есть ли способ добавить пользовательские данные в ListAPIView в django rest framework
Итак, я создал API для фильмов dataset
, которые содержат следующую структуру:
Models.py
class Directors(models.Model):
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
class Meta:
db_table = 'directors'
ordering = ['-id']
class Movies(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100, blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
rank = models.FloatField(blank=True, null=True)
class Meta:
db_table = 'movies'
ordering = ['-id']
class Actors(models.Model):
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
gender = models.CharField(max_length=20, blank=True, null=True)
class Meta:
db_table = 'actors'
ordering = ['-id']
class DirectorsGenres(models.Model):
director = models.ForeignKey(Directors,on_delete=models.CASCADE,related_name='directors_genres')
genre = models.CharField(max_length=100, blank=True, null=True)
prob = models.FloatField(blank=True, null=True)
class Meta:
db_table = 'directors_genres'
ordering = ['-director']
class MoviesDirectors(models.Model):
director = models.ForeignKey(Directors,on_delete=models.CASCADE,related_name='movies_directors')
movie = models.ForeignKey(Movies,on_delete=models.CASCADE,related_name='movies_directors')
class Meta:
db_table = 'movies_directors'
ordering = ['-director']
class MoviesGenres(models.Model):
movie = models.ForeignKey(Movies,on_delete=models.CASCADE,related_name='movies_genres')
genre = models.CharField(max_length=100, blank=True, null=True)
class Meta:
db_table = 'movies_genres'
ordering = ['-movie']
class Roles(models.Model):
actor = models.ForeignKey(Actors,on_delete=models.CASCADE,related_name='roles')
movie = models.ForeignKey(Movies,on_delete=models.CASCADE,related_name='roles')
role = models.CharField(max_length=100, blank=True, null=True)
class Meta:
db_table = 'roles'
ordering = ['-actor']
urls.py
from django.urls import path, include
from . import views
from api.views import getMovies, getGenres, getActors
urlpatterns = [
path('', views.getRoutes),
path('movies/', getMovies.as_view(), name='movies'),
path('movies/genres/', getGenres.as_view(), name='genres'),
path('actor_stats/<pk>', getActors.as_view(), name='actor_stats'),
]
serializer.py
from rest_framework import serializers
from movies.models import *
class MoviesSerializer(serializers.ModelSerializer):
class Meta:
model = Movies
fields = '__all__'
class DirectorsSerializer(serializers.ModelSerializer):
class Meta:
model = Directors
fields = '__all__'
class ActorsSerializer(serializers.ModelSerializer):
class Meta:
model = Actors
fields = '__all__'
class DirectorsGenresSerializer(serializers.ModelSerializer):
class Meta:
model = DirectorsGenres
fields = '__all__'
class MoviesDirectorsSerializer(serializers.ModelSerializer):
movie = MoviesSerializer(many = False)
director = DirectorsSerializer(many = False)
class Meta:
model = MoviesDirectors
fields = '__all__'
class MoviesGenresSerializer(serializers.ModelSerializer):
movie = MoviesSerializer(many = False)
class Meta:
model = MoviesGenres
fields = '__all__'
class RolesSerializer(serializers.ModelSerializer):
movie = MoviesSerializer(many = False)
actor = ActorsSerializer(many = False)
class Meta:
model = Roles
fields = '__all__'
views.py
class getMovies(ListAPIView):
directors = Directors.objects.all()
queryset = MoviesDirectors.objects.filter(director__in=directors)
serializer_class = MoviesDirectorsSerializer
pagination_class = CustomPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['director__first_name', 'director__last_name']
class getGenres(ListAPIView):
movies = Movies.objects.all()
queryset = MoviesGenres.objects.filter(movie__in=movies).order_by('-genre')
serializer_class = MoviesGenresSerializer
pagination_class = CustomPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['genre']
class getActors(ListAPIView):
queryset = Roles.objects.all()
serializer_class = RolesSerializer
pagination_class = CustomPagination
def get_queryset(self):
return super().get_queryset().filter(
actor_id=self.kwargs['pk']
)
Теперь я хочу подсчитать количество фильмов по жанрам, в которых играл актер с определенным пк в getActors
классе.
Например, количество фильмов по жанрам, в которых участвовал актер. Например, Драма: 2, Ужасы: 3
Сейчас я получаю общее количество фильмов count: 2
:
GET /api/actor_stats/17
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 800480,
"movie": {
"id": 105231,
"name": "Everybody's Business",
"year": 1993,
"rank": null
},
"actor": {
"id": 17,
"first_name": "Luis Roberto",
"last_name": "Formiga",
"gender": "M"
},
"role": "Grandfather"
},
{
"id": 800481,
"movie": {
"id": 242453,
"name": "OP Pro 88 - Barra Rio",
"year": 1988,
"rank": null
},
"actor": {
"id": 17,
"first_name": "Luis Roberto",
"last_name": "Formiga",
"gender": "M"
},
"role": "Himself"
}
]
}
Каким оптимизированным способом можно достичь следующего:
- Количество_фильмов_по_жанрам
- Драма: 2
- Ужасы: 3
после подсчета?
Есть много способов реализовать этот маршрут, это зависит от многих критериев и от того, как часто он будет использоваться .
Я думаю, что правильным способом будет создание специальной модели, которая будет хранить статистику актера с отношением один к одному к актеру и пересчитывать значение каждый раз, когда добавляется фильм. Но если вы часто добавляете фильмы, это может замедлить работу базы данных.
Вы также можете согласиться на некоторое время иметь устаревшие данные и регулярно обновлять таблицу с помощью фонового задания и, возможно, используя пользовательский sql-запрос, который обеспечит вам лучшую производительность (массовое обновление).
Я бы начал с вашей модели, у вас есть жанры, определенные как CharField в двух ваших моделях. Не изолируя их нигде, вы должны искать в обеих таблицах все типы жанров. Если этого не сделать, то вы просто предполагаете, что все жанры, которые у вас есть в одной таблице, есть и в другой, что не может быть правдой.
Кроме того, запрос строковых полей не очень эффективен по сравнению с int PK, так что с точки зрения масштабирования это плохо. (Конечно, я говорю об этом в целом, как о хорошей практике, а не конкретно о жанрах кино)
Лучшим вариантом будет либо модель жанра, либо поле выбора, где вы определяете все возможные жанры.
Что касается подсчета, то это можно сделать внутри класса сериализатора, используя поле serializermethodfield.
Если вы хотите, чтобы number of movies
по genre
для данного актора, что вы можете сделать, это аннотировать и подсчитать агрегат
return Roles.objects.filter(
actor_id=self.kwargs['pk']
).values('movie__movies_genres__genre').annotate(
no_of_movies=Count('movie__movies_genres__genre'),
genre=F('movie__movies_genres__genre'),
)
Сначала мы фильтруем роли для данного актера
затем значения сгруппируем по genre
затем аннотация вычисляется над всеми членами группы, которые count
и получим genre
и это будет ваш сериализатор
class GenreNoMovies(serializers.Serializer):
genre = serializers.CharField()
no_of_movies = serializers.IntegerField()
если у вас огромный набор данных, он не будет работать хорошо, но вы можете создать индексы соответствующим образом
вы можете узнать больше о Django queryset API