get_or_create is a frequently used and handy function in Django. In Django DRF you can use it as you would use in a regular application if using functional view or class view for processing the data.
With Django Serializes offering many functionality in the box, I wanted to get this done in serialize level. It’s staright forward if you don’t have any unique_together
constrain set to the key fields.
For demo I am using a Guest Model with name, phone number and email, where email and phone is set to be unique_togethe
.
class Guest(models.Model):
first_name = models.CharField(max_length=300)
last_name = models.CharField(max_length=300, blank=True)
phone = models.CharField(max_length=300)
email = models.EmailField(max_length=300)
class Meta:
unique_together = ('email', 'phone')
Define a new create
function and use get_or_create
. You are good to go.
class GuestSerializer(serializers.ModelSerializer):
last_name = serializers.CharField(required=False)
id = serializers.ReadOnlyField()
class Meta:
model = Guest
fields = ('id','first_name', 'last_name', 'email', 'phone')
def create(self, validated_data):
guest, created = Guest.objects.get_or_create(
email=validated_data['email'],
phone=validated_data['phone'],
defaults={
'first_name': validated_data['first_name'],
'last_name': validated_data['last_name']
})
return guest
unique_together and get_or_create in DRF
If you have set up unique_together
constrains for fields in models then the above code will raise a unique together error before the create
functions is invoked.
It’s triggered by validation functions in ModelSerializer
which inspects unique together before passing control to create
function, to get over this a custom validation function to skip the unique together validation will work, because get_or_create
is used.
class GuestSerializer(serializers.ModelSerializer):
last_name = serializers.CharField(required=False)
id = serializers.ReadOnlyField()
class Meta:
model = Guest
fields = ('id','first_name', 'last_name', 'email', 'phone')
def run_validators(self, value):
for validator in copy(self.validators):
if isinstance(validator, validators.UniqueTogetherValidator):
self.validators.remove(validator)
super(GuestSerializer, self).run_validators(value)
def create(self, validated_data):
guest, created = Guest.objects.get_or_create(property=validated_data['property'],
email=validated_data['email'],
phone=validated_data['phone'],
defaults={
'first_name': validated_data['first_name'],
'last_name': validated_data['last_name']
})
return guest