Help on Django Rest Framework

urls.py (In my app)

from django.urls import path
from .views import DeviceListView
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
    path('', views.home, name='interface-home'),
    path('device/', DeviceListView.as_view(), name='interface-device'),
    path('device_edit/<int:pk>/', views.device_edit, name='device-edit'),
    path('device_delete/<int:pk>/', views.device_delete, name = 'device-delete'),
    path('device_add/', views.device_add, name='device-add'),
    path('device/port/', views.device_port, name='device-port'),
    path('device/port/selected/<int:pk>', views.device_port_selected, name='device-port-selected'),
    path('device/routeport/', views.device_routeport, name='device-routeport'),
    path('device/routeport/selected/<int:pk>', views.device_routeport_selected, name='device-routeport-selected'),
    path('interface/', views.interface_list, name='interface-list'),
    path('devicelist/', devicelist.as_views()),
]

models.py

class Device(models.Model):
    hostname = models.CharField(max_length=50, unique = True)
    ipaddr = models.GenericIPAddressField(protocol='ipv4', unique=True, verbose_name='mangement IP') ##Use for mgt_id_addr
    date_added = models.DateTimeField(default=timezone.now)
    
    def __str__(self):
        return self.hostname

serializers.py

from rest_framework import serializers
from .models import Device

class DeviceSerializers(serializers.ModelSerializer):
    class Meta:
        model=Device
        fields = '__all__'

views.py

class devicelist(APIView):
    def get (self,request):
        devices = Device.objects.all()
        serializer = DeviceSerializers(devices, many = True)
        return Response(serializer.data)
    def post(self):
        pass

I’m trying out on creating a restful API and i am using the guide : Django Rest Framework | How to Create a RESTful API Using Django | Django Tutorial | Edureka - YouTube. But i am getting a error, NameError: name 'devicelist' is not defined in urls.py. According to the video, I am suppose to put the url in the main urls.py which has the url for admin but it did not work so i tried in the urls.py in my app. It still doesnt not work. I do not get it as i defined the class in view already but it says it is not defined.

I also have another question. In the video, the creator import its models table by from rest_framework import tablename in serializers.py but that didnt work for me for , so I used from.models import Device. Is this correct? Or did i do something wrong?

Hello,

what does your project-level urls.py file look like? Have you included the url from your app with the include function?

Hi,

Let’s start with the easy one. from . models import Device is correct. You are creating a serializer for your model Device and if Device is defined in your local app’s model file, then your import statement is correct.

Regarding the other error, NameError: name 'devicelist' , I think it comes from this line in your urls.py

path('devicelist/', devicelist.as_views()), There is an extra s and the end of as_view()

It should be:

path('devicelist/', devicelist.as_view()), # trailing "s" removed

Additionally, a lot of your path() urls are missing the .as_view() after them.

Last, the from .views import DeviceList isn’t necessary as you have already imported .views.

I would try the following edits:

from django.urls import path
from . import views
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = [
    path('', views.home, name='interface-home'),
    path('device/', DeviceListView.as_view(), name='interface-device'),
    path('device_edit/<int:pk>/', views.device_edit.as_view(), name='device-edit'),
    path('device_delete/<int:pk>/', views.device_delete.as_view(), name = 'device-delete'),
    path('device_add/', views.device_add.as_view(), name='device-add'),
    path('device/port/', views.device_port.as_view(), name='device-port'),
    path('device/port/selected/<int:pk>', views.device_port_selected.as_view(), name='device-port-selected'),
    path('device/routeport/', views.device_routeport.as_view(), name='device-routeport'),
    path('device/routeport/selected/<int:pk>', views.device_routeport_selected.as_view(), name='device-routeport-selected'),
    path('interface/', views.interface_list.as_view(), name='interface-list'),
    path('devicelist/', views.devicelist.as_view()),
]

Cheers,

Conor

I managed to fix the issue! But i got a question, for class in views, there is a need to import it to urls.py? I key in from .views import devicelist under urls.py in the app and it work. Before that, my statement has a extra s. Thanks for identifying that @conor ! I also got some additional question on this serializers.py.

  1. I am currently trying to display multiple model with a primary key to search for specific data but I encounter the following error: selected() got an unexpected keyword argument 'pk'

