11import ast
22from abc import abstractmethod
3- from typing import Any , Iterator , Type
3+ from typing import Any , Iterator , Type , Union
44
55import django .core .checks
66from django import forms
1616class CheckModelField (BaseCheck ):
1717 @abstractmethod
1818 def apply (
19- self , field : object , * , field_ast : FieldAST , model : Type [models .Model ]
19+ self ,
20+ field : models .fields .Field ,
21+ * ,
22+ field_ast : FieldAST ,
23+ model : Type [models .Model ],
2024 ) -> Iterator [django .core .checks .CheckMessage ]:
2125 raise NotImplementedError ()
2226
27+ def is_ignored (self , obj : Any ) -> bool :
28+ return obj .model in self .ignore_objects or type (obj ) in self .ignore_types
29+
2330
2431class GetTextMixin (BaseCheckMixin ):
2532 class GettTextFuncForm (BaseCheckForm ):
@@ -38,14 +45,32 @@ def _is_gettext_node(self, node: ast.AST) -> bool:
3845 )
3946
4047
48+ def get_verbose_name (
49+ field : models .fields .Field , field_ast : FieldAST
50+ ) -> Union [None , ast .Constant , ast .Call ]:
51+ result = field_ast .verbose_name
52+ if result :
53+ return result
54+ if isinstance (field , models .fields .related .RelatedField ):
55+ return None
56+ if field_ast .args :
57+ node = field_ast .args [0 ]
58+ if isinstance (node , ast .Call ) and hasattr (node .func , "id" ):
59+ return node
60+ elif isinstance (node , (ast .Constant , ast .Str )):
61+ return node
62+ return None
63+
64+
4165@registry .register (django .core .checks .Tags .models )
4266class CheckFieldVerboseName (CheckModelField ):
4367 Id = CheckId .X050
4468
4569 def apply (
46- self , field : object , field_ast : FieldAST , ** kwargs : Any
70+ self , field : models . fields . Field , field_ast : FieldAST , ** kwargs : Any
4771 ) -> Iterator [django .core .checks .CheckMessage ]:
48- if not field_ast .verbose_name :
72+ verbose_name = get_verbose_name (field , field_ast )
73+ if not verbose_name :
4974 yield self .message (
5075 "Field has no verbose name." ,
5176 hint = "Set verbose name on the field." ,
@@ -58,9 +83,10 @@ class CheckFieldVerboseNameGettext(GetTextMixin, CheckModelField):
5883 Id = CheckId .X051
5984
6085 def apply (
61- self , field : object , field_ast : FieldAST , ** kwargs : Any
86+ self , field : models . fields . Field , field_ast : FieldAST , ** kwargs : Any
6287 ) -> Iterator [django .core .checks .CheckMessage ]:
63- if field_ast .verbose_name and not self ._is_gettext_node (field_ast .verbose_name ):
88+ verbose_name = get_verbose_name (field , field_ast )
89+ if verbose_name and not self ._is_gettext_node (verbose_name ):
6490 yield self .message (
6591 "Verbose name should use gettext." ,
6692 hint = "Use gettext on the verbose name." ,
@@ -73,10 +99,11 @@ class CheckFieldVerboseNameGettextCase(GetTextMixin, CheckModelField):
7399 Id = CheckId .X052
74100
75101 def apply (
76- self , field : object , field_ast : FieldAST , ** kwargs : Any
102+ self , field : models . fields . Field , field_ast : FieldAST , ** kwargs : Any
77103 ) -> Iterator [django .core .checks .CheckMessage ]:
78- if field_ast .verbose_name and self ._is_gettext_node (field_ast .verbose_name ):
79- value = field_ast .verbose_name .args [0 ].s # type: ignore
104+ verbose_name = get_verbose_name (field , field_ast )
105+ if verbose_name and self ._is_gettext_node (verbose_name ):
106+ value = verbose_name .args [0 ].s # type: ignore
80107 if not all (
81108 w .islower () or w .isupper () or w .isdigit () for w in value .split (" " )
82109 ):
@@ -92,7 +119,7 @@ class CheckFieldHelpTextGettext(GetTextMixin, CheckModelField):
92119 Id = CheckId .X053
93120
94121 def apply (
95- self , field : object , field_ast : FieldAST , ** kwargs : Any
122+ self , field : models . fields . Field , field_ast : FieldAST , ** kwargs : Any
96123 ) -> Iterator [django .core .checks .CheckMessage ]:
97124 if field_ast .help_text and not self ._is_gettext_node (field_ast .help_text ):
98125 yield self .message (
@@ -107,7 +134,7 @@ class CheckFieldFileUploadTo(CheckModelField):
107134 Id = CheckId .X054
108135
109136 def apply (
110- self , field : object , ** kwargs : Any
137+ self , field : models . fields . Field , ** kwargs : Any
111138 ) -> Iterator [django .core .checks .CheckMessage ]:
112139 if isinstance (field , models .FileField ):
113140 if not field .upload_to :
@@ -123,7 +150,7 @@ class CheckFieldTextNull(CheckModelField):
123150 Id = CheckId .X055
124151
125152 def apply (
126- self , field : object , ** kwargs : Any
153+ self , field : models . fields . Field , ** kwargs : Any
127154 ) -> Iterator [django .core .checks .CheckMessage ]:
128155 if isinstance (field , (models .CharField , models .TextField )):
129156 if field .null :
@@ -140,7 +167,7 @@ class CheckFieldNullBoolean(CheckModelField):
140167 Id = CheckId .X056
141168
142169 def apply (
143- self , field : object , ** kwargs : Any
170+ self , field : models . fields . Field , ** kwargs : Any
144171 ) -> Iterator [django .core .checks .CheckMessage ]:
145172 if isinstance (field , models .NullBooleanField ):
146173 yield self .message (
@@ -155,15 +182,14 @@ class CheckFieldNullFalse(CheckModelField):
155182 Id = CheckId .X057
156183
157184 def apply (
158- self , field : object , field_ast : FieldAST , ** kwargs : Any
185+ self , field : models . fields . Field , field_ast : FieldAST , ** kwargs : Any
159186 ) -> Iterator [django .core .checks .CheckMessage ]:
160- if isinstance (field , models .fields .Field ):
161- if field .null is False and "null" in field_ast .kwargs :
162- yield self .message (
163- "Argument `null=False` is default." ,
164- hint = "Remove `null=False` from field arguments." ,
165- obj = field ,
166- )
187+ if field .null is False and "null" in field_ast .kwargs :
188+ yield self .message (
189+ "Argument `null=False` is default." ,
190+ hint = "Remove `null=False` from field arguments." ,
191+ obj = field ,
192+ )
167193
168194
169195@registry .register (django .core .checks .Tags .models )
@@ -183,7 +209,10 @@ def __init__(self, when: str, **kwargs: Any) -> None:
183209 super ().__init__ (** kwargs )
184210
185211 def apply (
186- self , field : object , field_ast : FieldAST , model : Type [models .Model ],
212+ self ,
213+ field : models .fields .Field ,
214+ field_ast : FieldAST ,
215+ model : Type [models .Model ],
187216 ) -> Iterator [django .core .checks .CheckMessage ]:
188217 if isinstance (field , models .fields .related .RelatedField ):
189218 if field .many_to_one and field_ast .kwargs .get ("db_index" ) is None :
0 commit comments