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, потому что мне нужно что-то делать с элементом, а не просто проверять, соответствует ли он условию, но это работает!

Вернуться на верх