Переопределение .update() и получение "... поля с таким <именем> уже существует" после добавления lookup_field

Мне приходится переопределять .create() и .update() в моем сериализаторе из-за наличия вложенных отношений crmfields.

Я расширяю модель User и добавляю некоторые поля из нашей CRM системы, которые синхронизируются через Webhooks. В основном, если пользователь создается/обновляется там, он синхронизируется с веб-приложением.

Моя POST выглядит следующим образом:

{
    "username": "test@test.com",
    "first_name": "Test First",
    "last_name": "Test Last",
    "email": "test@test.com",
    "is_active": true,
    "crmfields": {
        "guid": "00000000-0000-0000-0000-000000000001"
    }
}

Это создает пользователя и соответствующую запись crmfields примерно в одно и то же время.

Проблема, с которой я столкнулся, связана с PUT и выполнением чего-то вроде следующего:

{
    "username": "test@test3.com",
    "first_name": "Test First1",
    "last_name": "Test Last2",
    "email": "test@test1.com",
    "is_active": true,
    "crmfields": {
        "guid": "00000000-0000-0000-0000-000000000001"
    }
}

Возвращается ответ:

{
    "crmfields": {
        "guid": [
            "crm fields with this guid already exists."
        ]
    }
}

Похоже, что он не доходит до моего пользовательского метода .update() в сериализаторе, прежде чем сработает эта ошибка.

Несколько месяцев назад у меня это работало. С тех пор я добавил lookup_field к views.py, потому что мы решили, что хотим использовать guid в crmfields в качестве поиска (например, /api/users/00000000-0000-0000-0000-000000000001/), а не pk в модели Users. Для этого есть веские причины, связанные с интеграцией между двумя системами.

Как я должен решить эту ошибку, когда речь идет о .update() и lookup_field (если предположить, что они связаны)?

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserSyncViewSet

# Instantiate a DefaultRouter object to handle these routes
router = DefaultRouter()
router.register(r'users', UserSyncViewSet)

urlpatterns = [
    path('', include(router.urls))
]

# views.py
from django.contrib.auth.models import User
from rest_framework import viewsets
from . serializers import UserSerializer

class UserSyncViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]
    queryset = User.objects.all().order_by('id')
    serializer_class = UserSerializer
    lookup_field = 'crmfields'
# serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import CRMFields

class CRMFieldsSerializer(serializers.ModelSerializer):
    class Meta:
        model = CRMFields
        fields = [
            'guid'
        ]

class UserSerializer(serializers.ModelSerializer):
    crmfields = CRMFieldsSerializer()

    class Meta:
        model = User
        fields = [
            'id',
            'username',
            'first_name',
            'last_name',
            'email',
            'last_login',
            'is_active',
            'crmfields'
        ]
        extra_kwargs = {
            'username': {'required': True},
            'email': {'required': True},
            'first_name': {'required': True},
            'last_name': {'required': True},
            'is_active': {'required': True}
        }

    def create(self, data):
        user = User(
            username=data['email'],
            email=data['email'],
            first_name=data['first_name'],
            last_name=data['last_name'],
            is_active=data['is_active']
        )
        user.save()

        CRMFields.objects.create(
            user=user,
            guid=data['crmfields']['guid']
        )

        return user

    def update(self, instance, data):
        instance.email = data.get('email', instance.email)
        instance.username = data.get('username', instance.username)
        instance.first_name = data.get('first_name', instance.first_name)
        instance.last_name = data.get('last_name', instance.last_name)
        instance.password = data.get('password', instance.password)
        instance.is_active = data.get('is_active', instance.is_active)
        instance.save()

        # Saving the CMRFields value updates
        crmfields_data = data.pop('crmfields')
        crmfields = instance.crmfields
        crmfields.guid = crmfields_data.get('guid', crmfields.guid)
        crmfields.save()

        return instance
# models.py
from django.db import models
from django.contrib.auth.models import User

class CRMFields(models.Model):
    user = models.OneToOneField(
        User, on_delete=models.CASCADE)
    guid = models.UUIDField(primary_key=True, null=False)
    
    class Meta:
        db_table = 'auth_user_crm'        
Вернуться на верх