Our blazing fast Grid component built with pure JavaScript


Post by scottlin »

Dear Sirs,

With proper readUrl configuration in ajaxstore, the backend django rest framework datasets can be display in grid component now.

However, while I update the first record country column from "U.S." to "U.S.A.", the update action is failed with status 400 bad request.

I'm not quite sure whether it is happend at the backend django rest framework or the frontend bryntum process the response.

See below the error message in detail:

Failed to load resource: the server responded with a status of 400 (Bad Request)
action
: 
"commit"
changes
: 
added
: 
Array(0)
length
: 
0
[[Prototype]]
: 
Array(0)
modified
: 
Array(1)
0
: 
e
data
: 
{id: '3', country: 'U.S.A.', population: 326766748, yearlychange: 0.0071, netchange: 2307285, …}
response
: 
Response
parsedJson
: 
country
: 
Array(1)
0
: 
"This field is required."
length
: 
1
update
: 
action
: 
"update"
body
: 
data
: 
Array(1)
0
: 
{country: 'U.S.A.', id: '3'}
error
: 
TypeError: Cannot read properties of undefined (reading 'forEach') at AjaxStore.processReturnedData (http://127.0.0.1:8000/static/js/bryntum/build/grid.module.js:10:530467) at http://127.0.0.1:8000/static/js/bryntum/build/grid.module.js:10:529239
message
: 
"Cannot read properties of undefined (reading 'forEach')"
stack
: 
"TypeError: Cannot read properties of undefined (reading 'forEach')\n    at AjaxStore.processReturnedData (http://127.0.0.1:8000/static/js/bryntum/build/grid.module.js:10:530467)\n    at http://127.0.0.1:8000/static/js/bryntum/build/grid.module.js:10:529239"
[[Prototype]]
: 
Error
eventName
: 
"afterRequest"
exception
: 
true
exceptionType
: 
"server"
json
: 
{country: Array(1)}
params
: 
undefined
response
: 
Response {parsedJson: {…}, type: 'basic', url: 'http://127.0.0.1:8000/populations/', redirected: false, status: 400, …}
source
: 
AjaxStore {added: StoreBag, removed: StoreBag, modified: StoreBag, idRegister: {…}, internalIdRegister: {…}, …}
type
: 
"afterrequest"
[[Prototype]]
: 
Object
[[Prototype]]
: 
Object

See below the code:

// bryntum_grid_rest.js

import { Grid, AjaxStore, Model } from '../../js/bryntum/build/grid.module.js';

class Population extends Model {
    static fields = ['id', 'country', 'population', 'yearlychange', 'netchange', 'density', 'area', 'migrants', 'fert', 'age', 'urban' ]
}

const store = new AjaxStore({
	modelClass : Population,
	createUrl : "/populations/",
    readUrl : "/populations/",
    updateUrl : "/populations/",
    deleteUrl : "/populations/",
	autoLoad : true,
	autoCommit : true,
});

new Grid({

appendTo : 'container',

features : {
    group : false
},

// Headers will ripple on tap in Material theme
ripple : {
    delegate : '.b-grid-header'
},

columns:[
	{ field:"id", text:"id", width:20 },
	{ field:"country", text:"country", width:100 },
	{ field:"population", text:"population", width:100, type: "number" },
	{ field:"yearlychange",  text:"yearlyChange", width:100, type: "number" },
	{ field:"netchange", text:"netChange", width:100, type: "number"},
	{ field:"density", text:"density", width:80, type: "number"},
	{ field:"area", text:"area", width:80, type: "number"},
	{ field:"migrants", text:"migrants", width:80, type: "number"},
	{ field:"fert", text:"fert", width:80, type: "number"},
	{ field:"age", text:"age", width:80, type: "number"},
	{ field:"urban", text:"urban", width:80, type: "number"},
],

store
});
# quickstart/models.py
from django.db import models

# Create your models here.

class Population(models.Model):
    country = models.CharField(max_length=100)
    population = models.IntegerField()
    yearlychange = models.FloatField()
    netchange = models.IntegerField()
    density = models.IntegerField()
    area = models.IntegerField()
    migrants = models.IntegerField()
    fert = models.FloatField()
    age = models.IntegerField()
    urban = models.FloatField()
# quickstart/serializers.py

from rest_framework import serializers
      
from .models import Population class PopulationSerializer(serializers.HyperlinkedModelSerializer): id = serializers.CharField(required=False) country = serializers.CharField(required=True) population = serializers.IntegerField(required=False) yearlychange = serializers.FloatField(required=False) netchange = serializers.IntegerField(required=False) density = serializers.IntegerField(required=False) area = serializers.IntegerField(required=False) migrants = serializers.IntegerField(required=False) fert = serializers.FloatField(required=False) age = serializers.IntegerField(required=False) urban = serializers.FloatField(required=False) class Meta: model = Population fields = ('id', 'country', 'population', 'yearlychange', 'netchange', 'density', 'area', 'migrants', 'fert', 'age', 'urban')
# quickstart/urls.py

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

from quickstart import views

router = routers.DefaultRouter(trailing_slash=True)
router.register(r'populations', views.PopulationViewSet)

urlpatterns = [
]

urlpatterns += [
	path('', views.index, name='index'),
]

urlpatterns += router.urls
# quickstart/views.py

from django.shortcuts import render

from rest_framework.response import Response
from rest_framework import  viewsets

from .models import Population
from quickstart.serializers import PopulationSerializer


def index(request):
   return render(request, 'quickstart/indexBryntum.html')
    
class PopulationViewSet(viewsets.ModelViewSet): queryset = Population.objects.all() serializer_class = PopulationSerializer def list(self, request): queryset = Population.objects.all() serializer = PopulationSerializer(queryset, many=True, context={'request': request}) return Response(serializer.data)

Post by ghulam.ghous »

Hi Scotllin,

Can you please share a complete project here so we can run it and see what's wrong? I understand you have shared the code here but it would be great if you can share a running project. You can upload a zip file containing project or upload the folder to the google drive or github and share accessible link here. Also please share the steps to run it.

Regards,
Ghous


Post by scottlin »

Dear Sirs,

Kindly see the link below, which is the archive of my test case.
However, for a google drive access, an authorization is required.

https://drive.google.com/file/d/1FLg_wmCaWeeTOjfgc3CIedZk00sUDGM-/view?usp=drive_link


Post by alex.l »

Hi,

Not sure we can help with debugging your backend, we do not have Django experts here, only very basic knowledge. But the error you have is returned by backend, isn't it? Why do you think you need to do something with our components or API? Did you check Django related forums?
https://stackoverflow.com/questions/19875789/django-gives-bad-request-400-when-debug-false
https://code.djangoproject.com/ticket/27760

What do you see in Network tab as request and response?

All the best,
Alex


Post by scottlin »

Please check the following steps:

  1. Install Mini Conda

https://docs.conda.io/projects/miniconda/en/latest/index.html#quick-command-line-install

  1. Create Virtual Environment

conda create -n gantt python=3.10

  1. Activate Virtual Environment in Shell

conda activate gantt

  1. Install Packages

pip install django==4.2
pip install djangorestframework

  1. Unzip the archive using WinRAR

  2. Run Django

cd tutorial
python manage.py runserver

  1. Run Browser

127.0.0.1:8000


Post by alex.l »

Thanks for steps, please reply to my questions above.

All the best,
Alex


Post by gareth dwyer »

@scottlin we've made an example application using Django Rest Framework here that should help you https://replit.com/@rideamtech/brygrid.

In short, you were missing some configuration steps on both the Django side and the Bryntum side.

  • On the Django side, you need to take into account whether fields are required and handle that if that data is not passed through with the update from the Bryntum side
  • On the Bryntum side, you need to configure the httpMethods, and also link the itemIds passed through from Django.

HTH! We're working on turning this into a complete Django Rest Framework guide that will be coming to the official documentation soon.

const store = new AjaxStore({
	modelClass : Population,
	createUrl : "/populations/",
    readUrl : "/populations/",
    updateUrl : "/populations/",
    deleteUrl : "/populations/",
	autoLoad : true,
	autoCommit : true,
	useRestfulMethods: true,
	httpMethods: {
		read: "GET",
		create: "POST",
		update: "PATCH",
		delete: "DELETE",
	},
	listeners: {
		// change request body as needed
		beforeRequest: (event) => {
			if (event.action === "create") {
				const newItem = event.body.data[0];
				delete newItem.id;
				event.body = newItem;
			}
			if (event.action === "update") {
				const updatedItem = event.body.data[0];
				const itemId = updatedItem.id;
				delete updatedItem.id;
				event.body = updatedItem;
				store.updateUrl = `/populations/${itemId}/`;
			}
			if (event.action === "delete") {
				const itemIds = event.body.ids;
				event.body = itemIds;
				store.deleteUrl = `/populations/${itemIds[0]}/`;
			}
		},
	},
});

Post Reply