Heres the code:

views.py

from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import DeviceSerializers, DeviceDetailSerializers
@api_view(['GET'])
def selected(request,pk):
    devices = Device.objects.all(pk=pk)
    devicedetail = DeviceDetail.objects.all(DD2DKEY=pk)
    devserializer = DeviceSerializers(devices, many = True)
    devdserializer = DeviceDetailSerializers(devicedetail, many = True)
    results = devserializer.data + devdserializer.data
    return Response(results)

serializers.py

from rest_framework import serializers
from .models import Device, DeviceDetail

class DeviceSerializers(serializers.ModelSerializer):
    class Meta:
        model=Device
        fields = '__all__'

class DeviceDetailSerializers(serializers.ModelSerializer):
    class Meta:
        model = DeviceDetail
        fields = '__all__'

models.py

class Device(models.Model):
    hostname = models.CharField(max_length=50, unique = True)
    ipaddr = models.GenericIPAddressField(protocol='ipv4', unique=True, verbose_name='mangement IP') ##Use for mgt_id_addr
    date_added = models.DateTimeField(default=timezone.now)
    
    def __str__(self):
        return self.hostname

class DeviceDetail(models.Model):
    
    SUBNET_CHOICES = (
    ('16','16'),
    ('17', '17'),
    ('18','18'),
    ('19','19'),
    ('20','20'),
    ('21', '21'),
    ('22', '22'),
    ('23', '23'),
    ('24', '24'),
    ('25', '25'),
    ('26', '26'),
    ('27', '27'),
    ('28', '28'),
    ('29', '29'),
    ('30', '30'),
    )

    DEV_MODS =(
        ('Catalyst 9606R', 'Catalyst 9606R'),
        ('C9300L-48T-4X', 'C9300L-48T-4X')
    )

    
    mgt_interface = models.CharField(max_length=50)
    subnetmask = models.CharField(max_length=2, choices = SUBNET_CHOICES)
    ssh_id = models.CharField(max_length=50)
    ssh_pwd = models.CharField(max_length=50)
    enable_secret = models.CharField(max_length=50)
    dev_mod=models.CharField(max_length=50, choices = DEV_MODS) ##device_model replacement
    DD2DKEY = models.ForeignKey(Device, on_delete=models.CASCADE) ##The key to link up the tables
    
    def __str__(self):
        return self.hostname

urls.py (In app)

from django.urls import path
from .views import DeviceListView
from . import views
from .views import devicelist
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
    path('', views.home, name='interface-home'),
    path('device/', DeviceListView.as_view(), name='interface-device'),
    path('device_edit/<int:pk>/', views.device_edit, name='device-edit'),
    path('device_delete/<int:pk>/', views.device_delete, name = 'device-delete'),
    path('device_add/', views.device_add, name='device-add'),
    path('device/port/', views.device_port, name='device-port'),
    path('device/port/selected/<int:pk>', views.device_port_selected, name='device-port-selected'),
    path('device/routeport/', views.device_routeport, name='device-routeport'),
    path('device/routeport/selected/<int:pk>', views.device_routeport_selected, name='device-routeport-selected'),
    path('interface/', views.interface_list, name='interface-list'),
    path('devicelist/', devicelist.as_view()),
    path('selecteddevice/<int:pk>', views.selected),
]

Is passing a primary key not allowed for API? Or am i doing something wrong here?

Here is the traceback if anyone needs

Traceback (most recent call last):
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\P1338475\.virtualenvs\django_swing-t91g66f4\lib\site-packages\rest_framework\decorators.py", line 50, in handler
    return func(*args, **kwargs)
TypeError: selected() got an unexpected keyword argument 'pk'
  1. Is it possible to use this rest framework on a created table using raw sql instead of models? I know using raw sql is bad but the raw sql way is the one that can come out with a solution from the task im given and I dont know how use other ways.

For the error i am facing, i tried removing the pk, it works normally by displaying all my data. But with the pk , it goes haywire

Hi Calvin,

