Переопределение .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'