I get KeyError
when I post and I don't write either email or telephone. In my code, either email or telephone are required but not both of them.
Why is this happening?
This is the serializer:
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = order
fields = '__all__'
extra_kwargs = {
'email': {'required': False}, 'phonenumber': {'required': False}}
def create(self, validated_data):
order= Order.objects.create(
email=validated_data['email'],
phonenumber=validated_data['phonenumber'],
food=validated_data['food']
)
This is the model:
class Order(models.Model):
date = models.DateTimeField(auto_now_add=True)
email = models.EmailField(max_length=200, unique=True)
phonenumber = models.IntegerField(unique=True)
food= models.ManyToManyField('Food', related_name='orders', blank=True)
def __str__(self):
return self.food
This is the error:
django-challenge-back | Traceback (most recent call last):
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
django-challenge-back | response = get_response(request)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
django-challenge-back | response = wrapped_callback(request, *callback_args, **callback_kwargs)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
django-challenge-back | return view_func(*args, **kwargs)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
django-challenge-back | return self.dispatch(request, *args, **kwargs)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
django-challenge-back | response = self.handle_exception(exc)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
django-challenge-back | self.raise_uncaught_exception(exc)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
django-challenge-back | raise exc
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
django-challenge-back | response = handler(request, *args, **kwargs)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/generics.py", line 190, in post
django-challenge-back | return self.create(request, *args, **kwargs)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/mixins.py", line 19, in create
django-challenge-back | self.perform_create(serializer)
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/mixins.py", line 24, in perform_create
django-challenge-back | serializer.save()
django-challenge-back | File "/usr/local/lib/python3.9/site-packages/rest_framework/serializers.py", line 205, in save
django-challenge-back | self.instance = self.create(validated_data)
django-challenge-back | File "/app/order/serializers.py", line 39, in create
django-challenge-back | email=validated_data['email'],
django-challenge-back | KeyError: 'email'
django-challenge-back | [16/Apr/2021 20:02:22] "POST /pedido HTTP/1.1" 500 103807
Answer for original question:
email=validated_data['email']
will throw KeyError
if email
is not in validated_data
, the same for phonenumber
.
You can check if the key exists beforehand or use the .get
syntax to avoid the error and get a None
value if not present.
email=validated_data.get('email')
will return None
if the email is not present, instead of the KeyError
.
Edit for new approach:
You need to allow null values if they will not be required (see the added null=True
in email and phonenumber):
class Order(models.Model):
date = models.DateTimeField(auto_now_add=True)
email = models.EmailField(max_length=200, unique=True, null=True)
phonenumber = models.IntegerField(unique=True, null=True)
food= models.ManyToManyField('Food', related_name='orders', blank=True)
Now neither email or phonenumber are required. If you want to have one of them required add a validate
method to your serializer to check that one of them is present.
Something like:
class OrderSerializer(serializers.ModelSerializer):
def validate(self, data):
email = data.get('email')
phonenumber = data.get('phonenumber')
if not email and not phonenumber:
raise serializers.ValidationError("one of email or phone number required")
return data