drf-react-by-schema 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,22 +1,75 @@
1
1
  # DRF and React By Schema - JS
2
2
 
3
- *This Package will offer out-of-the-box Components, Providers and other tools to make it a breeze to build editable tables and complete forms, completely integrated to Django Rest Framework schemas for validations and widgets, and all CRUD operations working, based diractly, primarily and automatically on the Model, Viewsets and Serializers built in Django.*
3
+ *This package, with the corresponding Django package with the same name, enables a django headless infrastructure for running with react very easily, directly from your models.*
4
4
 
5
5
  ## About
6
6
 
7
- Working with forms and CRUD operations in React having Django as the Backend Server can be too "manual" and prone for errors.
7
+ React has great power in building highly responsive and dynamic interfaces for users, with great flexibility and quality.
8
8
 
9
- There are no simple ways to make things more integrated among both systems.
9
+ On the other hand, Django offers a consolidated infrastructure, high performance, and a popular way to organize data models for creating, removing, and updating data (CRUD) for simple to highly complex and intricate systems.
10
10
 
11
- There will be two solutions: one for react (this very one!) and one for Django, with the same name (still not released).
11
+ A recognized and active library in Django, called Django Rest Framework, or simply DRF, leverages these Django characteristics to allow developers to create powerful API schemas directly related to the data model, using the concept of `viewsets` (analogous to `views`) and `serializers` (analogous to `forms`).
12
12
 
13
- **Warning:** This is still a work in progress! At the moment, only some of the components are built, besides the Provider Component.
13
+ Building systems with a large number of forms for managing different processes is Django's power, but its approach to the interface, based on `views`, `forms`, and especially `templates`, `templatetags`, and `inclusiontags`, is outdated, as every update must refresh the entire page, following a logic of HTML template rendering.
14
14
 
15
- For `drf-react-by-schema` to work, you will have to install other important packages, as MUI, YUP, among others (listed as Peer Dependencies in `package.json`).
15
+ An attractive option is to use React as the client (interface) for a Django instance with DRF as the server. In other words, Django serves the data and performs operations on the data as a Headless server (i.e., without `views`, `forms`, or `templates`, but only through the API).
16
16
 
17
- In the next weeks, we hope to offer full Documentation, guides to install, and other useful informations for using it.
17
+ However, a common problem with this approach is that it often requires duplicating work in the client (React) and the server (Django/DRF) to handle form interactions and validations in React that are related to the server models.
18
18
 
