Created
January 19, 2021 11:08
-
-
Save kingbuzzman/7bd1d720a272baed08096a4a81e593b1 to your computer and use it in GitHub Desktop.
libcst django 1.11 to 2.x add on_delete=models.CASCADE if missing with formatting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# pip install libcst | |
python -c "$(cat <<EOF - | |
import libcst as cst | |
import os | |
import difflib | |
def build_arg(inline): | |
return cst.Arg( | |
value=cst.Attribute( | |
value=cst.Attribute( | |
value=cst.Name( | |
value='models', | |
), | |
attr=cst.Name( | |
value='deletion', | |
), | |
), | |
attr=cst.Name( | |
value='CASCADE', | |
), | |
), | |
keyword=cst.Name( | |
value='on_delete', | |
), | |
equal=cst.AssignEqual( | |
whitespace_before=cst.SimpleWhitespace(value=''), | |
whitespace_after=cst.SimpleWhitespace(value=''), | |
), | |
comma=cst.Comma( | |
whitespace_before=cst.SimpleWhitespace( | |
value='', | |
), | |
whitespace_after=cst.ParenthesizedWhitespace( | |
first_line=cst.TrailingWhitespace( | |
whitespace=cst.SimpleWhitespace(value=''), | |
comment=None, | |
newline=cst.Newline(value=None), | |
), | |
indent=True, | |
last_line=cst.SimpleWhitespace(value=''), | |
), | |
) if not inline else None, | |
whitespace_after_star=cst.SimpleWhitespace( | |
value=' ' if not inline else '', | |
), | |
) | |
class ForeignKeyOnDeleteTransformer(cst.CSTTransformer): | |
def __init__(self): | |
self.changes = 0 | |
def leave_Call(self, original_node, updated_node): | |
if not (updated_node.func and hasattr(updated_node.func, 'attr') and updated_node.func.attr.value in ('ForeignKey', 'OneToOneField')): | |
return updated_node | |
for arg in updated_node.args: | |
if arg.keyword and arg.keyword.value == 'on_delete': | |
# This ForeignKey already has the on_delete specified, ignore it. | |
return updated_node | |
inline_insert = cst.parse_module("").code_for_node(original_node).count('\n') == 0 | |
new_args = updated_node.args + (build_arg(inline_insert),) | |
args, second_to_last_arg, last_arg = new_args[:-2], new_args[-2], new_args[-1] | |
if not inline_insert: | |
# import ipdb; print('\a'); ipdb.sset_trace() | |
second_to_last_arg = second_to_last_arg.with_changes( | |
comma=cst.Comma(), | |
whitespace_after_arg=cst.ParenthesizedWhitespace( | |
first_line=cst.TrailingWhitespace( | |
whitespace=cst.SimpleWhitespace(value=''), | |
newline=cst.Newline(value=None), | |
), | |
indent=True, | |
last_line=cst.SimpleWhitespace(value='') | |
) | |
) | |
else: | |
# import ipdb; print('\a'); ipdb.sset_trace() | |
second_to_last_arg = second_to_last_arg.with_changes(whitespace_after_arg=cst.SimpleWhitespace(value='')) | |
# import ipdb; print('\a'); ipdb.sset_trace() | |
# if new_args[-2] | |
self.changes += 1 | |
change = updated_node.with_changes(args=args + (second_to_last_arg,) + (last_arg,)) | |
return change | |
# cst.parse_statement(cst.parse_module("").code_for_node(change)) #.replace(' \n', '\n')) | |
# cst.parse_module("").code_for_node(updated_node.with_changes(args=args + (second_to_last_arg,) + (last_arg,))) | |
for root, dirs, files in os.walk(".", topdown=False): | |
for name in files: | |
if not name.endswith('.py'): | |
continue | |
elif not ('models' in root or 'models' in name): | |
continue | |
relative_path = os.path.join(root, name) | |
with open(relative_path) as r_f: | |
source_tree = cst.parse_module(r_f.read()) | |
transformer = ForeignKeyOnDeleteTransformer() | |
modified_tree = source_tree.visit(transformer) | |
if transformer.changes: | |
with open(relative_path, 'w') as w_f: | |
w_f.write(modified_tree.code) | |
print(relative_path) | |
# print('*' * 20) | |
# print("".join(difflib.unified_diff(source_tree.code.splitlines(1), modified_tree.code.splitlines(1)))) | |
# print('*' * 20) | |
# import sys; sys.exit() | |
EOF | |
)" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment