Redefine functions in views to be as generic as possible

hi,
I didn’t found a similar post so I’ve created this one.

I have a project using Django with DRF as well. There are differents apps in this project :

  • mapping
  • log
  • modulesconf
  • etc

They are using the files reproduced below.

My question is : while everything is working properly, how can I change the views_v1.py to use generic calls. Currently it’s relied only on CpuConf class. But this code from the view needs to be adapted to use all the classes from the model. Moreover, the user may create many instances of the differents classes (3 CANAx, 2 CANEx, etc) from the front or the API as well.
I don’t see how to avoid reproducing as much as methods as there are classes . A scenario like this should be possible :
the user creates an instance of CANEx and this class is passed to the “generic” functions generic_list(CANEx) (instead of the cpuconf_list) .
Same for generic_detail (CANEx) instead of cpuconf_detail.

The main difficulty for me is that methods can be generic but serializer is specific to each class because they don’t share the same informations.

from api/urls.py:

urlpatterns = [
#    path('', CpuAPIView.as_view()),
    url(r'^mapping$', mapping.views.mapping_list),
    url(r'^mapping/(?P<pk>[0-9]+)$', mapping.views.mapping_detail),
    url(r'^mapping/enabled$', mapping.views.mapping_list_enabled),
    url(r'^cpuconf/v1$', modulesconf.views_v1.cpuconf_list),
    url(r'^cpuconf/v1/(?P<pk>[0-9]+)$', modulesconf.views_v1.cpuconf_detail),
    url(r'^cpuconf/v1/enabled$', modulesconf.views_v1.cpuconf_list_enabled),
    url(r'^cpuconf/v2$', modulesconf.views_v2.cpuconf_list),
    url(r'^cpuconf/v2/(?P<pk>[0-9]+)$', modulesconf.views_v2.cpuconf_detail),
    url(r'^cpuconf/v2/enabled$', modulesconf.views_v2.cpuconf_list_enabled)

from modulesconf/models.py:

class CommonData(models.Model):
    eui = models.CharField(unique=True, null=False, max_length=30)
    ip_address = models.GenericIPAddressField(max_length=30,validators=[validate_hostname])
    subnet = models.GenericIPAddressField(max_length=30,validators=[validate_hostname])
    mac= models.CharField(max_length=30)
    bauds =  models.PositiveIntegerField()
    data_bits = models.PositiveSmallIntegerField()
   stop_bits = models.PositiveSmallIntegerField()
    parity = models.PositiveSmallIntegerField()

    class Meta:
        abstract = True

# CPU settings
class CpuConf(models.Model):
    eui = models.CharField(unique=True, null=False, max_length=30)
    hostname = models.CharField(max_length=30,validators=[validate_hostname])
    ip_address = models.GenericIPAddressField(validators=[validate_ipv4])
    subnet = models.GenericIPAddressField(validators=[validate_ipv4])
    dns = models.GenericIPAddressField(validators=[validate_ipv4])
    ntp_address = models.GenericIPAddressField(validators=[validate_ipv4])

    def __str__(self):
        return str(self.ip_address)

class CANAx(models.Model):
    eui = models.CharField(unique=True, null=False, max_length=30)
    ......etc)

class CANEx(models.Model):
    eui = models.CharField(unique=True, null=False, max_length=30)
     ......etc)

class CANCx(models.Model):
    eui = models.CharField(unique=True, null=False, max_length=30)
    ......etc)

class ModuleRA(CommonData):
    pass


class ModuleRE(CommonData):
    pass

class ModuleRC(CommonData):
    acq_network = models.PositiveSmallIntegerField()
    network_type = models.CharField(max_length=30)
    enabled = models.BooleanField()

from modulesconf/views_v1.py:


api = NinjaAPI(version='1.0.0')

# Create a view for web
class CpuConfListView(ListView):
    model = CpuConf
    template_name = 'cpuconf_list.html'

# === Create dedicated methods for API calls ===