Glad to hear that you’re making progress. Before I go further, can I ask if you’re also a network engineer? Until recently the vast majority of my career has been in network engineering and at the moment I’m building a new spine-leaf EVPN DC with NSX-T. What sort of networking tool are you building with your app? If it’s top secret, I completely understand, I’m just curious as I also build a few tools for my customers - often the sort of simple tools which list all devices and can add vlans to all switches and their trunk ports - cheaper than buying the equivalent software from Cisco or Aruba given that most of my clients use about 1% of the functionality. Anyway, I digress!

OK, let’s start with the easy stuff. I see you’re using some choices with SUBNET_CHOICES - nothing wrong with that, but Django >= 3.0 supports ENUM types. @adamchainz has a good blog post on them. I use them everywhere where I have choices in my models.

PK Issue

I think your PK issue is that your syntax is incorrect

    devices = Device.objects.all(pk=pk)  # incorrect
    devicedetail = DeviceDetail.objects.all(DD2DKEY=pk)  # incorrect

To filter results, you must use the filter() method

    devices = Device.objects.all().filter(pk=pk)  # this should work
    devicedetail = DeviceDetail.objects.all().filter(DD2DKEY=pk)  # as should this (if your query params are correct)

Views and multiple models in responses

You absolutely can have multiple models in your views. The way I do this is by using Class Based Views (CBV) + Nested Serializers. I’ll walk you through how I’d do this with your models.

serializers.py

from rest_framework import serializers
from .models import Device, DeviceDetail

class DeviceSerializers(serializers.ModelSerializer):
  # I don't use __all__, but rather declare each attribute. 
  # This is a good practice as it can accidental data leakage.
    class Meta:
        model=Device
        fields = ("id", "hostname","ipaddr", "date_added")  

class DeviceDetailSerializers(serializers.ModelSerializer):
  DD2DKEY = DeviceSerializers()  #  I declare that this model's attribute, DD2KEY, will be serialized by DeviceSerializers
    class Meta:
        model = DeviceDetail
        fields = '__all__'  # I'm lazy and leaving this for you to update if you want

On the subject of serializers, check out the documentation for SerializerMethodField Serializer fields - Django REST framework - they come in very handy

views.py

from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, generics
from . import serializers
from . import models

class DetailDeviceRetrieveView(generics.RetrieveAPIView):
  """
  Use this for retrieving your nested serialized device
  """
    serializer_class = serializers.DetailDeviceSerializers
    permission_classes = [IsAuthenticated]
    model = models.DeviceDetail
    queryset = models.DeviceDetail.objects.all()
    

# a really nice way for full CRUD in DRF is by using viewsets.
# have a read about them. Below is an example of a viewset 
# for full CRUD for a Device
class DeviceViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.DeviceSerializers
    permission_classes = [IsAuthenticated]
    model = models.Device

    # if you ever want to override the queryset, instead of doing it as 
    # it has been done in DeviceDetailRetrieveView, you can include your
    # own querset in get_queryset
    def get_queryset(self):
        return self.model.objects.all()

urls.py

from rest_framework.routers import SimpleRouter
from django.urls import path, include
from . import views

