Sequelize admin panel
TL;DR
ΠΠ°ΠΏΡΡΡΠΈΡΠ΅
git clone https://github.com/eliseevmikhail/sequelize-admin-panel.git
cd sequelize-admin-panel
yarn install && cd demo && yarn install && yarn initdb && yarn start
ΠΈ ΠΎΡΠΊΡΠΎΠΉΡΠ΅ localhost:3000
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
ΠΡΠΎΡΡΠ°Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°
- ΠΡΠΎΡΡΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΠ΅ sequelizeAdmin ΠΊΠ°ΠΊ middleware ΠΏΠΎ ΡΡΠ΅Π±ΡΠ΅ΠΌΠΎΠΌΡ ΠΏΡΡΠΈ
- Π ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ sequelize Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠ°Π±ΠΎΡΠ΅ΠΉ Π°Π΄ΠΌΠΈΠ½-ΠΏΠ°Π½Π΅Π»ΠΈ
- ΠΡΡ ΠΎΠ΄Π½ΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π½Π΅ ΡΡΠ΅Π±ΡΡΡ ΠΌΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ. ΠΡΠ΅ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π½Π°Ρ ΠΎΠ΄ΡΡΡΡ Π² Π½Π°ΡΠ»Π΅Π΄Π½ΠΈΠΊΠ°Ρ ΠΊΠ»Π°ΡΡΠ° ModelAdmin
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ
ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΡΠ½ΠΊΡΠΈΠΉ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π° ΠΏΠΎΠ»Π΅ΠΉ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π² ΡΠΏΠΈΡΠΊΠ΅ Π·Π°ΠΏΠΈΡΠ΅ΠΉ ΠΈ Π½Π° ΡΠΊΡΠ°Π½Π΅ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π·Π°ΠΏΠΈΡΠΈ
ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° ΡΠΈΠΏΠΎΠ² ΠΈ ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠΉ
ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΡΡΡ Π±ΠΎΠ»ΡΡΠΈΠ½ΡΡΠ²ΠΎ ΠΏΡΠΈΠΌΠΈΡΠΈΠ²Π½ΡΡ ΡΠΈΠΏΠΎΠ² Sequelize ΠΈ Π²ΡΠ΅ ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΡ (associations)
ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ ΠΈ ΠΏΡΠ°Π²Π°
ΠΡΡΡΠΎΠ΅Π½Π½ΠΎΠ΅ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΠΌΠΈ ΠΈ ΠΏΡΠ°Π²Π°ΠΌΠΈ Π΄ΠΎΡΡΡΠΏΠ° Π½Π° ΡΠ°Π±Π»ΠΈΡΡ
ΠΠΎΠΊΠ°Π»ΠΈΠ·Π°ΡΠΈΡ
ΠΡΠ΅ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Ρ, Π²ΠΊΠ»ΡΡΠ°Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ, Π½Π°Π·Π²Π°Π½ΠΈΡ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ, ΠΏΠΎΠ»Π΅ΠΉ ΠΈ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡΡΡ Π² Π΅Π΄ΠΈΠ½ΠΎΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΠ΅, ΡΠ°Π·Π±ΠΈΡΠΎΠΌ ΠΏΠΎ Π»ΠΎΠΊΠ°Π»ΡΠΌ. Π‘ΠΈΡΡΠ΅ΠΌΠ° ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ Π½ΡΠΆΠ½ΡΡ Π»ΠΎΠΊΠ°Π»Ρ ΠΏΠΎ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°ΠΌ Π±ΡΠ°ΡΠ·Π΅ΡΠ°. Π ΡΠ»ΡΡΠ°Π΅ ΠΎΡΡΡΡΡΡΠ²ΠΈΡ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Π° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π²ΡΡΡΠΎΠ΅Π½Π½ΡΠΉ Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΈΠΉ Π΄Π»Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ ΠΈΠ»ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈΠ»ΠΈ ΠΏΠΎΠ»Ρ Π΄Π»Ρ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΈ ΠΏΠΎΠ»Π΅ΠΉ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ.
ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΠΏΡΠΎΠ΅ΠΊΡ
mkdir sequelize-admin-demo; cd sequelize-admin-demo
yarn init
yarn add express sequelize sequelize-cli sequelize-admin-panel
yarn add mysql2 # ΠΈΠ»ΠΈ Π΄ΡΡΠ³ΠΎΠΉ Π°Π΄Π°ΠΏΡΠ΅Ρ
node node_modules/.bin/sequelize init
ΠΎΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΡΠΉΡΠ΅ config/config.json
Π΄Π»Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΠΊ Π²Π°ΡΠ΅ΠΉ Π±Π°Π·Π΅ Π΄Π°Π½Π½ΡΡ
, ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ ΡΡΠ°Π·Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ
"define": {"charset": "utf8", "collate": "utf8_general_ci"}
, ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΠΠ
node node_modules/.bin/sequelize db:create
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π³Π»Π°Π²Π½ΡΠΉ ΡΠ°ΠΉΠ» ΠΏΡΠΎΠ΅ΠΊΡΠ° index.js
const express = require('express')
const db = require('./models')
const { sequelizeAdmin } = require('sequelize-admin-panel')
const app = express()
app.use('/admin', sequelizeAdmin(express, db.sequelize))
app.listen(process.env.PORT || 3000, () => console.log('Server started'))
ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠ°ΠΉΠ» cli.js
const db = require('./models')
require('sequelize-admin-panel').cli(db.sequelize)
ΠΎΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, ΡΠ°ΠΊΠ°Ρ ΡΠΎΡΠΌΠ° Π·Π°ΠΏΡΡΠΊΠ° ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ ΡΠΎΠ·Π΄Π°Π½Π½ΡΠΉ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΡΠ°ΠΉΠ» ./models ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅Ρ Π²ΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ. ΠΡΠ»ΠΈ Π²Ρ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΠ΅ sequelize-cli
, ΡΠ±Π΅Π΄ΠΈΡΠ΅ΡΡ ΡΡΠΎ Π½ΡΠΆΠ½ΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°Π½Ρ (Π²ΡΠ·Π²Π°Π½ Sequelize.define).
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΠΈ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΡ sequelize-cli
node node_modules/.bin/sequelize model:generate --name MyModel --attributes name:STRING,count:INTEGER
node node_modules/.bin/sequelize db:migrate
node ./cli init # ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠ°Π±Π»ΠΈΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ
Π»ΠΈΠ±ΠΎ, Π΅ΡΠ»ΠΈ Π²Ρ Π½Π΅ Ρ ΠΎΡΠΈΡΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡΡΡ sequelize migration, Π²ΡΡΡΠ½ΡΡ ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π² ΠΊΠ°ΡΠ°Π»ΠΎΠ³Π΅ models ΡΠ°ΠΉΠ»Ρ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π² ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠΈ Ρ ΡΠ°Π±Π»ΠΎΠ½ΠΎΠΌ:
module.exports = (sequelize, DataTypes) => {
const MyModel = sequelize.define(
'MyModel',
{
// <--- ΠΈΠΌΡ ΠΌΠΎΠ΄Π΅Π»ΠΈ
name: DataTypes.STRING,
count: DataTypes.INTEGER
},
{}
)
MyModel.associate = function(models) {
// associations can be defined here
// like this: MyModel.belongsTo(models.OtherModel)
}
return MyModel
}
ΠΈ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²ΡΠ·ΠΎΠ²ΠΎΠΌ
node ./cli init --all # ΠΎΡΠΈΡΡΠΈΡ Π²ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅!
ΠΠ΅Π΄ΠΎΡΡΠ°ΡΠΊΠΎΠΌ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΡΠΏΠΎΡΠΎΠ±Π° ΡΠ²Π»ΡΠ΅ΡΡΡ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΡ ΡΡΡΠ½ΠΎΠ³ΠΎ ΠΎΠΏΠΈΡΠ°Π½ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΡΡ Π΅ΠΌ ΡΠ°Π±Π»ΠΈΡ Π² ΡΠ°ΠΉΠ»Π°Ρ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ, Π²ΡΠΎΡΠΎΠ³ΠΎ -- ΡΠ±ΡΠΎΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ. ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΎ ΡΠ°Π±ΠΎΡΠ΅ Ρ sequelizejs ΠΏΠΎ ΡΡΡΠ»ΠΊΠ΅.
ΠΡΠΈΠΌΠ΅ΡΠ°Π½ΠΈΠ΅: ΠΏΠΎΠΏΡΡΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Sequelize.sync({alter: true}) ΠΌΠΎΠΆΠ΅Ρ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΡΡ ΠΊ Π΄ΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΠΈ constraints
ΠΠ°ΠΏΡΡΡΠΈΡΠ΅ ΡΠ΅ΡΠ²Π΅Ρ node .
ΠΈ ΠΎΡΠΊΡΠΎΠΉΡΠ΅ Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅ http://localhost:3000/admin
. ΠΠΎΡΠΎΠ²ΠΎ!
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠΉ
ΠΠ»Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π½Π°ΡΠ»Π΅Π΄Π½ΠΈΠΊΠ° ΠΊΠ»Π°ΡΡΠ° ModelAdmin
, Π² ΡΡΠ½ΠΊΡΠΈΠΈ init
Π·Π°Π΄Π°ΠΉΡΠ΅ ΡΡΠ΅Π±ΡΠ΅ΠΌΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ, Π° Π·Π°ΡΠ΅ΠΌ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΠΏΠ°ΡΡ [MyModel,MyModelAdmin]
Π² ΡΡΠ½ΠΊΡΠΈΡ sequelizeAdmin
ΡΠ²ΠΎΠΉΡΡΠ²ΠΎΠΌ models
ΡΡΠ΅ΡΡΠ΅Π³ΠΎ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°.
MyModelAdmin.js
:
const { ModelAdmin } = require('sequelize-admin-panel')
class MyModelAdmin extends ModelAdmin {
repr(req, entry) {
return entry.name
}
init() {
super.init()
this.list_fields = ['id', 'name', 'count', 'nonExistedField']
this.list_links = ['id']
this.search_fields = ['name', 'count']
this.ordering = ['count', '-name']
this.list_per_page = 20
this.editor_fields = ['id', 'name', 'count']
this.readonly_fields = ['id']
this.icon = '<span class="oi oi-media-play"></span>'
this.setFieldDescription('name', {
view: (req, entry, fieldName) => {
return 'I love ' + entry.name + '!'
},
html: false
})
this.setFieldDescription('count', {
// ΠΌΠΎΠΆΠ½ΠΎ ΠΈ Promise
view: (req, entry, fieldName) => {
const model = req.SA.modelAdminInstance.model,
Sequelize = req.SA.Sequelize
// Π΄ΡΡΠ³ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π΄ΠΎΡΡΡΠΏΠ½Ρ ΡΠ΅ΡΠ΅Π· req.SA.modelAdminManager.getModelAdminByModelName('model_name').model
return (
model
.find({
// ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅
attributes: [
[Sequelize.fn('max', Sequelize.col('count')), 'max_count']
]
})
// ΠΏΡΠΎΡΡΠΎΠ΅ ΡΠΊΡΠ°Π½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
.then(entry => parseInt(entry.get('max_count'), 10))
.then(max => `<progress value="${entry.count}" max="${max}">`)
)
},
html: true // ΠΎΡΡΠΎΡΠΎΠΆΠ½ΠΎ -- Π²ΡΠ²ΠΎΠ΄ Π½Π΅ ΡΠΊΡΠ°Π½ΠΈΡΡΠ΅ΡΡΡ
})
// ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ³ΠΎΠ΄Π½ΠΎ ΠΏΡΠ΅Π²Π΄ΠΎ-ΠΏΠΎΠ»Π΅ΠΉ
this.setFieldDescription('nonExistedField', {
view: (req, entry, fieldName) => {
return entry.name.toUpperCase() + ' is great!'
}
})
}
}
module.exports = MyModelAdmin
index.js
:
...
app.use('/admin', sequelizeAdmin(express, db.sequelize, {
models: [ [db.MyModel, require('./MyModelAdmin')] ]
}))
...
Π ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅ ΠΏΠΎΠ»Π΅ name
Π±ΡΠ΄Π΅Ρ ΠΏΡΠΈΠ·Π½Π°Π²Π°ΡΡΡΡ Π² Π»ΡΠ±Π²ΠΈ, count
ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ progressbar ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ Π½Π°ΠΈΠ±ΠΎΠ»ΡΡΠ΅Π³ΠΎ Π² ΡΠ°Π±Π»ΠΈΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ, Π° ΡΡΠ΅ΡΡΠ΅ ΠΏΠΎΠ»Π΅, ΠΎΡΡΡΡΡΠ²ΡΡΡΠ΅Π΅ Π² ΡΠ°Π±Π»ΠΈΡΠ΅, Π·Π°Π½ΠΈΠΌΠ°ΡΡΡΡ Π²ΠΎΡΡ
Π²Π°Π»Π΅Π½ΠΈΠ΅ΠΌ.
Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ ΠΏΠΎ ΠΏΡΠ½ΠΊΡΠ°ΠΌ:
- Π€ΡΠ½ΠΊΡΠΈΡ
repr
ΡΠΎΠ·Π΄Π°ΡΡ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π·Π°ΠΏΠΈΡΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ (ΡΡΡΠΎΠΊΠΈ ΡΠ°Π±Π»ΠΈΡΡ). ΠΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π² ΡΠ°Π±Π»ΠΈΡΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² Π΅ΡΠ»ΠΈ Π½Π΅ Π·Π°Π΄Π°Π½Ρ ΠΏΠΎΠ»Ρ Π²list_fields
, Π° ΡΠ°ΠΊΠΆΠ΅ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠΏΠΈΡΠΊΠΎΠ² ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠΉ. ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅ΡΡΡ ΠΊΠ°ΠΊ plain-text, ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ Promise. - Π‘Π²ΠΎΠΉΡΡΠ²Π°
list_fields
,list_links
,search_fields
,ordering
Π·Π°Π΄Π°ΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ ΠΊΠ°ΠΊΠΈΠ΅ ΠΏΠΎΠ»Ρ Π±ΡΠ΄ΡΡ Π²ΠΈΠ΄Π½Ρ Π² ΡΠ°Π±Π»ΠΈΡΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ², ΠΊΠ°ΠΊΠΈΠ΅ ΠΈΠ· Π½ΠΈΡ ΡΠ²Π»ΡΡΡΡΡ ΡΡΡΠ»ΠΊΠ°ΠΌΠΈ, ΠΏΠΎ ΠΊΠ°ΠΊΠΈΠΌ ΠΎΡΡΡΠ΅ΡΡΠ²Π»ΡΠ΅ΡΡΡ ΠΏΠΎΠΈΡΠΊ ΠΈ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ (Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ Π² ΡΠΏΠΈΡΠΊΠ°Ρ ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠΉ). ΠΠ°Π΄ΠΎ ΠΎΡΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ ΠΏΠΎΠΈΡΠΊ ΠΈ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠ° Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎ ΠΏΡΠΈΠΌΠΈΡΠΈΠ²Π½ΡΡ ΡΠΈΠΏΠ°ΠΌ, ΠΏΡΠΈΡΡΡΡΡΠ²ΡΡΡΠΈΠΌ Π² Π±Π°Π·Π΅ (Ρ.Π΅. Π½Π΅ ΠΏΠΎ associations ΠΈ Π½Π΅ ΠΏΠΎ ΠΈΡΠΊΡΡΡΡΠ²Π΅Π½Π½ΡΠΌ ΠΏΠΎΠ»ΡΠΌ). - ΠΡΠ»ΠΈ Π½ΡΠΆΠ½ΠΎ Π²ΡΠ²Π΅ΡΡΠΈ Π²ΡΠ΅ ΠΏΠΎΠ»Ρ ΠΊΡΠΎΠΌΠ΅ ΡΠΊΠ°Π·Π°Π½Π½ΡΡ
, ΠΏΠ΅ΡΠ΅ΡΠΈΡΠ»ΠΈΡΠ΅ ΠΈΡ
Π²
list_exclude
. ΠΡΠ»ΠΈ Π½ΡΠΆΠ½ΠΎ Π²ΡΠ²Π΅ΡΡΠΈ Π²ΠΎΠΎΠ±ΡΠ΅ Π²ΡΠ΅ ΠΏΠΎΠ»Ρ, ΡΠΊΠ°ΠΆΠΈΡΠ΅ Π²list_exclude
Π½Π΅ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠ΅Π΅ ΠΏΠΎΠ»Π΅.list_fields
ΠΏΡΠΈ ΡΡΠΎΠΌ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ ΠΏΡΡΡΡΠΌ. - Π
list_fields
ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ Π½Π΅ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠ΅Π΅ ΠΏΠΎΠ»Π΅, Π° Π·Π°ΡΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°ΡΡ Π΅Π³ΠΎ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅. - Π
icon
ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ»ΡΠ½ΡΠΉ html, ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ ΠΈΠΊΠΎΠ½ΠΎΡΠ½ΡΠΉ ΡΡΠΈΡΡ.
ΠΠ°Π»Π΅Π΅, Π²ΡΠ·ΠΎΠ²ΠΎΠΌ setFieldDescription
ΠΌΡ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΡΠ½ΠΊΡΠΈΡ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π° ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΏΠΎΠ»Ρ ΡΡΡΠΎΠΊΠΈ ΡΠ°Π±Π»ΠΈΡΡ ΠΈ ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ, ΡΠ»Π΅Π΄ΡΠ΅Ρ Π»ΠΈ Π΅Ρ Π²ΡΠ²ΠΎΠ΄ ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠΈΡΠΎΠ²Π°ΡΡ ΠΊΠ°ΠΊ html. ΠΠ΅ Π·Π°Π±ΡΠ²Π°ΠΉΡΠ΅ ΠΎΠ± ΡΠΊΡΠ°Π½ΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ.
ΠΠ΅Π³ΠΊΠΎ Π·Π°ΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ ΠΊΠ°ΠΆΠ΄ΡΠΉ Π²ΡΠ·ΠΎΠ² view
Π΄Π΅Π»Π°Π΅Ρ ΠΎΠ΄Π½Ρ ΠΈ ΡΡ ΠΆΠ΅ ΡΠ°Π±ΠΎΡΡ ΠΏΠΎ Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΡ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ³ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡ. ΠΡΠ°Π²ΠΈΠ»ΡΠ½ΡΠΌ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ΠΌ Π±ΡΠ΄Π΅Ρ Π²ΡΠ½Π΅ΡΡΠΈ Π΅Ρ Π² ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΠΉ ΠΊΠΎΠ»Π»Π±ΡΠΊ beforeListRender
. ΠΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡΡΡ ΠΊΠ°ΠΊ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° req:
class MyModelAdmin extends ModelAdmin {
...
beforeListRender(req, count, entries) {
const model = req.SA.modelAdminInstance.model,
Sequelize = req.SA.Sequelize
return model.find({
// ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅
attributes: [
[Sequelize.fn('max', Sequelize.col('count')), 'max_count']
]
})
// ΠΏΡΠΎΡΡΠΎΠ΅ ΡΠΊΡΠ°Π½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ ΠΊΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
.then(entry => req.MAX = parseInt(entry.get('max_count'), 10))
}
...
init() {
...
this.setFieldDescription('count', {
view: (req, entry, fieldName) =>
`<progress value="${entry.count}" max="${req.MAX}">`,
html: true
})
...
}
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° Π²ΠΈΠ΄ΠΆΠ΅ΡΠΎΠ²
Π€ΡΠ½ΠΊΡΠΈΠΈ setFieldDescription
ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ widget
, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΡΠΊΠ°Π·Π°ΡΡ ΡΡΠ½ΠΊΡΠΈΡ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π° Π²ΠΈΠ΄ΠΆΠ΅ΡΠ° Ρ ΡΠΈΠ³Π½Π°ΡΡΡΠΎΠΉ (req, entry, fieldName, value, options)
ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡΠ΅ΠΉ html-ΠΊΠΎΠ΄ Π²ΠΈΠ΄ΠΆΠ΅ΡΠ°.
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Π½Π°ΠΏΠΈΡΠ΅ΠΌ Π²ΠΈΠΆΠ΄Π΅Ρ Π΄Π»Ρ ΠΏΠΎΠ»Ρ count
Π² ΡΠΎΡΠΌΠ΅ ΠΏΠΎΠ»Π·ΡΠ½ΠΊΠ°
this.setFieldDescription('count', {
...
widget: (req, entry, fieldName, value, options) =>
`<input type="range" min="0" max="100" step="1"
class="form-control"
${options.readOnly ? 'disabled' : ''}
name=${fieldName}
value=${value} />`
})
ΠΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Π½ΡΠΆΠ½ΠΎ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ name=fieldName
, ΠΆΠ΅Π»Π°ΡΠ΅Π»ΡΠ½ΠΎ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΡΠ΅ΠΊΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ value
ΠΈ ΡΠ»Π°Π³ readonly
. ΠΠ»Π°ΡΡ form-control
ΠΈΠ· bootstrap
ΡΠ°ΡΡΡΠ³ΠΈΠ²Π°Π΅Ρ Π²ΠΈΠ΄ΠΆΠ΅Ρ ΠΏΠΎ ΡΠΈΡΠΈΠ½Π΅ ΠΈ Π² ΠΎΠ±ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ Π½Π΅ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»Π΅Π½.
ΠΡΠ»ΠΈ Π²ΠΈΠ΄ΠΆΠ΅Ρ ΡΠ»ΠΎΠΆΠ½Π΅Π΅ ΠΏΠΎΠ»Ρ Π²Π²ΠΎΠ΄Π°, ΡΠ»Π΅Π΄ΡΠ΅Ρ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠΊΡΡΡΡΠΉ input
ΠΈ ΠΏΠΎΠΌΠ΅ΡΠ°ΡΡ Π² Π½Π΅Π³ΠΎ Π°ΠΊΡΡΠ°Π»ΡΠ½ΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π½Π° ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΎΠΉ ΡΡΠΎΡΠΎΠ½Π΅. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΏΡΡΡΡ ΠΏΠΎΠ»Π΅ point
ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΎ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ:
point: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: '55.75027920193085,37.622483042183035',
}
ΡΠΎΠ³Π΄Π° Π²ΠΈΠ΄ΠΆΠ΅Ρ Ρ ΡΠ½Π΄Π΅ΠΊΡ-ΠΊΠ°ΡΡΠΎΠΉ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠΏΠΈΡΠ°ΡΡ ΡΠ°ΠΊ:
widget: (req, entry, fieldName, value) => {
return `
<!-- hidden form field -->
<input type=hidden name="${fieldName}" value=${value} />
<div style='width: 100%; height: 240px; border: solid black 1px' id="${fieldName}_mapid"></div>
<script>
function ${fieldName}_map() {
var coord = '${value}'.split(',')
var map = new ymaps.Map("${fieldName}_mapid", {
center: coord,
zoom: 7
});
var placemark = new ymaps.Placemark(coord);
map.events.add('click', function (e) {
var coords = e.get('coords');
placemark.geometry.setCoordinates(coords);
// setup hidden form field
document.getElementsByName("${fieldName}")[0].value=coords.join(',')
});
map.geoObjects.add(placemark);
}
ymaps.ready(${fieldName}_map);
</script>`
}
Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ ΡΠ½Π΄Π΅ΠΊΡ ΠΊΠ°ΡΡ Π² ΠΌΠ΅ΡΠΎΠ΄Π΅ init
Π²ΡΠ·ΠΎΠ²ΠΈΡΠ΅
this.addExtraResource(
'<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>'
)
Π»ΠΈΠ±ΠΎ
// js Π² ΠΊΠΎΠ½ΡΠ΅ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌ ΠΏΠ°ΡΡΠ΅ΡΡ
this.addExtraResource('https://api-maps.yandex.ru/2.1/?lang=ru_RU#js')
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΡΠΈ Π»ΡΠ±ΠΎΠΌ Π½Π°ΠΆΠ°ΡΠΈΠΈ Π½Π° ΠΊΠ°ΡΡΡ ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΏΠΎΠΏΠ°Π΄Π°ΡΡ Π² ΡΠΊΡΡΡΡΠΉ input
, ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡΡΡ ΠΏΡΠΈ ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠΈ Π½Π° ΡΠ΅ΡΠ²Π΅Ρ Ρ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠΌ ΠΈΠΌΠ΅Π½Π΅ΠΌ ΠΏΠΎΠ»Ρ. ΠΡΠ΄Π΅Π»ΡΠ½ΠΎ ΡΡΠΎΠΈΡ ΠΎΡΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ Π»ΡΠ±ΡΡ
id
Π»ΡΡΡΠ΅ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ Ρ ΡΡΠ°ΡΡΠΈΠ΅ΠΌ ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»Ρ (${fieldName}_mapid
) Π²ΠΎ ΠΈΠ·Π±Π΅ΠΆΠ°Π½ΠΈΠ΅ ΠΊΠΎΠ»Π»ΠΈΠ·ΠΈΠΉ.
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΡΠ΅ΡΡΠ΅ΡΠΎΠ²
ΠΡΠΈ ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠΈ Π·Π°ΠΏΠΈΡΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ Π΅Ρ ΠΏΠΎΠ»Π΅ ΡΠΎΡ
ΡΠ°Π½ΡΠ΅ΡΡΡ ΡΠ΅ΡΡΠ΅ΡΠΎΠΌ Π² ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠΈ Ρ Π΅Ρ ΡΠΈΠΏΠΎΠΌ. ΠΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΡΡΠ½ΠΊΡΠΈΡ ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΡ ΠΌΠΎΠΆΠ½ΠΎ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΡΠ²ΠΎΠΉΡΡΡΠ²Π° setter
ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² addFieldsDescriptions
ΠΈ setFieldDescription
. Π ΡΠ»ΡΡΠ°Π΅ ΠΏΡΠΈΠΌΠΈΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Ρ, ΡΠ΅ΡΡΠ΅Ρ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΡΠ°ΠΊ:
function defaultSetter(req, entry, fieldName, transaction) {
entry[fieldName] = req.body[fieldName]
}
Π’Π°ΠΊ ΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ Promise
. Π ΡΠ΅ΡΡΠ΅ΡΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅ΡΡΠΈΡΡ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ:
function setter(req, entry, fieldName, transaction) {
if (req.body[fieldName] % 10 === 0) entry[fieldName] = req.body[fieldName]
else throw req.SA.getSequelizeError('multiples of ten', fieldName)
}
ΠΠ»ΠΎΠ±Π°Π»ΡΠ½ΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ
ΠΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°ΡΡ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΠΎΠ³ΠΎ Π½Π°ΡΠ»Π΅Π΄Π½ΠΈΠΊΠ° ΠΊΠ»Π°ΡΡΠ° ModelAdmin
, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΡΡΠ½ΠΊΡΠΈΠΈ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π° ΠΈ ΡΠ΅ΡΡΠ΅Ρ Π΄Π»Ρ ΡΠ°Π·Π»ΠΈΡΠ½ΡΡ
ΡΠΈΠΏΠΎΠ², Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ Π½Π΅ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΡ
. Π Π·Π°ΡΠ΅ΠΌ ΡΡΡΠ»Π°ΡΡΡΡ Π½Π° ΠΈΠΌΠ΅Π½Π° ΡΡΠΈΡ
ΡΠΈΠΏΠΎΠ² Π²ΠΌΠ΅ΡΡΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΡΡΠ½ΠΊΡΠΈΠΉ Π² ΡΠ²ΠΎΠΉΡΡΠ²Π°Ρ
view
, widget
ΠΈ setter
ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² addFieldsDescriptions
ΠΈ setFieldDescription
:
class CustomTypes extends ModelAdmin {
init() {
super.init()
this.overrideTypeView('X_FILEBLOB', (req, entry, fieldName) => {
return entry[fieldName]
? req.SA.tr('File size:') + entry[fieldName].length
: req.SA.tr('No file')
})
this.overrideTypeWidget(
'X_FILEBLOB',
(req, entry, fieldName, value, options) => {
return `<input type="file" class="form-control" ${
options.readOnly ? 'disabled' : ''
} name="${fieldName}" />`
}
)
this.overrideTypeSetter(
'X_FILEBLOB',
(req, entry, fieldName, transaction) => {
return new Promise((resolve, reject) => {
const file = req.files[fieldName]
if (file.size > 1048576)
reject(req.SA.getSequelizeError('file length', fieldName))
fs.readFile(file.path, (err, buf) => {
if (err) reject(err)
else {
entry[fieldName] = buf
resolve()
}
})
})
}
)
}
}
class OverridedAdmin extends CustomTypes {
init() {
super.init()
this.addFieldDescriptions({
file: {
view: 'X_FILEBLOB',
widget: 'X_FILEBLOB',
setter: 'X_FILEBLOB'
}
})
}
}
ΠΠ΅ΠΉΡΡΠ²ΠΈΡ (actions)
ΠΠ»Ρ ΠΌΠ°ΡΡΠΎΠ²ΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² Π² ΡΠ°Π±Π»ΠΈΡΠ΅ Π·Π°ΠΏΠΈΡΠ΅ΠΉ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π·Π°Π΄Π°ΡΡ action.
ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΠΌΠ°ΡΡΠΈΠ² Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ Π² ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ ModelAdmin.actions
. ΠΠΎΠΏΡΡΡΠΈΠΌ, ΠΌΡ Ρ
ΠΎΡΠΈΠΌ ΠΈΠΌΠ΅ΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ ΠΎΠ±Π½ΡΠ»ΡΡΡ ΠΏΠΎΠ»Π΅ count
this.actions = [
{
name: 'zerofy',
renderer: (req, res, modelAdmin, ids, exit) => {
let transaction
return req.SA.sequelizeInstance
.transaction()
.then(_transaction => (transaction = _transaction))
.then(() =>
modelAdmin.model.update(
{ count: 0 },
{ where: { [modelAdmin.pkName]: ids }, transaction }
)
)
.then(() => transaction.commit())
.catch(() => transaction.rollback())
.then(() => exit())
}
}
]
Π’Π΅ΠΏΠ΅ΡΡ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΎΡΠΌΠ΅ΡΠΈΡΡ ΡΠ΅ΠΊΠ±ΠΎΠΊΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΡ
Π·Π°ΠΏΠΈΡΠ΅ΠΉ ΠΈ Π²ΡΠ±ΡΠ°ΡΡ ΠΏΡΠ½ΠΊΡ zerofy
ΠΈΠ· Π²ΡΠΏΠ°Π΄Π°ΡΡΠ΅Π³ΠΎ ΡΠΏΠΈΡΠΊΠ° Π² Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ΅ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ Ρ ΡΠ΅ΠΊΠ±ΠΎΠΊΡΠ°ΠΌΠΈ. ΠΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΡΠ°ΠΌ ΠΏΡΠΈΡΡΡΡΡΠ²ΡΠ΅Ρ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΡΠ½ΠΊΡ delete
.
ΠΠΎΠ»Π»Π±ΡΠΊ renderer
ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ ΡΠΏΡΠ°Π²Π»ΡΡΡ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΠΌ ΠΈ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π°ΠΌΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΡΠ°Π½ΠΈΡΠ°ΠΌΠΈ, ΠΊΠ°ΠΊ ΡΡΠΎ ΡΠ΄Π΅Π»Π°Π½ΠΎ Π² Π΄Π΅ΠΉΡΡΠ²ΠΈΠΈ delete
, Π½ΠΎ Π² Π΄Π°Π½Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΠ²Π½ΠΎ ΠΈΠ·Π±ΡΡΠΎΡΠ΅Π½. ΠΠΌΠ΅ΡΡΠΎ Π½Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π² ΡΠ²ΠΎΠΉΡΡΠ²Π΅ changer
ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΏΡΠΎΡΡΠΎΠΉ ΠΊΠΎΠ»Π»Π±ΡΠΊ, ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡΠΈΠΉ Π·Π°ΠΏΠΈΡΠΈ ΠΏΠΎ ΠΎΠ΄Π½ΠΎΠΉ:
this.actions = [
{
name: 'zerofy',
changer: entry => (entry.count = 0)
}
]
ΠΠ΅ Π²ΡΠ΅Π³Π΄Π° ΠΎΠΏΡΠΈΠΌΠ°Π»ΡΠ½ΠΎ, Π½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎ Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΡΠ΅.
TODO
: Π²ΠΎΠ·Π²ΡΠ°Ρ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΈ affected rows
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Π·Π°ΠΏΡΠΎΡΠ° ΠΈ ΡΠΊΠ²ΠΎΠ·Π½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ
ΠΠΎΡΠ»Π΅ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π·Π°ΠΏΠΈΡΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΆΠ΅Π»Π°ΡΠ΅Π»ΡΠ½ΠΎ Π²Π΅ΡΠ½ΡΡΡΡΡ ΠΊ ΠΏΡΠΎΡΠΌΠΎΡΡΡ ΡΠΏΠΈΡΠΊΠ° Π·Π°ΠΏΠΈΡΠ΅ΠΉ Π² ΡΠΎΠΌ ΠΆΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ, Π²ΠΊΠ»ΡΡΠ°Ρ ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΡ, ΠΏΠΎΠΈΡΠΊ ΠΈ Ρ.Π΄. ΠΠ»Ρ ΡΠΏΡΠΎΡΠ΅Π½ΠΈΡ ΡΡΠΎΠ³ΠΎ, Π½Π΅ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π΄ΠΎΠ»ΠΆΠ½Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π²Π°ΡΡΡΡ ΠΏΡΠΈ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ. Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ΠΎ ΡΡΠΎ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ. ΠΡΡΡ ΠΏΡΠ΅Π΄ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΡΠΉ ΡΠΏΠΈΡΠΎΠΊ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² ΠΈ ΡΡΠ½ΠΊΡΠΈΡ, ΠΊΠΎΡΠΎΡΠ°Ρ ΡΠΎΠ·Π΄Π°ΡΡ URL ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π° Π²ΠΊΠ»ΡΡΠ°ΡΡΠ΅Π³ΠΎ Π²ΡΠ΅ ΡΡΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π² ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎΠΌ Π²ΠΈΠ΄Π΅ Π² ΡΠ²ΠΎΠΉΡΡΠ²Π΅ params
. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Π΅ΡΠ»ΠΈ ΠΌΡ Π½Π°Ρ
ΠΎΠ΄ΡΡ Π½Π° ΡΡΡΠ°Π½ΠΈΡΠ΅ /admin/model/modelName/?search=ΡΠ΅ΡΡ
, ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π΅ Π½Π° Π²ΡΠΎΡΡΡ ΡΡΡΠ°Π½ΠΈΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² ΠΏΠΎΠΈΡΠΊΠ°, URL ΡΡΡΠ»ΠΊΠΈ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π° Π±ΡΠ΄Π΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ /admin/model/modelName?params={"search":"ΡΠ΅ΡΡ","page":1}
.
ΠΡΠ΅ URL ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄ΠΎΠ² Π²Π½ΡΡΡΠΈ Π°Π΄ΠΌΠΈΠ½-ΠΏΠ°Π½Π΅Π»ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΠΎΠ·Π΄Π°Π²Π°ΡΡΡΡ ΡΡΠ½ΠΊΡΠΈΠ΅ΠΉ req.SA.queryExtender()
, ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡΠ΅ΠΉ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ:
- ΠΌΠ°ΡΡΠΈΠ² ΡΠ°ΡΡΠ΅ΠΉ ΠΏΡΡΠΈ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΊΠΎΡΠ½Ρ Π°Π΄ΠΌΠΈΠ½-ΠΏΠ°Π½Π΅Π»ΠΈ
['model', 'modelName']
- ΠΎΠ±ΡΠ΅ΠΊΡ Ρ Π½ΠΎΠ²ΡΠΌΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ
{page: 1}
- ΠΌΠ°ΡΡΠΈΠ² ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π½Π΅ ΡΠ»Π΅Π΄ΡΠ΅Ρ Π²ΠΊΠ»ΡΡΠ°ΡΡ Π² Π·Π°ΠΏΡΠΎΡ ΠΈΠ»ΠΈ
false
Π΄Π»Ρ ΠΈΡΠΊΠ»ΡΡΠ½ΠΈΡ Π²ΡΠ΅Ρ .
Π ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅ Π²ΡΡΠ΅ΡΠΏΠΎΠΌΡΠ½ΡΡΠ°Ρ ΡΡΡΠ»ΠΊΠ° ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΠΈ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΡΠ°ΠΊ (ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ pug
):
a(href=req.SA.queryExtender(["model", req.SA.modelName], {page: data.page+1}, []) >>
ΠΠ½Π°Π»ΠΎΠ³ΠΈΡΠ½ΠΎ, ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ ΡΠΎΡΠΌ, Π² ΡΠΎΡΠΌΡ ΡΠ»Π΅Π΄ΡΠ΅Ρ Π²ΠΊΠ»ΡΡΠΈΡΡ ΠΏΠΎΠ»Π΅ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ req.SA.formExtender
Ρ ΡΠ΅ΠΌΠΈ ΠΆΠ΅ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°ΠΌΠΈ ΠΊΡΠΎΠΌΠ΅ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ
//- action ΡΠΎΡΠΌΡ ΡΠΎΠ·Π΄Π°ΡΡΡΡ Π±Π΅Π· ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Π·Π°ΠΏΡΠΎΡΠ°
form(action=req.SA.queryExtender(["model", req.SA.modelName], {}, false), enctype="multipart/form-data")
//- ΠΏΠΎΠ»Π΅ ΡΠΎΡΠΌΡ
input(type="text", name="search", value=req.SA.params.search)
//- ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΡΠΎΠΌΠ΅ search ΠΈ page Π²ΡΡΠ°Π²Π»Π΅Π½Ρ Π² ΡΠΎΡΠΌΡ
| !{req.SA.formExtender({}, ["search", "page"])}
ΠΠ° ΡΡΠΎ ΡΠ»Π΅Π΄ΡΠ΅Ρ ΠΎΠ±ΡΠ°ΡΠΈΡΡ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅:
-
action
ΡΠΎΡΠΌΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² - Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ»Ρ ΡΠΎΡΠΌΡ Π±Π΅ΡΡΡΡΡ ΠΈΠ·
req.SA.params.search
- ΠΏΡΠΈ Π²ΡΠ·ΠΎΠ²Π΅
req.SA.formExtender
Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ²Π½ΠΎ ΠΈΡΠΊΠ»ΡΡΠΈΡΡ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎsearch
, ΠΈΠ½Π°ΡΠ΅ ΠΏΠΎΡΠ»Π΅ ΠΎΡΠΈΡΡΠΊΠΈ ΠΏΠΎΠ»Π΅ ΠΏΠΎΠΈΡΠΊΠ° Π±ΡΠ΄Π΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΎ ΡΡΠ°ΡΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ -
page
ΠΎΡΠΈΡΠ°Π΅ΡΡΡ ΠΈΠ· ΡΠ΅Ρ ΡΠΎΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ, ΡΡΠΎ Π½ΠΎΠΌΠ΅Ρ ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΡ ΠΏΠΎΡΠ»Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² Π²ΡΠ²ΠΎΠ΄Π° Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ ΡΠΌΡΡΠ»Π°
ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΎ req.SA.params.search
: Π² Π½Π΅Π³ΠΎ ΠΏΠΎΠΏΠ°Π΄Π°ΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π΄Π΅ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Ρ params
, ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π·Π°ΠΏΡΠΎΡΠ°, ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΡΠΎΡΠΌ Π² ΠΏΠΎΡΡΠ΄ΠΊΠ΅ ΠΏΠ΅ΡΠ΅ΠΊΡΡΡΠΈΡ.
Π‘ΠΏΠΈΡΠΎΠΊ ΡΠΊΠ²ΠΎΠ·Π½ΡΡ
ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²: ['sort', 'page', 'search', 'subwindow', 'action', 'entryIds', 'backurl']
ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ
ΠΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΠΈ Π²Π²ΠΎΠ΄Π½ΡΡ
Π΄Π°Π½Π½ΡΡ
Π»Π΅ΠΆΠ°Ρ Π½Π° sequelize
, ΠΏΠΎΡΡΠΎΠΌΡ ΠΆΠ΅Π»Π°ΡΠ΅Π»ΡΠ½ΠΎ ΠΎΠΏΠΈΡΡΠ²Π°ΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΏΡΠΈ ΠΎΠ±ΡΡΠ²Π»Π΅Π½ΠΈΠΈ ΠΏΠΎΠ»Ρ. ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΏΠΎ ΡΡΡΠ»ΠΊΠ΅
Π‘ΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅ΡΡΠΈΡΡ Π² ΡΠ΅ΡΡΠ΅Ρ.
ΠΠΎΠΊΠ°Π»ΠΈΠ·Π°ΡΠΈΡ
ΠΠ°ΠΏΡΡΡΠΈΡΠ΅ ΡΠΎΠ·Π΄Π°Π½Π½ΡΠΉ ΡΠ°Π½Π΅Π΅ cli.js Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ΠΎΠ²
node cli dumptranslation [--empty] [--hints] > translations/[Π»ΠΎΠΊΠ°Π»Ρ].json
ΠΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΡΠΉΡΠ΅ Π»ΠΎΠΊΠ°Π»Ρ, ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ, ΠΈΠΌΠ΅Π½Π° ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ, ΠΏΠΎΠ»Π΅ΠΉ ΠΈ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ. ΠΠΎΠ²ΡΠΎΡΠΈΡΡ ΠΏΠΎ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Ρ Π»ΠΎΠΊΠ°Π»Π΅ΠΉ.
Π’Π°ΠΊ ΠΆΠ΅ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π·Π°Π΄Π°ΡΡ ΠΏΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠΈ Π΄Π»Ρ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΈ ΠΏΠΎΠ»Π΅ΠΉ, Π² ΠΏΠ΅ΡΠ²ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΠ΅ΡΠ΅Π· ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ hint
, Π²ΠΎ Π²ΡΠΎΡΠΎΠΌ ΡΠ΅ΡΠ΅Π· ΠΎΠ΄Π½ΠΎΠΈΠΌΠ΅Π½Π½ΠΎΠ΅ Ρ ΠΈΠΌΠ΅Π½Π΅ΠΌ ΠΏΠΎΠ»Ρ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ Ρ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π½ΡΠΌ ΡΡΡΡΠΈΠΊΡΠΎΠΌ _hint
"sequelize_admin_user": {
"label": "",
"plural": "",
"hint": "ΠΏΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠ° ΠΌΠΎΠ΄Π΅Π»ΠΈ",
"fields": {
"username": "",
"username_hint": "ΠΏΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠ° ΠΏΠΎΠ»Ρ",
...
Π‘Π³ΡΡΠΏΠΏΠΈΡΡΠΉΡΠ΅ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Ρ ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ translation
Π² ΡΡΠ΅ΡΠΈΠΉ Π°ΡΠ³ΡΠΌΠ΅Π½Ρ sequelizeAdmin
:
app.use(
'/admin',
sequelizeAdmin(express, db.sequelize, {
translation: Object.assign(
{},
require('./translations/ru'),
require('./translations/de')
)
})
)
ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΠ·Π°ΡΠΈΡ ΠΈΠΌΠΏΠΎΡΡΠ° ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ ΠΏΠ°ΠΏΠΊΠΈ translations Π½Π° Π²Π°ΡΠ΅ ΡΡΠΌΠΎΡΡΠ΅Π½ΠΈΠ΅.
TODO: ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ ΠΎΡΠΈΠ±ΠΎΠΊ
Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΠΌΠΈ ΠΈ ΠΏΡΠ°Π²Π°ΠΌΠΈ
ΠΠ»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΡΠΏΠ΅ΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΠ΅
node cli createsuperuser Π»ΠΎΠ³ΠΈΠ½ ΠΏΠ°ΡΠΎΠ»Ρ
Π ΡΠ΅ΠΆΠΈΠΌΠ΅ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ (process.env.NODE_ENV !== 'production'
) ΡΡΠΎ Π½Π΅ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ, ΠΏΡΠΈ ΠΎΡΡΡΡΡΡΡΠ²ΠΈΠΈ ΡΡΠΏΠ΅ΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ ΠΎΡΡΡΠ΅ΡΡΠ²Π»ΡΠ΅ΡΡΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ Π²Ρ
ΠΎΠ΄ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ EMERGENCY
ΠΈ Π²ΡΠ²ΠΎΠ΄ΠΈΡΡΡ ΠΏΡΠ΅Π΄ΡΠΏΡΠ΅ΠΆΠ΄Π΅Π½ΠΈΠ΅.
Π ΡΠΏΠΈΡΠΎΠΊ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π° sequelize_admin_users
, Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΏΠ΅ΡΠ΅ΡΠΈΡΠ»Π΅Π½Ρ Π²ΡΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ, Π° ΡΠ°ΠΊΠΆΠ΅ Π΅ΡΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΏΡΠ°Π²Π°ΠΌΠΈ Π΄ΠΎΡΡΡΠΏΠ° ΠΊ ΠΌΠΎΠ΄Π΅Π»ΡΠΌ ΡΠΎΠ³Π»Π°ΡΠ½ΠΎ CRUD.
ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ
- ΠΏΠΎΠ΄ΡΠ°Π·ΡΠΌΠ΅Π²Π°Π΅ΡΡΡ ΡΡΠΎ
primary key
ΡΠ²Π»ΡΠ΅ΡΡΡ ΡΠΈΡΠ»ΠΎΠ²ΡΠΌ - ΡΠ°Π±ΠΎΡΠ° c
paranoid option
Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΠ»Π°ΡΡ - Π΄Π»Ρ ΠΏΠ°ΡΡΠΈΠ½Π³Π° ΡΠΎΡΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ
formidable
, ΠΆΠ΅Π»Π°ΡΠ΅Π»ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡenctype="multipart/form-data"
. ΠΠ°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΠ°ΡΡΠ΅ΡΠ° ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ Π² ΡΠ²ΠΎΠΉΡΡΠ²Π΅formidableOpts
ΡΡΠ΅ΡΡΠ΅Π³ΠΎ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ° sequelizeAdmin - ΡΡΠ΅Π±ΡΠ΅ΡΡΡ NodeJS Π½Π΅ Π½ΠΈΠΆΠ΅ 6.0.0 Π²Π΅ΡΡΠΈΠΈ, ΠΏΡΠΎΠ²Π΅ΡΡΠ»ΠΎΡΡ Π½Π° 6.14.2LTS ΠΈ 8.11.2LTS
- ΡΠ΅ΡΡΠΈΠΈ Ρ ΡΠ°Π½ΡΡΡΡ Π² ΠΏΠ°ΠΌΡΡΠΈ