• footfaults@lemmygrad.ml
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    17 days ago

    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.

      • footfaults@lemmygrad.ml
        link
        fedilink
        English
        arrow-up
        2
        ·
        17 days ago

        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

        • ☆ Yσɠƚԋσʂ ☆@lemmygrad.mlOP
          link
          fedilink
          arrow-up
          6
          ·
          17 days ago

          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.

          • footfaults@lemmygrad.ml
            link
            fedilink
            English
            arrow-up
            3
            ·
            edit-2
            17 days ago

            Going through as I go:

            • 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:

            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 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
            
            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?

            • ☆ Yσɠƚԋσʂ ☆@lemmygrad.mlOP
              link
              fedilink
              arrow-up
              3
              ·
              17 days ago

              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.

              • footfaults@lemmygrad.ml
                link
                fedilink
                English
                arrow-up
                2
                arrow-down
                2
                ·
                17 days ago

                , there’s still very obviously a non trivial amount of boilerplate,

                Where?

                Where is the boilerplate?

                • ☆ Yσɠƚԋσʂ ☆@lemmygrad.mlOP
                  link
                  fedilink
                  arrow-up
                  3
                  ·
                  17 days ago

                  That whole code is boilerplate that I originally generated using an LLM from the following query write a django endpoint for handling a json payload with user information and storing it in the db. After you making it concise, it’s still over 40 lines of code.

                  • footfaults@lemmygrad.ml
                    link
                    fedilink
                    English
                    arrow-up
                    2
                    arrow-down
                    4
                    ·
                    edit-2
                    17 days ago

                    Three thoughts.

                    Firstly: write a django endpoint for handling a json payload with user information and storing it in the db

                    And yet this LLM failed to consider just using the built in contrib.auth.User which already stores this information. What about extending the Auth user model?

                    Secondly:

                    This means that any of this supposed “boilerplate” (you are not even using the right term, more on that in a second) is just AI slop that doesn’t even do a half decent job of what you have been arguing, around getting rid of “boilerplate” that is inconvenient. It is the LLM itself that is creating all this supposed boilerplate!.

                    Thirdly:

                    I don’t understand what you think “boilerplate” is. Because 40 lines of code where you define a model, the fields that are serialized through the REST API, and the REST API implementation is not boilerplate. Boilerplate is defined as “sections of code that are repeated in multiple places with little to no variation.” (Wikipedia). Where is the “repeated in multiple places” in this example? If we even took this example further and extended it to other models - where would the duplication be? The fact that you inherit from ModelViewSet? That you inherit from ModelSerializer? There is no boilerplate here! If anything the boilerplate is inside ModelSerializer and ModelViewSet, but that’s object oriented design! You’re inheriting from those classes so you don’t have to do it yourself!.