Skip to content

Commit

Permalink
feat: handle deprecation for NullBooleanField
Browse files Browse the repository at this point in the history
  • Loading branch information
Bruno Alla authored and browniebroke committed Apr 22, 2021
1 parent 18a93b9 commit d2cb1c5
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 4 deletions.
7 changes: 6 additions & 1 deletion django_codemod/visitors/__init__.py
Expand Up @@ -18,7 +18,11 @@
IsSafeUrlTransformer,
)
from .lru_cache import LRUCacheTransformer
from .models import ModelsPermalinkTransformer, OnDeleteTransformer
from .models import (
ModelsPermalinkTransformer,
NullBooleanFieldTransformer,
OnDeleteTransformer,
)
from .os_utils import AbsPathTransformer
from .paginator import QuerySetPaginatorTransformer
from .postgres_fields import (
Expand Down Expand Up @@ -60,6 +64,7 @@
"JSONModelFieldTransformer",
"LRUCacheTransformer",
"ModelsPermalinkTransformer",
"NullBooleanFieldTransformer",
"OnDeleteTransformer",
"QuerySetPaginatorTransformer",
"RenderToResponseTransformer",
Expand Down
29 changes: 29 additions & 0 deletions django_codemod/visitors/base.py
Expand Up @@ -4,6 +4,7 @@

from libcst import (
Arg,
AssignEqual,
Attribute,
BaseExpression,
BaseSmallStatement,
Expand All @@ -16,6 +17,7 @@
Name,
RemovalSentinel,
RemoveFromParent,
SimpleWhitespace,
)
from libcst import matchers as m
from libcst.codemod import CodemodContext, ContextAwareTransformer
Expand Down Expand Up @@ -45,6 +47,33 @@ def import_from_matches(node: ImportFrom, module_parts: Sequence[str]) -> bool:
return m.matches(node, m.ImportFrom(module=module_matcher(module_parts)))


def make_kwarg(name, value):
"""Helper to add simple kwarg to a function call.
By default, libCST adds some spaces around the equal sign:
func(name = value)
This helper builds it without spaces:
func(name=value)
"""
return Arg(
value=value,
keyword=Name(
value=name,
),
equal=AssignEqual(
whitespace_before=SimpleWhitespace(
value="",
),
whitespace_after=SimpleWhitespace(
value="",
),
),
)


class BaseRenameTransformer(BaseDjCodemodTransformer, ABC):
"""Base class to help rename or move a declaration."""

Expand Down
37 changes: 34 additions & 3 deletions django_codemod/visitors/models.py
@@ -1,4 +1,4 @@
from typing import Optional, Union
from typing import Optional, Sequence, Union

from libcst import (
Arg,
Expand All @@ -20,9 +20,21 @@
from libcst import matchers as m
from libcst.codemod.visitors import AddImportsVisitor

from django_codemod.constants import DJANGO_1_9, DJANGO_1_11, DJANGO_2_0, DJANGO_2_1
from django_codemod.constants import (
DJANGO_1_9,
DJANGO_1_11,
DJANGO_2_0,
DJANGO_2_1,
DJANGO_3_1,
DJANGO_4_0,
)
from django_codemod.utils.calls import find_keyword_arg
from django_codemod.visitors.base import BaseDjCodemodTransformer, module_matcher
from django_codemod.visitors.base import (
BaseDjCodemodTransformer,
BaseFuncRenameTransformer,
make_kwarg,
module_matcher,
)


class ModelsPermalinkTransformer(BaseDjCodemodTransformer):
Expand Down Expand Up @@ -190,3 +202,22 @@ def leave_Call(self, original_node: Call, updated_node: Call) -> BaseExpression:
)
return updated_node.with_changes(args=updated_args)
return super().leave_Call(original_node, updated_node)


class NullBooleanFieldTransformer(BaseFuncRenameTransformer):
"""Replace `NullBooleanField` by `BooleanField` with `null=True`."""

deprecated_in = DJANGO_3_1
removed_in = DJANGO_4_0

rename_from = "django.db.models.NullBooleanField"
rename_to = "django.db.models.BooleanField"

def update_call_args(self, node: Call) -> Sequence[Arg]:
return (
*node.args,
make_kwarg(
"null",
Name(value="True"),
),
)
39 changes: 39 additions & 0 deletions tests/visitors/test_models.py
Expand Up @@ -2,6 +2,7 @@

from django_codemod.visitors.models import (
ModelsPermalinkTransformer,
NullBooleanFieldTransformer,
OnDeleteTransformer,
)
from tests.visitors.base import BaseVisitorTest
Expand Down Expand Up @@ -344,3 +345,41 @@ def get_name(self):
return 'World'
"""
self.assertCodemod(before, after)


class TestNullBooleanFieldTransformer(BaseVisitorTest):

transformer = NullBooleanFieldTransformer

def test_noop_models(self) -> None:
before = after = """
from django.db import models
class MyThing(models.Model):
is_active = models.BooleanField()
"""
self.assertCodemod(before, after)

def test_noop_fields(self) -> None:
before = after = """
from django.db.models import BooleanField, Model
class MyThing(Model):
is_active = BooleanField()
"""
self.assertCodemod(before, after)

def test_simple_substitution(self) -> None:
before = """
from django.db import models
class MyThing(models.Model):
is_active = models.NullBooleanField()
"""
after = """
from django.db import models
class MyThing(models.Model):
is_active = models.BooleanField(null=True)
"""
self.assertCodemod(before, after)

0 comments on commit d2cb1c5

Please sign in to comment.