Use a Smart Action Form
We've just introduced Smart actions: they're great because you can execute virtually any business logic. However, there is one big part missing: how do you let your users provide more information or have interaction when they trigger the Smart action? In short, you need to open a Smart Action Form.

Opening a Smart Action Form

Very often, you will need to ask user inputs before triggering the logic behind a Smart Action. For example, you might want to specify a reason if you want to block a user account. Or set the amount to charge a user’s credit card.
SQL
Mongodb
Rails
Django
On our Live Demo example, we’ve defined 4 input fields on the Smart Action Upload Legal Docs on the collection companies.
/forest/companies.js
1
const { collection } = require('forest-express-sequelize');
2
3
collection('companies', {
4
actions: [{
5
name: 'Upload Legal Docs',
6
type: 'single',
7
fields: [{
8
field: 'Certificate of Incorporation',
9
description: 'The legal document relating to the formation of a company or corporation.',
10
type: 'File',
11
isRequired: true
12
}, {
13
field: 'Proof of address',
14
description: '(Electricity, Gas, Water, Internet, Landline & Mobile Phone Invoice / Payment Schedule) no older than 3 months of the legal representative of your company',
15
type: 'File',
16
isRequired: true
17
}, {
18
field: 'Company bank statement',
19
description: 'PDF including company name as well as IBAN',
20
type: 'File',
21
isRequired: true
22
}, {
23
field: 'Valid proof of ID',
24
description: 'ID card or passport if the document has been issued in the EU, EFTA, or EEA / ID card or passport + resident permit or driving licence if the document has been issued outside the EU, EFTA, or EEA of the legal representative of your company',
25
type: 'File',
26
isRequired: true
27
}]
28
}]
29
});
Copied!
/routes/companies.js
1
...
2
3
router.post('/actions/upload-legal-docs', permissionMiddlewareCreator.smartAction(),
4
(req, res) => {
5
// Get the current company id
6
let companyId = req.body.data.attributes.ids[0];
7
8
// Get the values of the input fields entered by the admin user.
9
let attrs = req.body.data.attributes.values;
10
let certificate_of_incorporation = attrs['Certificate of Incorporation'];
11
let proof_of_address = attrs['Proof of address'];
12
let company_bank_statement = attrs['Company bank statement'];
13
let passport_id = attrs['Valid proof of id'];
14
15
// The business logic of the Smart Action. We use the function
16
// UploadLegalDoc to upload them to our S3 repository. You can see the full
17
// implementation on our Forest Live Demo repository on Github.
18
return P.all([
19
uploadLegalDoc(companyId, certificate_of_incorporation, 'certificate_of_incorporation_id'),
20
uploadLegalDoc(companyId, proof_of_address, 'proof_of_address_id'),
21
uploadLegalDoc(companyId, company_bank_statement,'bank_statement_id'),
22
uploadLegalDoc(companyId, passport_id, 'passport_id'),
23
])
24
.then(() => {
25
// Once the upload is finished, send a success message to the admin user in the UI.
26
res.send({ success: 'Legal documents are successfully uploaded.' });
27
});
28
});
29
30
...
31
32
module.exports = router;
Copied!
On our Live Demo example, we’ve defined 4 input fields on the Smart Action Upload Legal Docs on the collection companies.
/forest/companies.js
1
const { collection } = require('forest-express-mongoose');
2
3
collection('companies', {
4
actions: [{
5
name: 'Upload Legal Docs',
6
type: 'single',
7
fields: [{
8
field: 'Certificate of Incorporation',
9
description: 'The legal document relating to the formation of a company or corporation.',
10
type: 'File',
11
isRequired: true
12
}, {
13
field: 'Proof of address',
14
description: '(Electricity, Gas, Water, Internet, Landline & Mobile Phone Invoice / Payment Schedule) no older than 3 months of the legal representative of your company',
15
type: 'File',
16
isRequired: true
17
}, {
18
field: 'Company bank statement',
19
description: 'PDF including company name as well as IBAN',
20
type: 'File',
21
isRequired: true
22
}, {
23
field: 'Valid proof of ID',
24
description: 'ID card or passport if the document has been issued in the EU, EFTA, or EEA / ID card or passport + resident permit or driving licence if the document has been issued outside the EU, EFTA, or EEA of the legal representative of your company',
25
type: 'File',
26
isRequired: true
27
}],
28
});
Copied!
/routes/companies.js
1
...
2
3
router.post('/actions/upload-legal-docs',
4
(req, res) => {
5
// Get the current company id
6
let companyId = req.body.data.attributes.ids[0];
7
8
// Get the values of the input fields entered by the admin user.
9
let attrs = req.body.data.attributes.values;
10
let certificate_of_incorporation = attrs['Certificate of Incorporation'];
11
let proof_of_address = attrs['Proof of address'];
12
let company_bank_statement = attrs['Company bank statement'];
13
let passport_id = attrs['Valid proof of id'];
14
15
// The business logic of the Smart Action. We use the function
16
// UploadLegalDoc to upload them to our S3 repository. You can see the full
17
// implementation on our Forest Live Demo repository on Github.
18
return P.all([
19
uploadLegalDoc(companyId, certificate_of_incorporation, 'certificate_of_incorporation_id'),
20
uploadLegalDoc(companyId, proof_of_address, 'proof_of_address_id'),
21
uploadLegalDoc(companyId, company_bank_statement,'bank_statement_id'),
22
uploadLegalDoc(companyId, passport_id, 'passport_id'),
23
])
24
.then(() => {
25
// Once the upload is finished, send a success message to the admin user in the UI.
26
res.send({ success: 'Legal documents are successfully uploaded.' });
27
});
28
});
29
30
...
31
32
module.exports = router;
Copied!
On our Live Demo example, we’ve defined 4 input fields on the Smart Action Upload Legal Docs on the collection Company.
/lib/forest_liana/collections/company.rb
1
class Forest::Company
2
include ForestLiana::Collection
3
4
collection :Company
5
6
action 'Upload Legal Docs', type: 'single', fields: [{
7
field: 'Certificate of Incorporation',
8
description: 'The legal document relating to the formation of a company or corporation.',
9
type: 'File',
10
is_required: true
11
}, {
12
field: 'Proof of address',
13
description: '(Electricity, Gas, Water, Internet, Landline & Mobile Phone Invoice / Payment Schedule) no older than 3 months of the legal representative of your company',
14
type: 'File',
15
is_required: true
16
}, {
17
field: 'Company bank statement',
18
description: 'PDF including company name as well as IBAN',
19
type: 'File',
20
is_required: true
21
}, {
22
field: 'Valid proof of ID',
23
description: 'ID card or passport if the document has been issued in the EU, EFTA, or EEA / ID card or passport + resident permit or driving licence if the document has been issued outside the EU, EFTA, or EEA of the legal representative of your company',
24
type: 'File',
25
is_required: true
26
}]
27
end
Copied!
/config/routes.rb
1
Rails.application.routes.draw do
2
# MUST be declared before the mount ForestLiana::Engine.
3
namespace :forest do
4
post '/actions/upload-legal-docs' => 'companies#upload_legal_docs'
5
end
6
7
mount ForestLiana::Engine => '/forest'
8
end
Copied!
/app/controllers/forest/companies_controller.rb
1
class Forest::CompaniesController < ForestLiana::SmartActionsController
2
3
def upload_legal_doc(company_id, doc, field)
4
id = SecureRandom.uuid
5
6
Forest::S3Helper.new.upload(doc, "livedemo/legal/#{id}")
7
8
company = Company.find(company_id)
9
company[field] = id
10
company.save
11
12
Document.create({
13
file_id: company[field],
14
is_verified: true
15
})
16
end
17
18
def upload_legal_docs
19
# Get the current company id
20
company_id = ForestLiana::ResourcesGetter.get_ids_from_request(params).first
21
22
# Get the values of the input fields entered by the admin user.
23
attrs = params.dig('data', 'attributes', 'values')
24
certificate_of_incorporation = attrs['Certificate of Incorporation'];
25
proof_of_address = attrs['Proof of address'];
26
company_bank_statement = attrs['Company bank statement'];
27
passport_id = attrs['Valid proof of ID'];
28
29
# The business logic of the Smart Action. We use the function
30
# upload_legal_doc to upload them to our S3 repository. You can see the
31
# full implementation on our Forest Live Demo repository on Github.
32
upload_legal_doc(company_id, certificate_of_incorporation, 'certificate_of_incorporation_id')
33
upload_legal_doc(company_id, proof_of_address, 'proof_of_address_id')
34
upload_legal_doc(company_id, company_bank_statement, 'bank_statement_id')
35
upload_legal_doc(company_id, passport_id, 'passport_id')
36
37
# Once the upload is finished, send a success message to the admin user in the UI.
38
render json: { success: 'Legal documents are successfully uploaded.' }
39
end
40
end
Copied!
On our Live Demo example, we’ve defined 4 input fields on the Smart Action Upload Legal Docs on the collection Company.
app/forest/cpmpany.py
1
from django_forest.utils.collection import Collection
2
from app.models import Company
3
4
class CompanyForest(Collection):
5
def load(self):
6
self.actions = [{
7
'name': 'Upload Legal Docs',
8
'fields': [
9
{
10
'field': 'Certificate of Incorporation',
11
'description': 'The legal document relating to the formation of a company or corporation.',
12
'isRequired': True,
13
'type': 'File'
14
},
15
{
16
'field': 'Proof of address',
17
'description': '(Electricity, Gas, Water, Internet, Landline & Mobile Phone Invoice / Payment Schedule) no older than 3 months of the legal representative of your company',
18
'isRequired': True,
19
'type': 'File'
20
},
21
{
22
'field': 'Company bank statement',
23
'description': 'PDF including company name as well as IBAN',
24
'isRequired': True,
25
'type': 'File'
26
},
27
{
28
'field': 'Valid proof of ID',
29
'description': 'ID card or passport if the document has been issued in the EU, EFTA, or EEA / ID card or passport + resident permit or driving licence if the document has been issued outside the EU, EFTA, or EEA of the legal representative of your company',
30
'isRequired': True,
31
'type': 'File'
32
},
33
],
34
}]
35
36
Collection.register(CompanyForest, Company)
Copied!
app/urls.py
1
from django.urls import path
2
from django.views.decorators.csrf import csrf_exempt
3
4
from . import views
5
6
app_name = 'app'
7
urlpatterns = [
8
path('/actions/upload-legal-docs', csrf_exempt(views.UploadLegalDocsView.as_view()), name='upload-legal-docs'),
9
]
Copied!
app/views.py
1
from django.http import JsonResponse
2
from django_forest.utils.views.action import ActionView
3
4
5
class UploadLegalDocsView(ActionView):
6
7
def upload_legal_doc(company_id, doc, field):
8
# FIXME
9
pass
10
11
def post(self, request, *args, **kwargs):
12
params = request.GET.dict()
13
body = self.get_body(request.body)
14
ids = self.get_ids_from_request(request, self.Model)
15
16
# Get the current company id
17
company_id = ids[0]
18
19
# Get the values of the input fields entered by the admin user.
20
attrs = body['data']['attributes']['values']
21
certificate_of_incorporation = attrs['Certificate of Incorporation']
22
proof_of_address = attrs['Proof of address']
23
company_bank_statement = attrs['Company bank statement']
24
passport_id = attrs['Valid proof of ID']
25
26
# The business logic of the Smart Action. We use the function
27
# upload_legal_doc to upload them to our S3 repository. You can see the
28
# full implementation on our Forest Live Demo repository on Github.
29
self.upload_legal_doc(company_id, certificate_of_incorporation, 'certificate_of_incorporation_id')
30
self.upload_legal_doc(company_id, proof_of_address, 'proof_of_address_id')
31
self.upload_legal_doc(company_id, company_bank_statement, 'bank_statement_id')
32
self.upload_legal_doc(company_id, passport_id, 'passport_id')
33
34
# Once the upload is finished, send a success message to the admin user in the UI.
35
return JsonResponse({'success': 'Legal documents are successfully uploaded.'})
Copied!

Handling input values

Here is the list of available options to customize your input form.
Name
Type
Description
field
string
Label of the input field.
type
string or array
Type of your field.
  • string: Boolean, Date, Dateonly, Enum, File, Number, String
  • array: ['Enum'], ['Number'], ['String']
reference
string
(optional) Specify that the input is a reference to another collection. You must specify the primary key (ex: category.id).
enums
array of strings
(optional) Required only for the Enum type. This is where you list all the possible values for your input field.
description
string
(optional) Add a description for your admin users to help them fill correctly your form
isRequired
boolean
(optional) If true, your input field will be set as required in the browser. Default is false.
hook
string
(optional) Specify the change hook. If specified the corresponding hook is called when the input change
widget
string
(optional) The following widgets are available to your smart action fields (text area, date, boolean, file, dateonly)

Making a form dynamic with hooks

Business logic often requires your forms to adapt to its context. Forest Admin makes this possible through a powerful way to extend your form's logic.
To make Smart Action Forms dynamic, we've introduced the concept of hooks: hooks allow you to run some logic upon a specific event.
The load hook is called when the form loads, allowing you to change its properties upon load.
The change hook is called whenever you interact with a field of the form.

Prefill a form with default values

Forest Admin allows you to set default values of your form. In this example, we will prefill the form with data coming from the record itself (1), with just a few extra lines of code.
SQL
Mongodb
Rails
Django
/forest/customers.js
1
const { collection } = require('forest-express-sequelize');
2
const { customers } = require('../models');
3
4
collection('Customers', {
5
actions: [{
6
name: 'Charge credit card',
7
type: 'single',
8
fields: [{
9
field: 'amount',
10
isRequired: true,
11
description: 'The amount (USD) to charge the credit card. Example: 42.50',
12
type: 'Number'
13
}, {
14
field: 'description',
15
isRequired: true,
16
description: 'Explain the reason why you want to charge manually the customer here',
17
type: 'String'
18
}, {
19
// we added a field to show the full potential of prefilled values in this example
20
field: 'stripe_id',
21
isRequired: true,
22
type: 'String'
23
}],
24
hooks: {
25
load: async ({ fields, request }) => {
26
const amount = fields.find(field => field.field === 'amount');
27
const stripeId = fields.find(field => field.field === 'stripe_id');
28
29
amount.value = 4520;
30
31
const id = request.body.data.attributes.ids[0];
32
const customer = await customers.findByPk(id);
33
34
stripeId.value = customer.stripe_id;
35
36
return fields;
37
},
38
},
39
}],
40
...
41
});
Copied!
/forest/customers.js
1
const { collection } = require('forest-express-mongoose');
2
const { customers } = require('../models');
3
4
collection('Customers', {
5
actions: [{
6
name: 'Charge credit card',
7
type: 'single',
8
fields: [{
9
field: 'amount',
10
isRequired: true,
11
description: 'The amount (USD) to charge the credit card. Example: 42.50',
12
type: 'Number'
13
}, {
14
field: 'description',
15
isRequired: true,
16
description: 'Explain the reason why you want to charge manually the customer here',
17
type: 'String'
18
}, {
19
// we added a field to show the full potential of prefilled values in this example
20
field: 'stripe_id',
21
isRequired: true,
22
type: 'String'
23
}],
24
hooks: {
25
load: async ({ fields, request }) => {
26
const amount = fields.find(field => field.field === 'amount');
27
const stripeId = fields.find(field => field.field === 'stripe_id');
28
29
amount.value = 4520;
30
31
const id = request.body.data.attributes.ids[0];
32
const customer = await customers.findByPk(id);
33
34
stripeId.value = customer.stripe_id;
35
36
return fields;
37
},
38
},
39
}],
40
...
41
});
Copied!
lib/forest_liana/customers.rb
1
class Forest::Customers
2
include ForestLiana::Collection
3
4
collection :Customers
5
6
action 'Charge credit card',
7
type: 'single',
8
fields: [{
9
field: 'amount',
10
isRequired: true,
11
description: 'The amount (USD) to charge the credit card. Example: 42.50',
12
type: 'Number'
13
}, {
14
field: 'description',
15
isRequired: true,
16
description: 'Explain the reason why you want to charge manually the customer here',
17
type: 'String'
18
}, {
19
# we added a field to show the full potential of prefilled values in this example
20
field: 'stripe_id',
21
isRequired: true,
22
type: 'String'
23
}],
24
:hook => {
25
:load => -> (context) {
26
amount = context[:fields].find{|field| field[:field] == 'amount'}
27
stripeId = context[:fields].find{|field| field[:field] == 'stripe_id'}
28
29
amount[:value] = 4520;
30
31
id = context[:params][:data][:attributes][:ids][0];
32
customer = Customers.find(id);
33
34
stripeId[:value] = customer['stripe_id'];
35
36
return context[:fields];
37
}
38
}
39
...
40
end
Copied!
app/forest/customer.py
1
import json
2
3
from django_forest.utils.collection import Collection
4
from app.models import Company
5
6
class CompanyForest(Collection):
7
def load(self):
8
self.actions = [{
9
'name': 'Charge credit card',
10
'fields': [
11
{
12
'field': 'amount',
13
'description': 'The amount (USD) to charge the credit card. Example: 42.50',
14
'isRequired': True,
15
'type': 'Number'
16
},
17
{
18
'field': 'description',
19
'description': 'Explain the reason why you want to charge manually the customer here',
20
'isRequired': True,
21
'type': 'String'
22
},
23
# we added a field to show the full potential of prefilled values in this example
24
{
25
'field': 'stripe_id',
26
'isRequired': True,
27
'type': 'String'
28
},
29
],
30
'hooks': {
31
'load': self.charge_credit_card_load,
32
},
33
}]
34
35
def charge_credit_card_load(fields, request, *args, **kwargs):
36
37
amount = next((x for x in fields if x['field'] == 'amount'), None)
38
stripeId = next((x for x in fields if x['field'] == 'stripe_id'), None)
39
40
amount['value'] = 4520;
41
42
body_unicode = request.body.decode('utf-8')
43
body = json.loads(body_unicode)
44
id = body['data']['attributes']['ids'][0]
45
customer = Customers.objects.get(pk=id)
46
47
stripeId['value'] = customer['stripe_id']
48
49
return fields
50
51
Collection.register(CompanyForest, Company)
Copied!

Making a field read-only

To make a field read only, you can use the isReadOnly property:
Name
Type
Description
isReadOnly
boolean
(optional) If true, the Smart action field won’t be editable in the form. Default is false
Combined with the load hook feature, this can be used to make a field read-only dynamically:
1
const { customers } = require('../models');
2
3
collection('customers', {
4
actions: [{
5
name: 'Some action',
6
type: 'single',
7
fields: [
8
{
9
field: 'country',
10
type: 'String',
11
isReadOnly: true
12
},
13
{
14
field: 'city',
15
type: 'String'
16
},
17
],
18
hooks: {
19
load: async ({ fields, request }) => {
20
const country = fields.find(field => field.field === 'country');
21
country.value = 'France';
22
23
const id = request.body.data.attributes.ids[0];
24
const customer = await customers.findById(id);
25
26
// If customer country is not France, empty field and make it editable
27
if (customer.country !== 'France') {
28
country.value = '';
29
country.isReadOnly = false;
30
}
31
return fields;
32
},
33
},
34
}],
35
fields: [],
36
segments: [],
37
});
Copied!

Change your form's data based on previous field values

This feature is only available from version 6.6.0 (forest-express-sequelize and forest-express-mongoose) / version 5.3.3 (forest-rails) and for single Smart actions.
Here's a typical example: Selecting a City within a list of cities from the Country you just selected. Then selecting a Zipcode within a list of zipcodes located in the City you just selected.
SQL
Mongodb
Rails
Django
forest/customers.js
1
const { getEnumsFromDatabaseForThisRecord } = require('./my-own-helper');
2
const { getZipCodeFromCity } = require('...');
3
const { collection } = require('forest-express-sequelize');
4
const { customers } = require('../models');
5
6
collection('customers', {
7
actions: [{
8
name: 'Send invoice',
9
type: 'single',
10
fields: [
11
{
12
field: 'country',
13
type: 'Enum',
14
enums: []
15
},
16
{
17
field: 'city',
18
type: 'String',
19
hook: 'onCityChange'
20
},
21
{
22
field: 'zip code',
23
type: 'String',
24
hook: 'onZipCodeChange'
25
},
26
],
27
hooks: {
28
load: async ({ fields, request }) => {
29
const country = fields.find(field => field.field === 'country');
30
31
const id = request.body.data.attributes.ids[0];
32
const customer = await customers.findByPk(id);
33
34
country.enums = getEnumsFromDatabaseForThisRecord(customer);
35
36
return fields;
37
},
38
change: {
39
onCityChange: async ({ fields, request, changedField }) => {
40
const zipCode = fields.find(field => field.field === 'zip code');
41
42
const id = request.body.data.attributes.ids[0];
43
const customer = await customers.findByPk(id);
44
45
zipCode.value = getZipCodeFromCity(
46
customer,
47
changedField.value
48
);
49
50
return fields;
51
},
52
onZipCodeChange: async ({ fields, request, changedField }) => {
53
const city = fields.find(field => field.field === 'city');
54
55
const id = request.body.data.attributes.ids[0];
56
const customer = await customers.findByPk(id);
57
58
city.value = getCityFromZipCode(
59
customer,
60
changedField.value
61
);
62
63
return fields;
64
},
65
},
66
},
67
}],
68
fields: [],
69
segments: [],
70
});
Copied!
forest/customers.js
1
const { getEnumsFromDatabaseForThisRecord } = require('./my-own-helper');
2
const { getZipCodeFromCity } = require('...');
3
const { collection } = require('forest-express-mongoose');
4
const { customers } = require('../models');
5
6
collection('customers', {
7
actions: [{
8
name: 'Send invoice',
9
type: 'single',
10
fields: [
11
{
12
field: 'country',
13
type: 'Enum',
14
enums: []
15
},
16
{
17
field: 'city',
18
type: 'String',
19
hook: 'onCityChange'
20
},
21
{
22
field: 'zip code',
23
type: 'String',
24
hook: 'onZipCodeChange'
25
},
26
],
27
hooks: {
28
load: async ({ fields, request }) => {
29
const country = fields.find(field => field.field === 'country');
30
31
const id = request.body.data.attributes.ids[0];
32
const customer = await customers.findById(id);
33
34
country.enums = getEnumsFromDatabaseForThisRecord(customer);
35
36
return fields;
37
},
38
change: {
39
onCityChange: async ({ fields, request, changedField }) => {
40
const zipCode = fields.find(field => field.field === 'zip code');
41
42
const id = request.body.data.attributes.ids[0];
43
const customer = await customers.findById(id);
44
45
zipCode.value = getZipCodeFromCity(
46
customer,
47
changedField.value
48
);
49
50
return fields;
51
},
52
onZipCodeChange: async ({ fields, request, changedField }) => {
53
const city = fields.find(field => field.field === 'city');
54
55
const id = request.body.data.attributes.ids[0];
56
const customer = await customers.findById(id);
57
58
city.value = getCityFromZipCode(
59
customer,
60
changedField.value
61
);
62
63
return fields;
64
},
65
},
66
},
67
}],
68
fields: [],
69
segments: [],
70
});
Copied!
/lib/forest_liana/collections/company.rb
1
actions 'Send invoice',
2
type: 'single',
3
fields: [
4
{
5
field: 'country',
6
type: 'Enum',
7
enums: []
8
},
9
{
10
field: 'city',
11
type: 'String',
12
hook: 'oncityChange'
13
},
14
{
15
field: 'zip code',
16
type: 'String',
17
hook: 'onZipCodeChange'
18
},
19
],
20
hooks: {
21
:load => -> (context){
22
country = context[:fields].find{|field| field[:field] == 'country'}
23
24
id = context[:params][:data][:attributes][:ids][0];
25
customer = Customers.find(id);
26
27
country[:enums] = getEnumsFromDatabaseForThisRecord(customer)
28
29
return context[:fields]
30
},
31
:change => {
32
'oncityChange'=> -> (context){
33
zipCode = context[:fields].find{|field| field[:field] == 'zip code'}
34
35
id = context[:params][:data][:attributes][:ids][0];
36
customer = Customers.find(id);
37
38
zipCode[:value] = getZipCodeFromCity(
39
context[:record],
40
context[:context][:changed_field][:value]
41
)
42
43
return context[:fields]
44
},
45
'onZipCodeChange'=> -> (context) {
46
city = context[:fields].find{|field| field[:field] == 'city'}
47
48
id = context[:params][:data][:attributes][:ids][0];
49
customer = Customers.find(id);
50
51
city[:value] = getCityFromZipCode(
52
context[:record],
53
context[:context][:changed_field][:value]
54
)
55
56
return context[:fields]
57
},
58
},
59
}
Copied!
app/forest/company.py
1
import json
2
3
from django_forest.utils.collection import Collection
4
from app.models import Company
5
6
class CompanyForest(Collection):
7
def load(self):
8
self.actions = [{
9
'name': 'Send invoice',
10
'fields': [
11
{
12
'field': 'country',
13
'type': 'Enum',
14
'enums': []
15
},
16
{
17
'field': 'city',
18
'type': 'String',
19
'hook': 'cityChange'
20
},
21
{
22
'field': 'zip code',
23
'type': 'String',
24
'hook': 'zipCodeChange'
25
},
26
],
27
'hooks': {
28
'load': self.send_invoice_load,
29
'change': {
30
'cityChange': self.send_invoice_change_city,
31
'zipCodeChange': self.send_invoice_change_zip_code,
32
},
33
},
34
}]
35
36
def get_enums_from_database_for_this_record(customer):
37
# TODO
38
pass
39
40
def get_zip_code_from_city(customer, value):
41
# TODO
42
pass
43
44
def get_city_from_zip_code(customer, value):
45
# TODO
46
pass
47
48
def send_invoice_load(fields, request, *args, **kwargs):
49
country = next((x for x in fields if x['field'] == 'country'), None)
50
body_unicode = request.body.decode('utf-8')
51
body = json.loads(body_unicode)
52
id = body['data']['attributes']['ids'][0]
53
customer = Customers.objects.get(pk=id);
54
55
country['enums'] = self.get_enums_from_database_for_this_record(customer)
56
return fields
57
58
def send_invoice_change_city(self, fields, request, changed_field, *args, **kwargs):
59
zip_code = next((x for x in fields if x['field'] == 'zip code'), None)
60
61
body_unicode = request.body.decode('utf-8')
62
body = json.loads(body_unicode)
63
id = body['data']['attributes']['ids'][0]
64
customer = Customers.objects.get(pk=id);
65
66
zip_code['value'] = self.get_zip_code_from_city(
67
customer,
68
changed_field['value']
69
)
70
71
return fields
72
73
def send_invoice_change_zip_code(self, fields, request, changed_field, *args, **kwargs):
74
city = next((x for x in fields if x['field'] == 'city'), None)
75
76
body_unicode = request.body.decode('utf-8')
77
body = json.loads(body_unicode)
78
id = body['data']['attributes']['ids'][0]
79
customer = Customers.objects.get(pk=id);
80
81
city['value'] = self.get_city_from_zip_code(
82
customer,
83