19
- For now, if you have interest in using this solution or helping to develop, send us an e-mail at dtygel@eita.org.br or create issues directly at our repository.
19
+ As an example, let's suppose that you want to manage products. Let's say that each product belongs to a product category. Let's assume we have the categories "fruits and vegetables" and "grains," and the products lettuce, papaya, and grapes in the "fruits and vegetables" category, and beans, rice, and fava beans in the "grains" category. In this example, we could consider that each product has attributes such as "name", "description", "unit", "price per unit," and "availability". For the categories, we could say they have attributes like "name" and "description".
20
+
21
+ In Django, the data model is created very simply: create a `ProductCategory` and a `Product` models, with a "ForeignKey" relationship indicating a one-to-many relationship between the product and the category (one category can have many products, and one product belongs to a single category).
22
+
23
+ In React, you would need to build the form, types of components (simple text input, long text input, numeric input, password input, yes/no switch input, autocomplete multiple-choice, single-choice dropdown, date pickers, etc.), validation conditions (whether it's mandatory or not, initial data, data format masks, etc.).
24
+
25
+ The problem is that this is redundant work because this information is already available in the Django model!
26
+
27
+ But how do you pass the data structure information to React? This is where _DRF and React By Schema - Django_ (`drf-react-by-schema-django`) comes in. It creates a powerful schema on the server that can be accessed by React via the OPTION method.
28
+
29
+ And how do you build forms, validation conditions, and form components in React? This is where _DRF and React By Schema - React_ (`drf-react-by-schema-react`) comes in, which consumes the schemas generated by `drf-react-by-schema-django` and automatically builds all of this for the developer with very few lines of code, while allowing complete customization of the interfaces (layout).
30
+
31
+ Furthermore, on the Django side, `drf-react-by-schema-django` automatically generates a full API endpoint structure directly from the app's models, along with powerful search tools, filters, nested related objects, validation, and handling of object creation, updating, and removal processes, which is a challenging task to do manually in Django DRF because it typically requires in-depth knowledge of `serializers` and `viewsets`. The Django package simplifies this entire process for the developer while maintaining performance.
32
+
33
+ On the React side, `drf-react-by-schema-react` offers a range of powerful components that allow for the automatic construction of forms, including forms for multiple related objects (through editable tables), using the full power of MUI and MUI x-DataGrid.
34
+
35
+ The _DRF and React By Schema_ Suite is available for React at https://www.npmjs.com/package/drf-react-by-schema and for Django at https://pypi.org/project/drf-react-by-schema/.
36
+
37
+ ## Getting started
38
+
39
+ 1. Build the Django model (a guide is available at https://pypi.org/project/drf-react-by-schema/)
40
+
41
+ 2. Create a `frontend` folder, where you will add your React project
42
+
43
+ 3. Install all peer-dependencies of drf-react-by-schema:
44
+ ```
45
+ yarn add @hookform/resolvers@^2.9.10 @mui/icons-material@5.11.0 @mui/lab@^5.0.0-alpha.112 @mui/material@^5.11.0 @mui/x-data-grid@5.17.14 @mui/x-date-pickers@^5.0.11 axios@^0.27.2 moment@^2.29.4 react@^18.2.0 react-dom@^18.2.0 react-hook-form@^7.41.3 react-number-format@^5.1.2 string-mask@^0.3.0 yup@^0.32.11
46
+ ```
47
+
48
+ 4. Install drf-react-by-schema:
49
+ ```
50
+ yarn add drf-react-by-schema
51
+ ```
52
+
53
+ 5. Wrap your app with DRFReactBySchema Provider, adding your custom themes and the Django endpoints. For example, in `App.js`:
54
+ ```
55
+ export default function App() {
56
+ return (
57
+ <DRFReactBySchemaProvider
58
+ serverEndPoint={{
59
+ url: 'http://localhost:8000',
60
+ apiTokenUrl: 'http://localhost:8000/api-auth/token',
61
+ signUp: 'http://localhost:8000/api-auth/signup/',
62
+ }}
63
+ theme={yourWonderfulTheme}>
64
+ <YourWonderfulApp />
65
+ </DRFReactBySchemaProvider>
66
+ );
67
+ }
68
+
69
+ ```
70
+
71
+
72
+ 6. Now you have the full power of the integration of React and Django! Explore the components to build your app seamlessly, with all validation, form widgets and functionalities built-in.
20
73
 
21
74
  ## Styleguides
22
75
 
package/dist/api.js CHANGED
@@ -388,7 +388,7 @@ const getDataGridColumns = (schema, columnFields = [], hiddenFields = [], creata
388
388
  });
389
389
  };
390
390
  const getAutoComplete = ({ model, serverEndPoint, }) => __awaiter(void 0, void 0, void 0, function* () {
391
- const data = yield getData({ path: model, serverEndPoint, route: 'autocomplete' });
391
+ const data = yield getData({ path: `${model}/`, serverEndPoint, route: 'autocomplete' });
392
392
  if (data === false) {
393
393
  return false;
394
394
  }
@@ -471,7 +471,7 @@ const isLoggedIn = (serverEndPoint) => __awaiter(void 0, void 0, void 0, functio
471
471
  if (!token) {
472
472
  return false;
473
473
  }
474
- const usuaria = yield getData({ path: 'minhaconta', serverEndPoint });
474
+ const usuaria = yield getData({ path: 'minhaconta/', serverEndPoint });
475
475
  if (!usuaria) {
476
476
  console.log('Erro ao recuperar dados de usuária!');
477
477
  return false;
@@ -531,13 +531,13 @@ const signUp = (data, serverEndPoint) => __awaiter(void 0, void 0, void 0, funct
531
531
  exports.signUp = signUp;
532
532
  const getGenericModelList = ({ model, serverEndPoint, id = '', relatedModel = '', relatedModelId = '', columnFields, hiddenFields = ['id'], creatableFields = [], isInBatches = false, loadedSchema, loadedModelOptions, page, filter, sort, sumRows, }) => __awaiter(void 0, void 0, void 0, function* () {
533
533
  let path = `${model}/${id}`;
534
- let schemaPath = model;
534
+ let schemaPath = `${model}/`;
535
535
  let schema = loadedSchema;
536
536
  let modelOptions = loadedModelOptions;
537
537
  let columns;
538
538
  if (!(0, utils_1.isTmpId)(id) && relatedModel) {
539
- path += `/${relatedModel}/${relatedModelId}`;
540
- schemaPath += `/${id}/${relatedModel}`;
539
+ path += `/${relatedModel}/${relatedModelId}/`;
540
+ schemaPath += `${id}/${relatedModel}/`;
541
541
  }
542
542
  const queryParams = [];
543
543
  // SERVER-SIDE TOTALS (sumRows):
@@ -638,10 +638,13 @@ const getGenericModelList = ({ model, serverEndPoint, id = '', relatedModel = ''
638
638
  exports.getGenericModelList = getGenericModelList;
639
639
  const getGenericModel = ({ model, serverEndPoint, id = '', relatedModel = '', relatedModelId = '', }) => __awaiter(void 0, void 0, void 0, function* () {
640
640
  let path = `${model}/${id}`;
641
- let schemaPath = model;
641
+ let schemaPath = `${model}/`;
642
642
  if (id && relatedModel) {
643
- path += `/${relatedModel}/${relatedModelId}`;
644
- schemaPath += `/${id}/${relatedModel}`;
643
+ path += `/${relatedModel}/${relatedModelId}/`;
644
+ schemaPath += `${id}/${relatedModel}/`;
645
+ }
646
+ else if (id) {
647
+ path += '/';
645
648
  }
646
649
  const options = yield getSchema(schemaPath, serverEndPoint);
647
650
  if (!options) {
@@ -659,8 +662,7 @@ const getAllModels = (serverEndPoint) => __awaiter(void 0, void 0, void 0, funct
659
662
  if (!serverEndPoint) {
660
663
  return [];
661
664
  }
662
- const path = 'endpoints';
663
- const data = yield getData({ path, serverEndPoint });
665
+ const data = yield getData({ path: 'endpoints/', serverEndPoint });
664
666
  return data || [];
665
667
  });
666
668
  exports.getAllModels = getAllModels;
@@ -96,7 +96,7 @@ const EditableAutocompleteFieldBySchema = react_1.default.forwardRef((_a, ref) =
96
96
  : schema[name].help_text || '',
97
97
  };
98
98
  if (!schema[model].disabled && onEditModel && optionsModel) {
99
- return (react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: name, render: ({ field }) => (react_1.default.createElement(Autocomplete_1.default, Object.assign({ key: name }, field, { id: name, options: options, disabled: options.length === 0 || schema[model].disabled === true, autoHighlight: true, isOptionEqualToValue: (option, value) => option.id === value.id, fullWidth: true, multiple: multiple, sx: sx, onChange: (e, value) => {
99
+ return (react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: name, render: ({ field }) => (react_1.default.createElement(Autocomplete_1.default, Object.assign({ key: name }, field, { id: name, options: options, disabled: schema[model].disabled === true, autoHighlight: true, isOptionEqualToValue: (option, value) => option.id === value.id, fullWidth: true, multiple: multiple, sx: sx, onChange: (e, value) => {
100
100
  let valueAr = value;
101
101
  if (!multiple) {
102
102
  valueAr = [value];
@@ -196,7 +196,7 @@ const EditableAutocompleteFieldBySchema = react_1.default.forwardRef((_a, ref) =
196
196
  }
197
197
  : undefined }))) }));
198
198
  }
199
- return (react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: name, render: ({ field }) => (react_1.default.createElement(Autocomplete_1.default, Object.assign({ key: name }, field, { id: name, options: options, disabled: options.length === 0 || schema[model].disabled === true, autoHighlight: true, isOptionEqualToValue: (option, value) => option.id === value.id, fullWidth: true, multiple: multiple, sx: sx, onChange: (e, value) => {
199
+ return (react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: name, render: ({ field }) => (react_1.default.createElement(Autocomplete_1.default, Object.assign({ key: name }, field, { id: name, options: options, disabled: schema[model].disabled === true, autoHighlight: true, isOptionEqualToValue: (option, value) => option.id === value.id, fullWidth: true, multiple: multiple, sx: sx, onChange: (e, value) => {
200
200
  field.onChange(value);
201
201
  }, renderInput: (params) => (react_1.default.createElement(TextField_1.default, Object.assign({}, params, { label: label, required: schema[model].required, margin: "normal", error: error, helperText: helperText }, other))), getOptionLabel: getOptionLabel, renderOption: renderOption }))) }));
202
202
  });
@@ -37,7 +37,9 @@ const DRFReactBySchemaProvider = ({ serverEndPoint, theme, isInBatches, firstBat
37
37
  if (serverEndPoint[hybridKey]) {
38
38
  continue;
39
39
  }
40
- serverEndPoint[hybridKey] = `${serverEndPoint.url}/${hybridUrl}`;
40
+ serverEndPoint[hybridKey] = ['refreshToken', 'verifyToken'].includes(hybridKey)
41
+ ? `${serverEndPoint.apiTokenUrl}${hybridUrl}/`
42
+ : `${serverEndPoint.url}/${hybridUrl}`;
41
43
  }
42
44
  }
43
45
  const mergedTheme = theme ? Object.assign(Object.assign({}, theme_1.default), theme) : theme_1.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drf-react-by-schema",
3
- "version": "0.7.6",
3
+ "version": "0.8.0",
4
4
  "description": "Components and Tools for building a React App having Django Rest Framework (DRF) as server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,7 +40,10 @@
40
40
  "@babel/preset-typescript": "^7.18.6",
41
41
  "@emotion/react": "^11.10.5",
42
42
  "@emotion/styled": "^11.10.5",
43
+ "@types/jest": "^29.2.4",
44
+ "@types/node": "^18.11.15",
43
45
  "@types/react": "^18.0.26",
46
+ "@types/react-dom": "^18.0.9",
44
47
  "@typescript-eslint/eslint-plugin": "^5.47.0",
45
48
  "@typescript-eslint/parser": "^5.47.0",
46
49
  "eslint": "^8.30.0",
@@ -65,13 +68,6 @@
65
68
  "@mui/material": "^5.11.0",
66
69
  "@mui/x-data-grid": "^5.17.14",
67
70
  "@mui/x-date-pickers": "^5.0.11",
68
- "@rjsf/core": "^5.0.0-beta.15",
69
- "@rjsf/mui": "^5.0.0-beta.15",
70
- "@rjsf/utils": "^5.0.0-beta.15",
71
- "@rjsf/validator-ajv8": "^5.0.0-beta.15",
72
- "@types/jest": "^29.2.4",
73
- "@types/node": "^18.11.15",
74
- "@types/react-dom": "^18.0.9",
75
71
  "axios": "^0.27.2",
76
72
  "moment": "^2.29.4",
77
73
  "react": "^18.2.0",