Search code examples
djangodjango-rest-frameworkdefault

DRF: Best way to supply a dynamic default field value?


Our SAAS site utilizes a DRF backend with a Vue frontend. We have fields that do not require a value from the user, but do require a value in the database. I'd like to know where's the best place to supply such dynamic defaults. I've read in other posts that "save() is not always called" - though I don't yet know the circumstances where it would not be called.

So, consider the following model:

class Tenant(models.Model):

    name = models.CharField(max_length=100)
    subdomain = models.CharField(max_length=100, blank=True, null=True)
    schema_name = models.CharField(max_length=63, unique=True)

In this case, only "name" is required (from the user); "schema_name", if left blank in the frontend form, can be derived from "name" (converting it to lowercase). Likewise, "subdomain" can be derived from "schema_name". "subdomain" can be blank/null because the "public" schema doesn't reference a subdomain, but its value will be required for all tenants other than "public".)

So where should I put the code that populates those fields if they are blank when it comes time to create or update a Tenant?


Solution

  • Save will be called unless you do bulk updates, so you can put it there just fine. I prefer not to if there is a choice, but sometimes there isn't.

    If you want to put it in the serializer, you can write something like this, and then use a ModelViewSet to handle the details:

    class TenantSerializer(ModelSerializer):
        name = CharField(required=True, min_length=1)
        sub_domain = CharField(required=False)
    
        class Meta:
            model = Tenant
            fields = ['id', 'name', 'sub_domain']
    
        def validate(self, attrs):
            # attrs is all fields parsed & validated on a per-field level
            # in here you can do validation that depends on >1 field
            # values returned will be passed to the serializer create()/update()
            # via the common serializer.save() method
            if self.instance:
                # doing an update, maybe different logic, or just ignore?
            else:
                if not attrs.get('sub_domain'): # missing or blank
                    attrs['sub_domain'] = parse_subdomain(attrs.get('name'))
            return attrs