mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Import data fix (#6253)
* Use '--natural-foreign' when exporting dataset - Uses "natural keys" (model names) instead of ContentType ID * Update task options - Change 'include_plugins' to 'exclude_plugins' - Change 'delete_temp' to 'retain_temp' * Split data import into two-step process - First, import auth models - Second, import data - Ensures auth.user and auth.group are in place before users.owner is loaded * Adjust temp file name * Touch apps.py - Just so the proper CI checks run
This commit is contained in:
parent
829e01dd33
commit
fa28697799
@ -1,4 +1,4 @@
|
|||||||
"""AppConfig for inventree app."""
|
"""AppConfig for InvenTree app."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
86
tasks.py
86
tasks.py
@ -59,7 +59,10 @@ def apps():
|
|||||||
|
|
||||||
|
|
||||||
def content_excludes(
|
def content_excludes(
|
||||||
allow_tokens: bool = True, allow_plugins: bool = True, allow_sso: bool = True
|
allow_auth: bool = True,
|
||||||
|
allow_tokens: bool = True,
|
||||||
|
allow_plugins: bool = True,
|
||||||
|
allow_sso: bool = True,
|
||||||
):
|
):
|
||||||
"""Returns a list of content types to exclude from import/export.
|
"""Returns a list of content types to exclude from import/export.
|
||||||
|
|
||||||
@ -83,6 +86,11 @@ def content_excludes(
|
|||||||
'user_sessions.session',
|
'user_sessions.session',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Optionally exclude user auth data
|
||||||
|
if not allow_auth:
|
||||||
|
excludes.append('auth.group')
|
||||||
|
excludes.append('auth.user')
|
||||||
|
|
||||||
# Optionally exclude user token information
|
# Optionally exclude user token information
|
||||||
if not allow_tokens:
|
if not allow_tokens:
|
||||||
excludes.append('users.apitoken')
|
excludes.append('users.apitoken')
|
||||||
@ -421,9 +429,9 @@ def update(c, skip_backup=False, frontend: bool = False, no_frontend: bool = Fal
|
|||||||
'overwrite': 'Overwrite existing files without asking first (default = False)',
|
'overwrite': 'Overwrite existing files without asking first (default = False)',
|
||||||
'include_permissions': 'Include user and group permissions in the output file (default = False)',
|
'include_permissions': 'Include user and group permissions in the output file (default = False)',
|
||||||
'include_tokens': 'Include API tokens in the output file (default = False)',
|
'include_tokens': 'Include API tokens in the output file (default = False)',
|
||||||
'include_plugins': 'Include plugin data in the output file (default = False)',
|
'exclude_plugins': 'Exclude plugin data from the output file (default = False)',
|
||||||
'include_sso': 'Include SSO token data in the output file (default = False)',
|
'include_sso': 'Include SSO token data in the output file (default = False)',
|
||||||
'delete_temp': 'Delete temporary files (containing permissions) at end of run. Note that this will delete temporary files from previous runs as well. (default = off/False)',
|
'retain_temp': 'Retain temporary files (containing permissions) at end of process (default = False)',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
def export_records(
|
def export_records(
|
||||||
@ -432,9 +440,9 @@ def export_records(
|
|||||||
overwrite=False,
|
overwrite=False,
|
||||||
include_permissions=False,
|
include_permissions=False,
|
||||||
include_tokens=False,
|
include_tokens=False,
|
||||||
include_plugins=False,
|
exclude_plugins=False,
|
||||||
include_sso=False,
|
include_sso=False,
|
||||||
delete_temp=False,
|
retain_temp=False,
|
||||||
):
|
):
|
||||||
"""Export all database records to a file.
|
"""Export all database records to a file.
|
||||||
|
|
||||||
@ -465,11 +473,11 @@ def export_records(
|
|||||||
|
|
||||||
excludes = content_excludes(
|
excludes = content_excludes(
|
||||||
allow_tokens=include_tokens,
|
allow_tokens=include_tokens,
|
||||||
allow_plugins=include_plugins,
|
allow_plugins=not exclude_plugins,
|
||||||
allow_sso=include_sso,
|
allow_sso=include_sso,
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd = f"dumpdata --indent 2 --output '{tmpfile}' {excludes}"
|
cmd = f"dumpdata --natural-foreign --indent 2 --output '{tmpfile}' {excludes}"
|
||||||
|
|
||||||
# Dump data to temporary file
|
# Dump data to temporary file
|
||||||
manage(c, cmd, pty=True)
|
manage(c, cmd, pty=True)
|
||||||
@ -497,16 +505,22 @@ def export_records(
|
|||||||
|
|
||||||
print('Data export completed')
|
print('Data export completed')
|
||||||
|
|
||||||
if delete_temp is True:
|
if not retain_temp:
|
||||||
print('Removing temporary file')
|
print('Removing temporary files')
|
||||||
os.remove(tmpfile)
|
os.remove(tmpfile)
|
||||||
|
|
||||||
|
|
||||||
@task(
|
@task(
|
||||||
help={'filename': 'Input filename', 'clear': 'Clear existing data before import'},
|
help={
|
||||||
|
'filename': 'Input filename',
|
||||||
|
'clear': 'Clear existing data before import',
|
||||||
|
'retain_temp': 'Retain temporary files at end of process (default = False)',
|
||||||
|
},
|
||||||
post=[rebuild_models, rebuild_thumbnails],
|
post=[rebuild_models, rebuild_thumbnails],
|
||||||
)
|
)
|
||||||
def import_records(c, filename='data.json', clear=False):
|
def import_records(
|
||||||
|
c, filename='data.json', clear: bool = False, retain_temp: bool = False
|
||||||
|
):
|
||||||
"""Import database records from a file."""
|
"""Import database records from a file."""
|
||||||
# Get an absolute path to the supplied filename
|
# Get an absolute path to the supplied filename
|
||||||
if not os.path.isabs(filename):
|
if not os.path.isabs(filename):
|
||||||
@ -521,11 +535,22 @@ def import_records(c, filename='data.json', clear=False):
|
|||||||
|
|
||||||
print(f"Importing database records from '{filename}'")
|
print(f"Importing database records from '{filename}'")
|
||||||
|
|
||||||
|
# We need to load 'auth' data (users / groups) *first*
|
||||||
|
# This is due to the users.owner model, which has a ContentType foreign key
|
||||||
|
authfile = f'{filename}.auth.json'
|
||||||
|
|
||||||
# Pre-process the data, to remove any "permissions" specified for a user or group
|
# Pre-process the data, to remove any "permissions" specified for a user or group
|
||||||
tmpfile = f'{filename}.tmp.json'
|
datafile = f'{filename}.data.json'
|
||||||
|
|
||||||
with open(filename, 'r') as f_in:
|
with open(filename, 'r') as f_in:
|
||||||
data = json.loads(f_in.read())
|
try:
|
||||||
|
data = json.loads(f_in.read())
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
print(f'Error: Failed to decode JSON file: {exc}')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
auth_data = []
|
||||||
|
load_data = []
|
||||||
|
|
||||||
for entry in data:
|
for entry in data:
|
||||||
if 'model' in entry:
|
if 'model' in entry:
|
||||||
@ -537,14 +562,41 @@ def import_records(c, filename='data.json', clear=False):
|
|||||||
if entry['model'] == 'auth.user':
|
if entry['model'] == 'auth.user':
|
||||||
entry['fields']['user_permissions'] = []
|
entry['fields']['user_permissions'] = []
|
||||||
|
|
||||||
# Write the processed data to the tmp file
|
# Save auth data for later
|
||||||
with open(tmpfile, 'w') as f_out:
|
if entry['model'].startswith('auth.'):
|
||||||
f_out.write(json.dumps(data, indent=2))
|
auth_data.append(entry)
|
||||||
|
else:
|
||||||
|
load_data.append(entry)
|
||||||
|
else:
|
||||||
|
print('Warning: Invalid entry in data file')
|
||||||
|
print(entry)
|
||||||
|
|
||||||
cmd = f"loaddata '{tmpfile}' -i {content_excludes()}"
|
# Write the auth file data
|
||||||
|
with open(authfile, 'w') as f_out:
|
||||||
|
f_out.write(json.dumps(auth_data, indent=2))
|
||||||
|
|
||||||
|
# Write the processed data to the tmp file
|
||||||
|
with open(datafile, 'w') as f_out:
|
||||||
|
f_out.write(json.dumps(load_data, indent=2))
|
||||||
|
|
||||||
|
excludes = content_excludes(allow_auth=False)
|
||||||
|
|
||||||
|
# Import auth models first
|
||||||
|
print('Importing user auth data...')
|
||||||
|
cmd = f"loaddata '{authfile}'"
|
||||||
|
manage(c, cmd, pty=True)
|
||||||
|
|
||||||
|
# Import everything else next
|
||||||
|
print('Importing database records...')
|
||||||
|
cmd = f"loaddata '{datafile}' -i {excludes}"
|
||||||
|
|
||||||
manage(c, cmd, pty=True)
|
manage(c, cmd, pty=True)
|
||||||
|
|
||||||
|
if not retain_temp:
|
||||||
|
print('Removing temporary files')
|
||||||
|
os.remove(datafile)
|
||||||
|
os.remove(authfile)
|
||||||
|
|
||||||
print('Data import completed')
|
print('Data import completed')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user