# this is the router for viewsets. We include this in the url patterns
router = SimpleRouter(trailing_slash=False)
router.register("device", views.DeviceViewSet, basename="device)


urlpatterns = [    
  path(
        "selecteddevice/<int:pk>",
        views.DetailDeviceRetrieveView.as_view(),
        name="device_detail",
    ),
  path("", include(router.urls)),
]

Viewsets

Have a read about DRF’s Viewsets. They’re awesome. In short, with the viewset code above in urls.py and views.py you can:

  1. GET all devices at /device
  2. GET a single device at /device/pk
  3. Create a device with POST at /device/pk
  4. DELETE a device at /device/pk
  5. PUT and PATCH a device at /device/pk

Summary

The issue you had was with your filtering - you must use the .filter() method. I recommend you steer clear from raw SQL and use your time to learn how to query to ORM. It’s so powerful and with practice, a pleasure to use. Don’t be afraid to ask questions in this forum, there are plenty of ORM wizards lurking herein (I am not one of them)

Returning mutiple models, i.e. a model and its relationships, is a breeze with DRF and CBV + nested serializers. You can use this same logic to nest 10 model serializers. I’m not saying it’s a good idea, but it is possible.

Viewsets are awesome and worth having a look at.

Some Gotchas

Viewsets for full CRUD work best when you do not nest your serializers. You will run into issues if you try and use the DeviceDetailSerializers with a Viewset. You will be able to get and fetch DeviceDetail but you will run into issues PUTing and POSTing. That is why I have specifically chosen Device as the model to use in my Viewset example. If you want to use nested serializers in a Viewset, use viewsets.ReadOnlyModelViewSet as this will limited you to GET requests, no POST, PATCH, DELETE etc.

In my experience, it’s better to keep your views simple. In other words, instead of trying to do 5 things in one view, think about spreading the 5 things over 2, 3 or 4 views, In my example, I have provided a view to retrieve a DetailDevice which also contains full information about the Device into one view, and then for full CRUD for a Device, I’ve built a separate viewset. Two views, two URLs, but a lot less headache, in my opinion.

Good luck, and please let me know how you get along.

Firstly, a very big thank you for writing all these and also i am sorry as I do not fully understand how to use the way you suggested. Very sorry about that. Also, thank you for telling me to not used fields='__all__ due to accidental data leakage. For now, I am using @api_view[('GET')] to get the content I need. As u have corrected, .all should be replaced with .filter. But what puzzled me is, one of my view for one of my html page. I used .all(pk=pk) and it worked. Other than changing the all to filter, the def selected(request,pk) has a issue with pk. So i replace it with pk=None and followed by if pk !=None and get the data I need.

Secondly, answering your question. I am working as a network engineer but unlike everyone here who are in the working life fully, I am a student doing my intern. I dont think I am using any tool so far. So my knowledge in this is not very good. And I am just currently doing what I have to am request to do. Would really appreciate it if you or the others could help me so I can learn about Django as I myself am interesting in doing stuff like this.

Thirdly, I am trying to avoid using raw SQL. But what I am tasked to do seem to only be able to be done via raw SQL.

Lastly, I am facing another issue right now on API post for @api_view[('POST'). I have the following codes:
models.py

class Job(models.Model):
    datetime = models.DateTimeField(default=timezone.now)
    combinedparameters = models.CharField(max_length = 1000)

serializers.py

class JobSerializers(serializers.ModelSerializer):
    class Meta:
        model = Job
        fields = ['combinedparameters']

As you can see there is only 1 field. But i dont know how to set up my @api_view['(POST)'] to do the same thing as my html because my html for this looks like this:

Upon clicking the save button, I have to get the individual inputs from the textbox, textarea, dropdown box and convert it into the following :
Example -

{'device': 177, 'configuration': {'port_range': 'TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4,TenGigabitEthernet1/0/5', 'port_mode': 'Access', 'port_status': 'Disabled', 'port_param1': 'Test\r\n1\r\n2\r\n3', 'port_param2': 'Test\\n1\\n2\\n3'}}

But how do I do the same thing in my API view if it doesnt follow how my html look like with so much input areas?

Hi Calvin,

Cool to hear that you’re a network engineer diving into Django and Python. I believe it is an excellent choice to learn Python and web frameworks like Django as a network engineer as there is a major change happening in our field at the moment. More and more devices now support reasonably complete API’s and we’re now seeing management systems coming in where they provide a central control-plane for the device’s data-planes. This means one can write code to automate mass configurations without having to hit each individual device. Apologies if this is already obvious to you, but I’m thinking if you’re new to the field, then you might not know what it was like 5 - ten years ago. I think it’s a pretty fun time in the networking game (unless you’re the poor engineer who fat fingered his or her BGP prefix-list yesterday - I feel sorry for him or her, I really do, we’ve all been there.)

Right, back to Django

On the subject of making queries: all() doesn’t accept any parameters according to the docs QuerySet API reference | Django documentation | Django, so I’m not sure what .all(pk) is doing in your case. Regardless, if you want to filter results then you use the filter() method.

Let’s say you want to perform the following SQL query

select * from device where hostname = 'my-lovely-router';

We would do that using .select()

results = Device.objects.all().filter(hostname="my-lovely-router")

Now let’s say you wanted to query all devices with the term “bgp” in their names

select * from device where hostname like '%bgp%';

This is where Django starts to get lovely.

results = Device.objects.all().filter(hostname__icontains="bgp")

Finally, let’t say we only want to get the hostnames of the devices where hostname contains “bgp”

select hostname from device where hostname like '%bgp%';

We do this using the only() method.

results = Device.objects.all().filter(hostname__icontains="bgp").only("hostname")

Without trying to sound like a broken record, I really do recommend you try to learn the basics of the ORM. It is wonderfully powerful.

Last, let’s look at querying the DetailDevice. We want to get all DetailDevice where its DD2DKEY’s hostname contains “bgp”

results = Device.objects.all().filter(DD2DKEY__hostname__icontains="bgp").only("hostname")

In my opinion, that is a delightful way of writing an SQL join.

For raw SQL, read the docs: Performing raw SQL queries | Django documentation | Django

On using __all__ in your serializers, it’s a best practice, but not gospel. It can be handy if you are subclassing serializers.

class MyRootSerializer(serializers.ModelSerializer):
  class Meta:
    model = Device
    fields = ("field1", "field2", "field3")
    
class MyChildSerializer(MyRootSerializer):  # I'm subclassing from MyRootSerializer
  some_attribute = serializers.SerializerMethodField()
  
  class Meta:
    model = Device
    fields = "__all__"  # use all the fields defined in my super class
  
  @staticmethod
  def get_some_attributes(obj)
    return obj.__str()__

Regarding your new issue, I’m not really sure I follow what you are asking. Are you using a javascript frontend, hence using JSON responses provided by DRF, or are you using Django and its templates?

Can you please provide the full code for your models, serializers, urls and views. That makes it much easier to help.

Cheers,

Conor

Thanks for the detail explanation. I understand the concept that you are trying to teach me. But the raw SQL statement I am using is creating tables. This is because the task assign to me was to create new tables on the go after each successful creation of a device. Due to the tables being created from raw sql, i am unable to use all the different function like .filter() and such which i am forced to use raw sql statements. And also apologies about .all issue. I re-read my code and realize i was using .get(pk=pk).
For the new issue, I will explain my html from scratch. So i have 2 pages.
The 1st page looks like this :


Upon selecting the hostname and press Go, the 2nd page appears as:

Within the HTML of this page, those drop down box, text areas are by itself. It is not linked to any django form function.
A explanation on how user are suppose to use this page,
From Port ID and To Port ID contains the interfaces register. Such as TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2 and more. So lets say for example, user select TenGigabitEthernet1/0/1 for From Port ID and TenGigabitEthernet1/0/5 for To Port ID
Within the port range textarea, it will list out the following: TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4TenGigabitEthernet1/0/5. This is done via scripts i coded in the html. Then user are to continue selecting the mode and so on.
Upon saving, here is what happen:
Within the textarea of Port Parameter 1 and Port Paramenter 2, if the user keys in multiple lines, the lines will be replaced with \n.
So for example user keys in :

switch access vlan1
switch trunk native vlan 5

It has to be changed on my side to

switch access vlan 1\nswitch trunk native vlan 5

Moving on to the view side,
views.py

def device_port_selected(request, pk):
    devices = Device.objects.get(pk=pk)        
    tablename = 'dev_interface_'+str(pk)
    cursor=connection.cursor()
    cursor.execute(f"SELECT interface FROM {tablename} WHERE id >=2")
    righttable = cursor.fetchall()
    cursor.close()        
    if request.method == "POST":
        job = JobForm(request.POST)
        if job.is_valid():        
            hostname = devices.id
            new_job = job.save(commit=False)         
            selection=request.POST.get('portrange','')                       
            mode=request.POST.get('portmode','')                          
            status=request.POST.get('portstatus','')                        
            portpara1 = request.POST.get('portpara1','')               
            portpara2 = request.POST.get('portpara2','')             
            if selection == "":
                messages.warning(request, "Please select the ports that you want to configure")                    
                return render(request, 'interface/device_port_selected.html',{'devices':devices, 'righttable':righttable, 'job':job} )    
            combined={"port_range":selection, "port_mode":mode, "port_status":status, "port_param1":portpara1, "port_param2": portpara2} 
            combinedfinal = {"device":hostname, "configuration":combined}            
            job.combinedparameters=str(combinedfinal)          
            new_job.combinedparameters = job.combinedparameters
            new_job.save()           
            return redirect('/device/', {'device':Device.objects.all, 'devices':device})
        else:
            print(job.errors)
            return render(request, 'interface/device_port_selected.html',{'devices':devices, 'righttable':righttable} )
    else:
        job=JobForm()      
    return render(request, 'interface/device_port_selected.html',{'devices':devices, 'righttable':righttable, 'job':job} )

As you can see in the views part. I am requesting individual textareas and textinput data to variables. With those variables, I need to further change it to a format I am requested and the format is

{'device': 177, 'configuration': {'port_range': 'TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4,TenGigabitEthernet1/0/5', 'port_mode': 'Access', 'port_status': 'Disabled', 'port_param1': 'Test\\n1\\n2\\n3', 'port_param2': 'Test\\n1\\n2\\n3'}}

The above format is then saved to my table under combinedparameters.
models.py

class Job(models.Model):
    datetime = models.DateTimeField(default=timezone.now)
    combinedparameters = models.CharField(max_length = 1000)

So the task I am asked to do now is to do the exact same thing but now using API. To start: I create the serializer first which i learnt.
serializers.py

class JobSerializers(serializers.ModelSerializer):
    class Meta:
        model = Job
        fields = ['combinedparameters']

Moving on to view, in this case, it will be POST instead of GET or DELETE or PUT. So code is:
views.py

@api_view[('POST')]
def createjob (request, pk=None)
   if pk !=None:

I have no idea how to continue because in the previous GET API i made, it is using linking to a model and to its fields. So like example for my device model:

class Device(models.Model):
    hostname = models.CharField(max_length=50, unique = True)
    ipaddr = models.GenericIPAddressField(protocol='ipv4', unique=True, verbose_name='mangement IP') ##Use for mgt_id_addr
    date_added = models.DateTimeField(default=timezone.now)
    def __str__(self):
        return self.hostname

The API knows one field = one item. Such as it will come out for

    "device": [
        {
            "id": 181,
            "hostname": "Testing321",
            "ipaddr": "1.1.1.2",
            "date_added": "2021-10-05T12:00:29.103527+08:00"
        }
    ]

So going by the same logic of one field = one item. In my POST Api, how do i key in the same amount of inputs like my html when there is only 1 field of combinedparameters. I dont know if I am making sense of this but basically from what i think, within the API if i manage to load it, it will only have one textbox labeled as combineparameters. And that is a problem because I cannot make the user type the long format. I assume the API can make it appear multiple textboxes for user to key is like the html which is guided. Then at the back, it is taking the inputs and converting to the format desired and save it.

For now , I have created the following:
models.py

class Job(models.Model):
    datetime = models.DateTimeField(default=timezone.now)
    combinedparameters = models.CharField(max_length = 1000)

serializers.py

class JobSerializers(serializers.ModelSerializer):
    class Meta:
        model = Job
        fields = ['combinedparameters']

views.py

@api_view(['POST'])
def create_job(request):
    job = Job()
    jobserializer = JobSerializers(job, data = request.data)
    if jobserializer.is_valid():
        jobserializer.save()
        return Response(jobserializer.data, status=status.HTTP_201_CREATED)
    return Response(jobserializer.errors, status=status.HTTP_400_BAD_REQUEST)

Page looks like this:

But if i copy

{'device': 177, 'configuration': {'port_range': 'TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4,TenGigabitEthernet1/0/5', 'port_mode': 'Access', 'port_status': 'Disabled', 'port_param1': 'Test\\n1\\n2\\n3', 'port_param2': 'Test\\n1\\n2\\n3'}}

And click post, got error saying the single quotes have to be double quotes. So i changed it to :

{"device": 177, "configuration": {"port_range": "TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/5", "port_mode": "Access", "port_status": "Disabled", "port_param1": "1\\n2\\n3", "port_param2": "1\\n2\\n3"}}

I clicked post again and this time the following error comes out:
image

I dont understand why is it happening