4

I have a Candidates object model in Django Application, who I get from a form in a Front End.

If this Candidate send personal data again with the same email, DRF response {"email":["A user with that email already exists."]} and not save the form.

The idea is, if this candidate send the form again with personal email and I have saved it in my database, I will update personal data of this candidate.

I tried with:

My view:

@csrf_exempt
@api_view(['GET', 'POST','PATCH'])
def CandidatesCreate(request, *args, **kwargs):
parser_classes = (FileUploadParser,)

if request.method == 'PATCH' or request.method == 'POST':

    serializer = CandidatesSerializer(data=request.data)
    if serializer.is_valid():
        instance, created = serializer.get_or_create()
        if not created:
            serializer.update(instance, serializer.validated_data)
        return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

My Serializer

class CandidatesSerializer(serializers.ModelSerializer):

cv = serializers.FileField(required=False,max_length=None, use_url=True)

class Meta:
    model = Candidates
    fields = (
        'pk', 
        'user',
        'name', 
        'email', 
        'whatever',
        'whatever'
        )

However, it not working.I am not be sure if get_or_create() was depreciated, to be honest. I not found real information about it.

Miguel Herreros Cejas
  • 664
  • 1
  • 12
  • 28

3 Answers3

8

You have "unique=True" on the model's e-mail field, correct? If so, I think the serializer checks if that's the case when you run:

serializer.is_valid()

To overcome this, you can try adding this to the serializer's Meta class:

extra_kwargs = {
    'email': {'validators': []},
}

And if you want to keep the email validation, you can try:

from django.core.validators import EmailValidator

class Meta:
    #... your code
    extra_kwargs = {
        'email': {'validators': [EmailValidator,]},
    }
Dev Catalin
  • 1,265
  • 11
  • 25
  • 1
    I am not sure that this validation is being run against the db. It's more like the [form validation](https://docs.djangoproject.com/en/3.0/ref/forms/validation/). Django tries to parse Http params into python dict and checks their type. Database validation comes when `.save()` is called and on `.is_valid()`. Moreover, [`EmailValidator`](https://docs.djangoproject.com/en/3.0/ref/validators/#emailvalidator) checks if the given string is an email regex and not if it exists in the db – dstrants Feb 06 '20 at 11:33
  • 1
    @dstrants , yes, you are correct, I showed how he can remove the unique validator on the serializer level. The model's validators are not impacted by this. – Dev Catalin Feb 06 '20 at 11:40
6

Finally, the solution was this:

from .models import Candidates
from rest_framework import serializers
from django.core.validators import EmailValidator

# first we define the serializers
class CandidatesSerializer(serializers.ModelSerializer):

cv = serializers.FileField(required=False,max_length=None, use_url=True)

class Meta:
    model = Candidates
    fields = (
        'pk', 
        'user',
        'name', 
        'email', 
        'whatever'
        )
    extra_kwargs = {'email': {'validators': [EmailValidator,]},
  }

And my view.py

@csrf_exempt
@api_view(['GET', 'POST','PATCH'])
def CandidatesCreate(request, *args, **kwargs):
parser_classes = (FileUploadParser,)

if request.method == 'PATCH' or request.method == 'POST':

    serializer = CandidatesSerializer(data=request.data)
    if serializer.is_valid():
        instance, created = Candidates.objects.update_or_create(email=serializer.validated_data.get('email', None), defaults=serializer.validated_data) 
        if not created:
            serializer.update(instance, serializer.validated_data)
        return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

And it working correctly.

Miguel Herreros Cejas
  • 664
  • 1
  • 12
  • 28
1

As also this solution suggests you can use update_or_create instead of get_or_create like:

# make sure you use the arguments the right way
instance, created = serializer.update_or_create(email=validated_data.get('email', None), defaults=dict_with_everything_else) 
# instead of
instance, created = serializer.get_or_create()

just make sure you pass the arguments the right way. When can also check the docs on how to do so

dstrants
  • 7,423
  • 2
  • 19
  • 27