# GET list of cpuconfs, POST a new cpuconf, DELETE all cpuconfs
@api_view(['GET', 'POST', 'DELETE'])
def cpuconf_list(request):
    if request.method == 'GET':
            cpuconfs = CpuConf.objects.all()

            # Check if the UID exists
            eui_value = request.query_params.get('eui',None)
            if eui_value is not None:
                cpuconfs = cpuconfs.filter(eui__contains = eui_value)

            cpuconfs_serializer = CpuConfSerializer(cpuconfs, many=True)
            return JsonResponse( cpuconfs_serializer.data, safe=False)

    elif request.method == 'POST':
            cpuconf_data = JSONParser().parse(request)
            cpuconf_serializer = CpuConfSerializer(data=cpuconf_data)
            if cpuconf_serializer.is_valid():
                cpuconf_serializer.save()
                return JsonResponse(cpuconf_serializer.data, status=status.HTTP_201_CREATED)
            return JsonResponse(cpuconf_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
            count = CpuConf.objects.all().delete()
            return JsonResponse({'message': '{} the cpu configurations have been deleted successfully!'.format(count[0])}, status=status.HTTP_204_NO_CONT
ENT)

# GET / PUT / DELETE cpuconf by its ‘id’
@api_view(['GET', 'PUT', 'DELETE'])
def cpuconf_detail(request, pk):
    try:
        single_cpuconf = CpuConf.objects.get(pk=pk)
    except CpuConf.DoesNotExist:
        return JsonResponse({'message': 'The CPU configuration does not exist'}, status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        cpuconf_serializer = CpuConfSerializer(single_cpuconf)
        return JsonResponse(cpuconf_serializer.data)

    elif request.method == 'PUT':
        cpuconf_data = JSONParser().parse(request)
        cpuconf_serializer = CpuConfSerializer(single_cpuconf, data=cpuconf_data)
        if cpuconf_serializer.is_valid():
            cpuconf_serializer.save()
            return JsonResponse(cpuconf_serializer.data)
        return JsonResponse(cpuconf_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        single_cpuconf.delete()
        return JsonResponse({'message': 'The CPU configuration was deleted successfully!'}, status=status.HTTP_204_NO_CONTENT)

from api/serializers.py:


from rest_framework import serializers
from modulesconf.models import CpuConf

class CpuConfSerializer(serializers.ModelSerializer):
    class Meta :
        model = CpuConf
        fields= ('id',
                'eui',
                'hostname',
                 'ip_address',
                 'subnet',
                 'dns',
                 'ntp_address' )

I’m not sure this is really going to address what you’re trying to accomplish, but in Python, you can pass classes and functions as parameters.

e.g.

def get_objects(SomeClass):
    return SomeClass.objects.all()

def show_objects():
    users = get_objects(User)
    group = get_objects(Group)

Beyond that, I’m not sure I really understand exactly what you’re trying to accomplish.

First of all Ken : thanks for the answer.
While I know how to pass a class as a function parameter It’s less obvious to me when it comes to Django.
This is indeed tricky to explain -and maybe I’m not so clear as well- so it’s quite normal that you don’t understand all .

In a1st time I’d like to know how to create different instances (this is where I Python differs from Django to my understanding) of a same Class. Let say the API receive a request for 2 instances of CpuConf Class ?
I think that the code coming from the frontend won’t differs that much.

Django is Python. (Or, to be more accurate, Django is written in Python) Every facility you have available in Python is still available to you in Django.

In exactly the same manner as you would create two instances of any class:

cpu_conf_1 = CpuConf(...)
cpu_conf_2 = CpuConf(...)

I think I make a confusion.
This is not about classes but the “instances” of a model in the database maybe ?
I’m saying this because if you look at this page I don’t see where (and this is maybe the information I’m getting confused with ) the object is created.
In the tutorials / views.py

@api_view(['GET', 'POST', 'DELETE'])
def tutorial_list(request):
......
elif request.method == 'POST':
     if tutorial_serializer.is_valid():
            tutorial_serializer.save()

So when I create a “tutorial” it’s in the db, not a class instance right ?
If yes, I have to query the object for each time I need to access it accordingly.

There are a number of different objects involved here.

A serializer is an object that can either take one or more objects and convert them to a JSON representation (serializing) or take a JSON representation of an object and create one or more instances of an object (deserializing). In the case of a Model Serializer, it will also save those instances to a database.

So in the specific code you’ve shown here:

The save method of the serializer will save all instances of the models submitted to it.

What do you mean by “create a tutorial” here in this context?

The save method of the serializer will save all instances of the models submitted to it.

So this confirms my confusion at start.

What do you mean by “create a tutorial” here in this context?

The code of the website I’ve provided is a Django app that use tutorials objects .

Indeed, and it was the 2 part of my question : when the demand comes not from an API request but from the form.

Just for clarity : in the context of my example it would be “CpuConf” instead of “Tutorials

So where does this leave you now with your questions? Are there still issues for which you are looking for answers?

Trying to address the last point you raised:

Forms do work differently than API requests on a number of different levels. If you’re not comfortable with the concepts of either one, trying to work on both of them together is only going to confuse the situations.

In a way I didn’t exepected, you helped me with clarifications.
I think I’m going to deep documentation and Django concepts a little more .

Thanks

1 Like