I’d argue that most of the code is conceptually boilerplate, even when you have a framework to paper over it. There’s really nothing exciting about declaring an HTTP endpoint that’s going to slurp some JSON, massage it a bit, and shove it n your db. It’s a boring repetitive task, and I’m happy to let a tool do it for me.
What I’m trying to say is that for Django, especially Django Rest Framework, you don’t even declare endpoints.
DRF has a ModelViewSet where you just create a class, inherit from MVS and set the model to point to your Django ORM model and that’s it. ModelViewSet already has all the implementation code for handling POST, PUT, PATCH and DELETE.
There is no boilerplate.
There isn’t anything that an LLM would add to this process.
Around which parts of Django? Because Django has generic class based views that do exactly the same thing, where all you do is set the model attribute. Then the generic view class you inherited from has the implementation. Especially if you use a ModelForm
Here’s what a typical Django enpoint might look like for handling a json payload with some user information and storing it in the db:
from django.db import models
classUserProfile(models.Model):
username = models.CharField(max_length=100, unique=True, help_text="Unique username")
email = models.EmailField(unique=True, help_text="User's email address")
full_name = models.CharField(max_length=255, blank=True, null=True, help_text="User's full name")
date_joined = models.DateTimeField(auto_now_add=True, help_text="Date when the user profile was created")
is_active = models.BooleanField(default=True, help_text="Designates whether this user should be treated as active.")
def__str__(self):
returnself.username
classMeta:
ordering = ['-date_joined']
verbose_name = "User Profile"
verbose_name_plural = "User Profiles"
then you’ll probably need to add some serializers
from rest_framework import serializers
from .models import UserProfile
classUserProfileSerializer(serializers.ModelSerializer):
classMeta:
model = UserProfile
fields = ['id', 'username', 'email', 'full_name', 'date_joined', 'is_active']
read_only_fields = ['id', 'date_joined']
defvalidate_username(self, value):
ifnot value:
raise serializers.ValidationError("Username cannot be empty.")
iflen(value) < 3:
raise serializers.ValidationError("Username must be at least 3 characters long.")
# Add any other custom username validation rules herereturn value
defvalidate_email(self, value):
ifnot value:
raise serializers.ValidationError("Email cannot be empty.")
# Django's EmailField already provides good validation,# but you can add more specific rules if needed.return value
then you’ll have to add some views
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import UserProfile
from .serializers import UserProfileSerializer
@api_view(['POST'])defcreate_user_profile(request):
if request.method == 'POST':
serializer = UserProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response({"error": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
next you have to define URL patterns
from django.urlsimport path
from .viewsimport create_user_profile
urlpatterns = [
path('users/create/', create_user_profile, name='create-user-profile'),
]
This is all just a bunch of boilerplate. And with LLMs, you can just give it a sample JSON payload you want and this stuff just happens.
validate_email does not need to exist, since model fields by default have blank=False meaning they must have a value. This should be picked up by ModelSerializer already, since it is using the model.
validate_username doesn’t do anything that couldn’t be accomplished by using MinLengthValidator - and in addition it should not exist in serializers - it belongs directly in the declaration of the field in models.
That way, you are validating this field regardless of it being used for the REST API or the web application
On the subject of your view code:
That whole code can be thrown out and replaced with:
So, honestly I don’t know what to think of your example of “boilerplate” beyond the fact that you don’t quite grasp Django and Django Rest Framework well enough to understand how to implement things properly, without repeating yourself. I also think some of your code like verbose_name and verbose_name_plural is not an example of “boilerplate”. I would also argue that ordering is something that should be implemented via OrderingFilter so that the API client can pick different orderings as part of the GET request.
I think a full example could be reduced to something like:
from django.db import models
classUserProfile(models.Model):
username = models.CharField(
max_length=100,
unique=True,
help_text="Unique username",
validators=[MinLengthValidator(limit_value=3)]
)
email = models.EmailField(unique=True, help_text="User's email address")
full_name = models.CharField(max_length=255, blank=True, null=True, help_text="User's full name")
date_joined = models.DateTimeField(auto_now_add=True, help_text="Date when the user profile was created")
is_active = models.BooleanField(default=True, help_text="Designates whether this user should be treated as active.")
def__str__(self):
returnself.username
from rest_framework import serializers
from .models import UserProfile
classUserProfileSerializer(serializers.ModelSerializer):
classMeta:
model = UserProfile
fields = ['id', 'username', 'email', 'full_name', 'date_joined', 'is_active']
read_only_fields = ['id', 'date_joined']
from rest_framework import viewsets
from rest_framework.permissions import IsAccountAdminOrReadOnly
from .models import UserProfile
from .serializers import UserProfileSerializer
classUserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
permission_classes = [IsAccountAdminOrReadOnly]
So really the only “boilerplate” here is how many of the fields from UserProfile you want to expose via the REST API and how many are read only? That’s not something an LLM can decide, that’s a policy decision.
So really, I wonder what it is that you are expecting an LLM to do? Because this small example doesn’t have much to it and it does a LOT just by leveraging DRF and leaving you to just define your business logic and objects. If you can’t be bothered to even think about your models and what data you are storing, I don’t know what to say to that, beyond the fact that you just don’t want to code even the easiest things yourself?
The point is that the LLM can produce 90% of the code here, and then you might need to tweak a couple of lines. Even with your changes, there’s still very obviously a non trivial amount of boilerplate, and you just can’t bring yourself to acknowledge this fact.
So then you do write a bunch of boilerplate such as HTTP endpoints, database queries, and so on.
Not really. It’s Django and Django Rest Framework so there really isn’t a lot of boilerplate. That’s all hidden behind the framework
I’d argue that most of the code is conceptually boilerplate, even when you have a framework to paper over it. There’s really nothing exciting about declaring an HTTP endpoint that’s going to slurp some JSON, massage it a bit, and shove it n your db. It’s a boring repetitive task, and I’m happy to let a tool do it for me.
What I’m trying to say is that for Django, especially Django Rest Framework, you don’t even declare endpoints.
DRF has a
ModelViewSet
where you just create a class, inherit from MVS and set themodel
to point to your Django ORM model and that’s it.ModelViewSet
already has all the implementation code for handlingPOST
,PUT
,PATCH
andDELETE
.There is no boilerplate.
There isn’t anything that an LLM would add to this process.
I’ve used Django before and I disagree. 🤷
Around which parts of Django? Because Django has generic class based views that do exactly the same thing, where all you do is set the model attribute. Then the generic view class you inherited from has the implementation. Especially if you use a
ModelForm
Here’s what a typical Django enpoint might look like for handling a json payload with some user information and storing it in the db:
from django.db import models class UserProfile(models.Model): username = models.CharField(max_length=100, unique=True, help_text="Unique username") email = models.EmailField(unique=True, help_text="User's email address") full_name = models.CharField(max_length=255, blank=True, null=True, help_text="User's full name") date_joined = models.DateTimeField(auto_now_add=True, help_text="Date when the user profile was created") is_active = models.BooleanField(default=True, help_text="Designates whether this user should be treated as active.") def __str__(self): return self.username class Meta: ordering = ['-date_joined'] verbose_name = "User Profile" verbose_name_plural = "User Profiles"
then you’ll probably need to add some serializers
from rest_framework import serializers from .models import UserProfile class UserProfileSerializer(serializers.ModelSerializer): class Meta: model = UserProfile fields = ['id', 'username', 'email', 'full_name', 'date_joined', 'is_active'] read_only_fields = ['id', 'date_joined'] def validate_username(self, value): if not value: raise serializers.ValidationError("Username cannot be empty.") if len(value) < 3: raise serializers.ValidationError("Username must be at least 3 characters long.") # Add any other custom username validation rules here return value def validate_email(self, value): if not value: raise serializers.ValidationError("Email cannot be empty.") # Django's EmailField already provides good validation, # but you can add more specific rules if needed. return value
then you’ll have to add some views
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from .models import UserProfile from .serializers import UserProfileSerializer @api_view(['POST']) def create_user_profile(request): if request.method == 'POST': serializer = UserProfileSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response({"error": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
next you have to define URL patterns
from django.urls import path from .views import create_user_profile urlpatterns = [ path('users/create/', create_user_profile, name='create-user-profile'), ]
This is all just a bunch of boilerplate. And with LLMs, you can just give it a sample JSON payload you want and this stuff just happens.
Going through as I go:
validate_email
does not need to exist, since model fields by default haveblank=False
meaning they must have a value. This should be picked up byModelSerializer
already, since it is using the model.validate_username
doesn’t do anything that couldn’t be accomplished by usingMinLengthValidator
- and in addition it should not exist inserializers
- it belongs directly in the declaration of the field inmodels
.On the subject of your view code:
That whole code can be thrown out and replaced with:
class UserProfileViewSet(viewsets.ModelViewSet): queryset = UserProfile.objects.all() serializer_class = UserProfileSerializer permission_classes = [IsAccountAdminOrReadOnly]
So, honestly I don’t know what to think of your example of “boilerplate” beyond the fact that you don’t quite grasp Django and Django Rest Framework well enough to understand how to implement things properly, without repeating yourself. I also think some of your code like
verbose_name
andverbose_name_plural
is not an example of “boilerplate”. I would also argue thatordering
is something that should be implemented via OrderingFilter so that the API client can pick different orderings as part of the GET request.I think a full example could be reduced to something like:
from django.db import models class UserProfile(models.Model): username = models.CharField( max_length=100, unique=True, help_text="Unique username", validators=[MinLengthValidator(limit_value=3)] ) email = models.EmailField(unique=True, help_text="User's email address") full_name = models.CharField(max_length=255, blank=True, null=True, help_text="User's full name") date_joined = models.DateTimeField(auto_now_add=True, help_text="Date when the user profile was created") is_active = models.BooleanField(default=True, help_text="Designates whether this user should be treated as active.") def __str__(self): return self.username
from rest_framework import serializers from .models import UserProfile class UserProfileSerializer(serializers.ModelSerializer): class Meta: model = UserProfile fields = ['id', 'username', 'email', 'full_name', 'date_joined', 'is_active'] read_only_fields = ['id', 'date_joined']
from rest_framework import viewsets from rest_framework.permissions import IsAccountAdminOrReadOnly from .models import UserProfile from .serializers import UserProfileSerializer class UserProfileViewSet(viewsets.ModelViewSet): queryset = UserProfile.objects.all() serializer_class = UserProfileSerializer permission_classes = [IsAccountAdminOrReadOnly]
from rest_framework import routers router = routers.SimpleRouter() router.register(r'users', UserProfileViewSet) urlpatterns = router.urls
So really the only “boilerplate” here is how many of the fields from
UserProfile
you want to expose via the REST API and how many are read only? That’s not something an LLM can decide, that’s a policy decision.So really, I wonder what it is that you are expecting an LLM to do? Because this small example doesn’t have much to it and it does a LOT just by leveraging DRF and leaving you to just define your business logic and objects. If you can’t be bothered to even think about your models and what data you are storing, I don’t know what to say to that, beyond the fact that you just don’t want to code even the easiest things yourself?
The point is that the LLM can produce 90% of the code here, and then you might need to tweak a couple of lines. Even with your changes, there’s still very obviously a non trivial amount of boilerplate, and you just can’t bring yourself to acknowledge this fact.