Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Syncfusion React Grid with Django REST Framework (Custom Binding) + Microsoft SQL Server

A clean, production‑oriented example showing how to connect **Syncfusion React Grid** to **Django REST Framework (DRF)** and **SQL Server** using **Custom Binding**. Supports server‑driven paging, sorting, filtering, searching, and full CRUD with a REST‑native contract (`GET` with query params + `POST/PUT/DELETE` for edits).

## Key Features

- **Custom Binding Pattern**: Manual control of REST requests using `dataStateChange` & `dataSourceChanged`
- **DRF Query-String Model**: Native `page`, `page_size`, `ordering`, `search`, and flexible filter operators
- **SQL Server Integration**: `mssql-django` + `pyodbc` with migrations
- **Advanced Grid Behavior**: Excel-style filtering, multi-select date filtering, paging, sorting, searching
- **Full CRUD**: POST, PUT, DELETE via DRF `ModelViewSet`
- **CORS Ready**: Seamless local React ↔ Django development

## Prerequisites

- **Node.js** LTS (20+) with npm/yarn
- **React** 18+ (Vite)
- **Python** 3.11+
- **Django** 5.2+, **DRF**, **django-filter**, **corsheaders**
- **Microsoft SQL Server** (works with LocalDB or remote instances)

---

## Quick Start

### 1) Clone the repository
```bash
git clone <your-repo-url>
cd <your-project>
```

---

## 2) Backend (Django + DRF + SQL Server)

Create a virtual environment & install packages:

```bash
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
# source .venv/bin/activate

pip install django djangorestframework django-filter django-cors-headers mssql-django pyodbc
```

**Configure SQL Server in `settings.py`:**

```python
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "LibraryDB",
"USER": "django_user",
"PASSWORD": "Django@123",
"HOST": "(localdb)\MSSQLLocalDB",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"trustServerCertificate": "yes",
},
}
}
```

Enable CORS:

```python
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
]
```

Run migrations & start API:

```bash
python manage.py makemigrations
python manage.py migrate
python manage.py runserver 8000
```

API available at:
`http://localhost:8000/api/lendings/`

---

## 3) Frontend (React + Syncfusion Grid + Custom Binding)

Install packages:

```bash
npm install
```

Start the app:

```bash
npm run dev
```

Navigate to `http://localhost:5173`.

### Grid architecture:

- **Reads**: `dataStateChange → fetchLendings(state)` → DRF (`GET /lendings/?page=…&page_size=…&ordering=…`)
- **CRUD**: `dataSourceChanged → POST/PUT/DELETE` → DRF
- **Excel Filter UI**: filter-choice requests receive lightweight `result` arrays

The client uses a shared service (`apiClient.ts`) to convert Grid state into DRF query parameters including:

- `page`, `page_size`
- `ordering` (`author_name,-borrowed_date`)
- `search`
- field filters using operators (`__icontains`, `__gte`, `__lte`, `__in`, etc.)
- CSV date-set filtering using `<field>__in=YYYY-MM-DD,YYYY-MM-DD,…`

---

## Project Layout

| File/Folder | Purpose |
|-------------|---------|
| `django_server/settings.py` | SQL Server, DRF, CORS configuration |
| `django_server/urls.py` | Registers `lendings/` ViewSet |
| `library/models.py` | `BookLending` model |
| `library/serializers.py` | Serializer + UTC Zulu date formatting |
| `library/views.py` | REST ViewSet with ordering/search/filter/paging + `{ result, count }` |
| `client/src/services/apiClient.ts` | Custom Binding request builder & CRUD |
| `client/src/components/OrdersGrid.tsx` | Grid wired to DRF via Custom Binding |
| `client/src/index.css` | Syncfusion Bootstrap 5.3 theme imports |

---

## Common Tasks

### Add
1. Toolbar → **Add**
2. Fill fields → **Save**
Grid triggers `dataSourceChanged → POST`.

### Edit
1. Select row → **Edit**
2. Modify → **Update**
Grid triggers `PUT`.

### Delete
1. Select → **Delete**
2. Confirm
Grid triggers `DELETE`.

### Filtering / Searching / Sorting
- Excel filter menu with operators & date multi-select
- Search bar queries DRF `SearchFilter`
- Header click sorting → `ordering` query param

---

## Troubleshooting

**ODBC / SQL Errors**
- Install **ODBC Driver 18 for SQL Server**
- Ensure driver architecture matches Python (64‑bit recommended)

**CORS failures**
- Ensure `corsheaders` is in `INSTALLED_APPS` + middleware
- Confirm `http://localhost:5173` in `CORS_ALLOWED_ORIGINS`

**Filter/Date issues**
- DRF uses strict ISO formats; serializers convert all dates to `YYYY-MM-DDT00:00:00Z`
- Multi-select dates appear as `<field>__in=2026-01-01,2026-01-05`

**Pagination not working**
- Ensure frontend sends `skip` & `take`
- Backend must read `page` & `page_size`

---
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...

// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,

// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
<title>client</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "client",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@syncfusion/ej2-base": "^32.2.3",
"@syncfusion/ej2-data": "^32.2.3",
"@syncfusion/ej2-react-grids": "^32.2.3",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { registerLicense } from '@syncfusion/ej2-base';
import OrdersGrid from './components/OrdersGrid';
import './App.css';

const licenseKey = (import.meta as { env: Record<string, string | undefined> }).env
.VITE_SYNCFUSION_LICENSE_KEY;

if (licenseKey) {
registerLicense(licenseKey);
}

const App: React.FC = () => {
return (
<div className="app-shell">
<header className="app-header">
<h1>Library Lending Records</h1>
<p>Custom data binding with Syncfusion React Grid and Django REST Framework</p>
</header>
<OrdersGrid />
</div>
);
};

export default App;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading