1
votes

I'm trying to convert from using Django forms to just using REST Framework serializers for the forms. My models are:

class UserDetails(models.Model):
    user = models.OneToOneField(User, related_name='details', on_delete=models.CASCADE)
    date_of_birth = models.DateField(default=None, blank=True, null=True)

class Employee(models.Model):
    user = models.OneToOneField(User, related_name='employee', on_delete=models.CASCADE)
    company = models.ForeignKey(Company, on_delete=models.CASCADE)

class Company(models.Model):
    name = models.CharField(verbose_name='Company Name', max_length=50, unique=True)
    info = models.CharField(verbose_name='Company Information', max_length=300, blank=True)

I have serializers for User, UserDetails, and Company.

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.User
        fields = ('id', 'username', 'email', 'first_name', 'last_name', )

class UserDetailsSerializer(serializers.ModelSerializer):
    user = UserSerializer()

    class Meta:
        model = models.UserDetails
        fields = ('user', 'date_of_birth')
        read_only_fields = ('is_verified',)

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Company
        fields = ('name', 'info')

When users register, they will also specify company information and will become an employee of that company. How do I go about creating a form to create all three records atomically?

Here's my attempt:

class RegistrationSerializer(serializers.Serializer):
    details = UserDetailsSerializer(label='')
    password = serializers.CharField(
        label='Password',
        style={'input_type': 'password'}
    )

    password2 = serializers.CharField(
        label='Password Confirmation',
        style={'input_type': 'password'}
    )

    company = CompanySerializer()

I also want a password confirmation field, which will cause the form to be invalid if it does not match. Django forms had a clean() method which I used to valid the data. I'm not sure if serializers do (can't find it in the doucmentation). Also, when I call the serializer's save() in my View class, it's asking me to implement the create() method for the serializer.

1

1 Answers

2
votes

There are methods for creating and updating instances in serializers

From drf official docs below

If we want to be able to return complete object instances based on the validated data we need to implement one or both of the .create() and update() methods. For example:

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        return instance

drf also have Field-level validation from there api docs below

You can specify custom field-level validation by adding .validate_ methods to your Serializer subclass. These are similar to the .clean_ methods on Django forms. These methods take a single argument, which is the field value that requires validation. Your validate_ methods should return the validated value or raise a serializers.ValidationError. For example:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value