-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Migrating to Version 14
This page is intended to make it easier for users who maintain custom apps/forks to migrate their installations to Version 14.
- Open hooks.py
- Rename the
jenv
hook tojinja
- For each string in the
methods
list remove the part before:
(colon) including the colon. It should only be a list of method paths. - Repeat the same for strings in
filters
.
For e.g.,
- jenv = {
+ jinja = {
"methods": [
- "get_fullname:custom_app.jinja.get_fullname"
+ "custom_app.jinja.get_fullname"
],
"filters": [
- "format_currency:custom_app.jinja.currency_filter"
+ "custom_app.jinja.format_currency"
]
}
custom_app/jinja.py
- def currency_filter():
+ def format_currency():
...
Docs: https://frappeframework.com/docs/user/en/python-api/hooks#jinja-customization
The new build system does not support build.json
. To make sure bundles are built correctly, you need to create a bundle file for each key in build.json
.
Let's say your build.json looks like this:
{
"js/my_app.js": [
"public/js/utils.js",
"public/js/main.js",
"public/js/support.js"
],
"js/another_file.js": [
"public/js/another_file.js"
],
"css/my_app.css": [
"public/less/components.less",
"public/less/style.less"
],
}
Create a file named my_app.bundle.js
in the public/js
directory and import the 3 files.
// public/js/my_app.bundle.js
import './utils';
import './main';
import './support';
Now, since another_file.js is built with a single file, we can just rename that file from another_file.js
to another_file.bundle.js
Now, create a file named my_app.bundle.less
in the public/less
directory and import the 2 less files.
@import './components.less';
@import './style.less';
Now, assuming you included some of these files as part of app bundle or website bundle in hooks.py, you may need to do the following changes:
- app_include_js = ['/assets/js/my_app.js']
+ app_include_js = ['my_app.bundle.js']
- app_include_css = ['/assets/css/my_app.css']
+ app_include_css = ['my_app.bundle.css']
If you included the assets manually by explicitly writing the script tag in HTML files then you need to do the following changes:
// for js files
- <script src="/assets/js/my_app.js" type="text/javascript">
+ {{ include_script('my_app.bundle.js') }}
// for css files
- <link href="/assets/css/my_app.css" rel="stylesheet">
+ {{ include_style('my_app.bundle.css') }}
If you were lazy loading assets using frappe.require
you need to do the following changes:
- frappe.require('/assets/js/another_file.js', ...)
+ frappe.require('another_file.bundle.js', ...)
You can test if your bundles are being compiled by running the bench build command for your app:
bench build --app my_app
Finally, you can delete the build.json
file, you no longer need it.
Version 14 now targets ES2017 aka ES8 by default. So if you use any modern JS syntax like optional chaining in your bundled javascript then it will be automatically transpiled to support previous versions. This lets developers use modern syntax without having to worry about browser compatibility.
Read more here: https://github.com/frappe/frappe/pull/16491
There was a major refractor done for website routing and rendering. During this refactor, few methods were moved to different files. You might have to change the following code in your custom app.
- from frappe.website.render import render
+ from frappe.website.serve import get_response
...
- response = render()
+ response = get_response()
- from frappe.website.render import clear_cache
+ from frappe.website.utils import clear_cache
The Workspace is now upgraded. There were standard workspaces which get generated by JSON files stored in a particular module folder inside the workspace folder. (I will suggest creating a new workspace (copy of your existing workspace) using the awesome Workspace 2.0 feature. It is a much faster approach)
Let's take the example of the build.json file in Frappe App:
// frappe/core/workspace/build/build.json
frappe
│
└───core
│ │
│ └───workspace
| └───build
| build.json
If you have any such JSON files in your Custom App you might have to do the following changes in that JSON file.
- "category": "Modules",
- "is_standard": 1,
- "developer_mode_only": 0,
- "disable_user_customization": 0,
- "extends_another_page": 0,
- "pin_to_bottom": 0,
- "pin_to_top": 0,
- "extends": "",
+ "for_user": "",
+ "parent_page": "",
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 31,
+ "title": "Build",
if you have links update each links in following way:
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Data",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Import Data",
+ "link_count": 0,
"link_to": "Data Import",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
...
]
Also need to add the content on the page which is rendered based on json array which we can create using below content.
Header:
{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}
Chart:
{\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}
Shortcut: shortcut_name is the label of shortcuts.
{\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"System Settings\", \"col\": 4}}
Card: card_name is the label of links of type Card Break.
{\"type\": \"card\", \"data\": {\"card_name\": \"Data\", \"col\": 4}}
On-Boarding:
{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}
Spacer: It is used to add gap between Shortcuts and Cards.
{\"type\": \"spacer\", \"data\": {\"col\": 12}}
Combine this based on onboarding, charts, shortcuts, or links in the page to get the content as shown below.
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"DocType\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Workspace\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Report\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Elements\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Modules\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Models\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Views\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Scripting\", \"col\": 4}}]",
frappe.db.exists
now only returns value of name
if any were found, else None
. Previously when passing document dictionary a list of documents were retrieved. If you relied on this behaviour, consider replacing exists
with get_all
.
New signature of db.exists
:
def exists(dt: str | dict, dn: str | dict | list = None, cache: bool = False) -> str | None:
...
-
dt
can be of type-
str
: the name of a doctype -
dict
: filters in the standard frappe syntax. Filters have to include the"doctype"
key. When passing filters, all other parameters will have no effect and can be left empty.
-
-
dn
is needed only ifdt
was passed as a string. It can be of type:-
str
: name of one specific document. Use this if you want to check if a document with this name exists. -
dict
orlist
: filters in the standard frappe syntax. Use this to check if a document matching the filter values exists.
-
-
cache
only works ifdt
anddn
are both strings. In this case we cache the result, ifcache
is set toTrue
.
Frappe's video player is now asynchronously loaded on demand. So if you're using frappe.Plyr
you need to first load the required libraries using utility function like this.
frappe.utils.load_video_player().then(() => {
plyr = new frappe.Plyr(...);
})
In v14 and Frappe bench 5.12, dependencies that are only used during development are specificied in separate section in pyproject.toml
section. Example of Frappe Framework's own developer dependencies.
# pyproject.toml
[tool.bench.dev-dependencies]
coverage = "~=6.4.1"
Faker = "~=13.12.1"
pyngrok = "~=5.0.5"
unittest-xml-reporting = "~=3.0.4"
These development dependencies are installed by default in developer mode. They can also be manually installed by using bench setup requirements --dev
get_cached_value
uses get_cached_doc
internally which used to raise DoesNotExistError
if document wasn't found. Since this behavior was inconsistent with other database APIs like frappe.db.get_value
the behavior was changed to return None
instead of raising an exception.
key
is now a mandatory parameter when using doc.get
, it no longer returns the the internal __dict__
if key
is not passed. This is to avoid hard to trace bugs and maintain consistency with how dict.get
works.
PR: https://github.com/frappe/frappe/pull/15784
Consider the following API call from a browser client:
fetch('/api/method/app.api.buy_fruits', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fruits: ['Apple', 'Orange']
})
})
api.py
@frappe.whitelist()
def buy_fruits(fruits):
print(fruits) # 'Apple' instead of ['Apple', 'Orange']
As you can see, the value passed in fruits is 'Apple' instead of the list we passed. This is weird and incorrect behavior but is now fixed in Version 14. If you relied on this behavior, you need to make changes on your client-side API calls or server-side whitelisted methods based on your use case.
If you are one of the 2 people who is using the Data Migration Tool for syncing data between an external service and a Frappe site, you need to write your own syncing code because Data Migration Tool is removed from Version 14.
If you absolutely want to use the tool, you can pick up the code from the older version and move it into an app and use that instead.
log_error now supports two more parameters to link errors to a particular document.
To simplify error logging this function's signature was changed to flip order of title
and message`.
def log_error(title=None, message=None, reference_doctype=None, reference_name=None):
...
If you did not specify keyword argument manually, you don't need to change anyting. If you have specified title
explicitly and not specified message
then you should update function call as per new signature. Example:
- frappe.log_error("Some message", title="error title")
+ frappe.log_error(message="Some message", title="error title")
These three fields are removed from non-child doctypes. These fields were never used on non-child doctypes. If your code directly accesses three fields you might need to check if the doctype is child type first. Alternatively, you can use doc.get
to safely check and get value if it exists.
Frappe and ERPNext are now built using a single pyproject.toml
file specifying all the dependencies, development dependencies etc. This build system replaces old setup.py
and requirements.txt
Read more about this on the PR: https://github.com/frappe/frappe/pull/17174
Virtual doctypes need to implement a certain set of methods on doctype controller so it can use other data sources as backend. Version 14 improves this interface requirement by converting few object methods to static methods like get_list
and get_count
.
You can read more about the changed requirements and a practical example on the documentation page: https://frappeframework.com/docs/v14/user/en/basics/doctypes/virtual-doctype
Rating fields used to show value from 0 to 5 start before. Number of stars are now made configurable. This however also means that the storage underlying value needs to be changed to reflect the actual number of starts. Now Rating fields store a decimal number between 0 and 1 representing the rating out of the specified number of stars.
Stars | Previous Value | New Value |
---|---|---|
* * * * * |
5 | 1.0 |
* * * * . |
4 | 0.8 |
. . . . . |
0 | 0.0 |
* * * * * * * * . . |
Not possible | 0.8 |
If you performed any arithmetic operations on the rating field values you need to change the code to consider normalized values.
We have removed following libraries from the codebase in version 14. If you have used these library in your app, please add them manually for your app.
Javascript
- fluxify
- bootstrap.js (v3)
- jquery.hotkeys.js
- prettyDate.js
- bootstrap_theme
- express
- fuse.js
- hyper.js
Python
- html2text
- md5
- paytmchecksum
- razorpay
- stripe
- braintree
Icons
- glyphicons
- font-awesome (moved to separate folder)
- /assets/frappe/css/font-awesome.css
+ /assets/frappe/css/fonts/fontawesome/font-awesome.min.css # use minified version
We have also removed local copy of sockect.io & sortableJS and used these libraries that are pulled from package managers. So you might have to update the path of these libraries in your application.
We have moved all the payment gateways (Braintree, Stripe, PayPal, PayTM, Razorpay - along with Payment Gateway DocType) as well as it's dependencies from frappe to a separate app payments. To install this app, run following commands:
$ bench get-app payments
$ bench --site <your-site-name> install-app payments