Django/drf: многие ко многим, сериализатор, дубликаты
У меня есть M2M отношения между моделями Combo и ComboLinks.
Пользователь должен иметь возможность предоставлять ComboLinks, которые собираются бэкендом в один Combo. Это делается с помощью сериализатора DRF и работает, но с проблемой: если пользователь предоставляет список ComboLinks, который имеет одну или более ComboLinks, уже присутствующих в базе данных - создается дубликат.
Как я могу избежать создания дубликатов и использовать уже имеющиеся ComboLinks для указания на новый объект Combo?
структура модели django:
class Combo(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='searches')
updated = models.DateTimeField(auto_now = True)
class ComboLink(models.Model):
combo = models.ManyToManyField('Combo', blank=True, related_name='links')
title = models.CharField(max_length = 64, null=True, blank=True)
django view:
@api_view(['GET', 'POST', 'PUT', 'DELETE'])
def getCombo(request, pk=None):
user = request.user
# for all paths besides create new search
if (pk != 'new') and (pk != None):
combo = Combo.objects.get(id=int(pk))
if user != combo.user:
return Response('Not valid user for a search', status=status.HTTP_401_UNAUTHORIZED)
# handle different request methods
if request.method == 'GET':
serializer = ComboSerializer(combo, many=False)
return Response(serializer.data, status=status.HTTP_200_OK)
if request.method == 'POST':
data = request.data
data['user'] = user.id
combo = Combo.objects.create(user= User.objects.get(id=user.id))
serializer = ComboSerializer(instance = combo, data = data)
if serializer.is_valid():
print('updateSearch: combo serializer IS valid')
serializer.save()
combo.save()
else:
print('updateSearch: combo serializer NOT valid, failing to save')
print(serializer.errors)
return Response(serializer.data, status=status.HTTP_200_OK)
сериализаторы (с возможностью записи):
from rest_framework.serializers import ModelSerializer, HyperlinkedRelatedField
from drf_writable_nested.serializers import WritableNestedModelSerializer
from .models import ComboLink, Combo
class ComboLinkSerializer(ModelSerializer):
class Meta:
model = ComboLink
fields = ['title']
class ComboSerializer(WritableNestedModelSerializer):
links = ComboLinkSerializer(many=True, read_only=False)
class Meta:
model = Combo
fields = ['user', 'id', 'updated','links']
Я создал обходной путь, который не включает DRF записываемые сериализаторы, просто проверьте в предоставленных данных, присутствуют ли эти ComboLinks, и если да, свяжите их и выберите из данных, которые будут добавлены в сериализатор, например, так:
def check_links(request, new_combo=True):
data = request.data.copy()
data['user'] = user.id
if new_combo:
combo = Combo.objects.create(user=request.user)
# if provided link was already present in the db just use it instead of creating a duplicate
indx_to_rm = []
for indx, i in enumerate(data['links']):
if ComboLink.objects.filter(link=i['link']).exists():
print('\none exist\n')
existingComboLink = ComboLink.objects.get(link=i['link'])
combo.links.add(existingComboLink)
index_to_rm.append(indx)
for indx in indx_to_rm:
data['links'].pop(indx)
serializer = ComboSerializer(instance = combo, data = data)
if serializer.is_valid():
print('updateSearch: combo serializer IS valid')
serializer.save()
combo.save()
else:
print('updateSearch: combo serializer NOT valid, failing to save')
print(serializer.errors)
return serializer.data
Это идеальный вариант, я не могу перебирать массив и удалять из него элементы чистым способом с помощью простого понимания generator|list, потому что мне нужно что-то делать с элементом, а не просто проверять, соответствует ли он условию, но это работает!