Code generators are a good way to save time. Most of my projects have two main parts:
- The application server written using Django
- The user interface written in Flutter
I create a lot of the structure in Django first. Most of this is designing the tables by writing out their structure using classes in “models.py”. Eventually I get to writing the user interface and I have to create the matching Flutter classes that mirror the tables in Django.
If I only needed to write 3 or 4 classes and if I got the model classes perfect on the first try, I probably wouldn’t bother with a code generator. All projects I’ve worked on use much more than 3 and I have to update the models often. After only a couple of projects, the time I spent writing the code for the code generator is saving me a lot of time. It is a great tool for creating reliable code.
Python’s introspection provides a good way to create the code generator. In my work I use Django Rest Framework and I use the view classes that come with the framework. Each of these class based views are linked to a Serializer class which in turn are, in most cases, linked to a model class. My generator only needs to work at the Serializer level because there is enough information there to generate the Flutter classes.
I wrote a set of functions that loads Django and then iterates over the entries in urls.py and inspects the linked Serializers to create the matching Flutter classes for the models. In addition to that, I have API class that I generate using the urls.py information to retrieve the object from the application server.
There are a couple of steps that I need to follow to make sure that code generation works properly:
- I need to create at least one database entry for each class in models.py. This is because introspection uses a class instance to determine type information. Using issubclass works in most cases except when we have a calculated property on the model which is often represented by a ReadOnly serializer field. If we create an object instance with the values filled in we can discover the type. Django comes with the admin interface which I use early on development to test my models and create the necessary entries.
- For Serializers not based on a model class, I need to have a function with a specific name that creates an instance of the class for introspection.
- I hijacked the help_text attribute in Serializer fields in cases where introspection was not producing the result I wanted.
- I ended up creating my own list of words to help with changing my underscore field names to camel case Flutter field names.
The process of creating the code generator is slow and involves working in the interactive console and playing with the various objects to see where data is and what data is returned in various situations to figure out how to write functions that will properly discern the objects and generate the code I want.
I am working on a series of posts that will look at my work. In the next post I will look at the Django application whose classes I want to mirror in Flutter.