mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-08 13:04:47 +08:00
make BaseModelAdmin generic to properly type methods dealing with models (#504)
* make BaseModelAdmin generic to properly type the `obj` argument of ModelAdmin.delete_model closes #482 * turn BaseModelAdmin into bound generic, run black * add test for generic ModelAdmin
This commit is contained in:
@@ -1,5 +1,20 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, Mapping, TypeVar
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
Union,
|
||||||
|
Mapping,
|
||||||
|
TypeVar,
|
||||||
|
)
|
||||||
|
|
||||||
from django.forms.forms import BaseForm
|
from django.forms.forms import BaseForm
|
||||||
from django.forms.formsets import BaseFormSet
|
from django.forms.formsets import BaseFormSet
|
||||||
@@ -57,7 +72,11 @@ _T = TypeVar("_T")
|
|||||||
_ListOrTuple = Union[Tuple[_T, ...], List[_T]]
|
_ListOrTuple = Union[Tuple[_T, ...], List[_T]]
|
||||||
_FieldsetSpec = _ListOrTuple[Tuple[Optional[str], _FieldOpts]]
|
_FieldsetSpec = _ListOrTuple[Tuple[Optional[str], _FieldOpts]]
|
||||||
|
|
||||||
class BaseModelAdmin:
|
# Generic type specifically for models, for use in BaseModelAdmin and subclasses
|
||||||
|
# https://github.com/typeddjango/django-stubs/issues/482
|
||||||
|
_ModelT = TypeVar("_ModelT", bound=Model)
|
||||||
|
|
||||||
|
class BaseModelAdmin(Generic[_ModelT]):
|
||||||
autocomplete_fields: Sequence[str] = ...
|
autocomplete_fields: Sequence[str] = ...
|
||||||
raw_id_fields: Sequence[str] = ...
|
raw_id_fields: Sequence[str] = ...
|
||||||
fields: Sequence[Union[str, Sequence[str]]] = ...
|
fields: Sequence[Union[str, Sequence[str]]] = ...
|
||||||
@@ -69,7 +88,7 @@ class BaseModelAdmin:
|
|||||||
radio_fields: Mapping[str, _Direction] = ...
|
radio_fields: Mapping[str, _Direction] = ...
|
||||||
prepopulated_fields: Mapping[str, Sequence[str]] = ...
|
prepopulated_fields: Mapping[str, Sequence[str]] = ...
|
||||||
formfield_overrides: Mapping[Type[Field], Mapping[str, Any]] = ...
|
formfield_overrides: Mapping[Type[Field], Mapping[str, Any]] = ...
|
||||||
readonly_fields: Sequence[Union[str, Callable[[Model], Any]]] = ...
|
readonly_fields: Sequence[Union[str, Callable[[_ModelT], Any]]] = ...
|
||||||
ordering: Sequence[str] = ...
|
ordering: Sequence[str] = ...
|
||||||
sortable_by: Sequence[str] = ...
|
sortable_by: Sequence[str] = ...
|
||||||
view_on_site: bool = ...
|
view_on_site: bool = ...
|
||||||
@@ -92,28 +111,28 @@ class BaseModelAdmin:
|
|||||||
self, db_field: ManyToManyField, request: Optional[HttpRequest], **kwargs: Any
|
self, db_field: ManyToManyField, request: Optional[HttpRequest], **kwargs: Any
|
||||||
) -> ModelMultipleChoiceField: ...
|
) -> ModelMultipleChoiceField: ...
|
||||||
def get_autocomplete_fields(self, request: HttpRequest) -> Tuple: ...
|
def get_autocomplete_fields(self, request: HttpRequest) -> Tuple: ...
|
||||||
def get_view_on_site_url(self, obj: Optional[Model] = ...) -> Optional[str]: ...
|
def get_view_on_site_url(self, obj: Optional[_ModelT] = ...) -> Optional[str]: ...
|
||||||
def get_empty_value_display(self) -> SafeText: ...
|
def get_empty_value_display(self) -> SafeText: ...
|
||||||
def get_exclude(self, request: HttpRequest, obj: Optional[Model] = ...) -> Any: ...
|
def get_exclude(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Any: ...
|
||||||
def get_fields(self, request: HttpRequest, obj: Optional[Model] = ...) -> Sequence[Union[Callable, str]]: ...
|
def get_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Sequence[Union[Callable, str]]: ...
|
||||||
def get_fieldsets(
|
def get_fieldsets(
|
||||||
self, request: HttpRequest, obj: Optional[Model] = ...
|
self, request: HttpRequest, obj: Optional[_ModelT] = ...
|
||||||
) -> List[Tuple[Optional[str], Dict[str, Any]]]: ...
|
) -> List[Tuple[Optional[str], Dict[str, Any]]]: ...
|
||||||
def get_ordering(self, request: HttpRequest) -> Union[List[str], Tuple]: ...
|
def get_ordering(self, request: HttpRequest) -> Union[List[str], Tuple]: ...
|
||||||
def get_readonly_fields(self, request: HttpRequest, obj: Optional[Model] = ...) -> Union[List[str], Tuple]: ...
|
def get_readonly_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Union[List[str], Tuple]: ...
|
||||||
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[Model] = ...) -> Dict[str, Tuple[str]]: ...
|
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Dict[str, Tuple[str]]: ...
|
||||||
def get_queryset(self, request: HttpRequest) -> QuerySet: ...
|
def get_queryset(self, request: HttpRequest) -> QuerySet: ...
|
||||||
def get_sortable_by(self, request: HttpRequest) -> Union[List[Callable], List[str], Tuple]: ...
|
def get_sortable_by(self, request: HttpRequest) -> Union[List[Callable], List[str], Tuple]: ...
|
||||||
def lookup_allowed(self, lookup: str, value: str) -> bool: ...
|
def lookup_allowed(self, lookup: str, value: str) -> bool: ...
|
||||||
def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ...
|
def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ...
|
||||||
def has_add_permission(self, request: HttpRequest) -> bool: ...
|
def has_add_permission(self, request: HttpRequest) -> bool: ...
|
||||||
def has_change_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
def has_change_permission(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> bool: ...
|
||||||
def has_delete_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
def has_delete_permission(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> bool: ...
|
||||||
def has_view_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
def has_view_permission(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> bool: ...
|
||||||
def has_module_permission(self, request: HttpRequest) -> bool: ...
|
def has_module_permission(self, request: HttpRequest) -> bool: ...
|
||||||
|
|
||||||
class ModelAdmin(BaseModelAdmin):
|
class ModelAdmin(BaseModelAdmin[_ModelT]):
|
||||||
list_display: Sequence[Union[str, Callable[[Model], Any]]] = ...
|
list_display: Sequence[Union[str, Callable[[_ModelT], Any]]] = ...
|
||||||
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
|
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
|
||||||
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
|
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
|
||||||
list_select_related: Union[bool, Sequence[str]] = ...
|
list_select_related: Union[bool, Sequence[str]] = ...
|
||||||
@@ -140,24 +159,24 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
actions_on_top: bool = ...
|
actions_on_top: bool = ...
|
||||||
actions_on_bottom: bool = ...
|
actions_on_bottom: bool = ...
|
||||||
actions_selection_counter: bool = ...
|
actions_selection_counter: bool = ...
|
||||||
model: Type[Model] = ...
|
model: Type[_ModelT] = ...
|
||||||
opts: Options = ...
|
opts: Options = ...
|
||||||
admin_site: AdminSite = ...
|
admin_site: AdminSite = ...
|
||||||
def __init__(self, model: Type[Model], admin_site: Optional[AdminSite]) -> None: ...
|
def __init__(self, model: Type[_ModelT], admin_site: Optional[AdminSite]) -> None: ...
|
||||||
def get_inline_instances(self, request: HttpRequest, obj: Optional[Model] = ...) -> List[InlineModelAdmin]: ...
|
def get_inline_instances(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> List[InlineModelAdmin]: ...
|
||||||
def get_urls(self) -> List[URLPattern]: ...
|
def get_urls(self) -> List[URLPattern]: ...
|
||||||
@property
|
@property
|
||||||
def urls(self) -> List[URLPattern]: ...
|
def urls(self) -> List[URLPattern]: ...
|
||||||
@property
|
@property
|
||||||
def media(self) -> Media: ...
|
def media(self) -> Media: ...
|
||||||
def get_model_perms(self, request: HttpRequest) -> Dict[str, bool]: ...
|
def get_model_perms(self, request: HttpRequest) -> Dict[str, bool]: ...
|
||||||
def get_form(self, request: Any, obj: Optional[Any] = ..., change: bool = ..., **kwargs: Any): ...
|
def get_form(self, request: Any, obj: Optional[_ModelT] = ..., change: bool = ..., **kwargs: Any): ...
|
||||||
def get_changelist(self, request: HttpRequest, **kwargs: Any) -> Type[ChangeList]: ...
|
def get_changelist(self, request: HttpRequest, **kwargs: Any) -> Type[ChangeList]: ...
|
||||||
def get_changelist_instance(self, request: HttpRequest) -> ChangeList: ...
|
def get_changelist_instance(self, request: HttpRequest) -> ChangeList: ...
|
||||||
def get_object(self, request: HttpRequest, object_id: str, from_field: None = ...) -> Optional[Model]: ...
|
def get_object(self, request: HttpRequest, object_id: str, from_field: None = ...) -> Optional[_ModelT]: ...
|
||||||
def get_changelist_form(self, request: Any, **kwargs: Any): ...
|
def get_changelist_form(self, request: Any, **kwargs: Any): ...
|
||||||
def get_changelist_formset(self, request: Any, **kwargs: Any): ...
|
def get_changelist_formset(self, request: Any, **kwargs: Any): ...
|
||||||
def get_formsets_with_inlines(self, request: HttpRequest, obj: Optional[Model] = ...) -> Iterator[Any]: ...
|
def get_formsets_with_inlines(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Iterator[Any]: ...
|
||||||
def get_paginator(
|
def get_paginator(
|
||||||
self,
|
self,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
@@ -166,10 +185,10 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
orphans: int = ...,
|
orphans: int = ...,
|
||||||
allow_empty_first_page: bool = ...,
|
allow_empty_first_page: bool = ...,
|
||||||
) -> Paginator: ...
|
) -> Paginator: ...
|
||||||
def log_addition(self, request: HttpRequest, object: Model, message: Any) -> LogEntry: ...
|
def log_addition(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ...
|
||||||
def log_change(self, request: HttpRequest, object: Model, message: Any) -> LogEntry: ...
|
def log_change(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ...
|
||||||
def log_deletion(self, request: HttpRequest, object: Model, object_repr: str) -> LogEntry: ...
|
def log_deletion(self, request: HttpRequest, object: _ModelT, object_repr: str) -> LogEntry: ...
|
||||||
def action_checkbox(self, obj: Model) -> SafeText: ...
|
def action_checkbox(self, obj: _ModelT) -> SafeText: ...
|
||||||
def get_actions(self, request: HttpRequest) -> OrderedDict: ...
|
def get_actions(self, request: HttpRequest) -> OrderedDict: ...
|
||||||
def get_action_choices(
|
def get_action_choices(
|
||||||
self, request: HttpRequest, default_choices: List[Tuple[str, str]] = ...
|
self, request: HttpRequest, default_choices: List[Tuple[str, str]] = ...
|
||||||
@@ -198,8 +217,8 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
fail_silently: bool = ...,
|
fail_silently: bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def save_form(self, request: Any, form: Any, change: Any): ...
|
def save_form(self, request: Any, form: Any, change: Any): ...
|
||||||
def save_model(self, request: Any, obj: Any, form: Any, change: Any) -> None: ...
|
def save_model(self, request: Any, obj: _ModelT, form: Any, change: Any) -> None: ...
|
||||||
def delete_model(self, request: HttpRequest, obj: Model) -> None: ...
|
def delete_model(self, request: HttpRequest, obj: _ModelT) -> None: ...
|
||||||
def delete_queryset(self, request: HttpRequest, queryset: QuerySet) -> None: ...
|
def delete_queryset(self, request: HttpRequest, queryset: QuerySet) -> None: ...
|
||||||
def save_formset(self, request: Any, form: Any, formset: Any, change: Any) -> None: ...
|
def save_formset(self, request: Any, form: Any, formset: Any, change: Any) -> None: ...
|
||||||
def save_related(self, request: Any, form: Any, formsets: Any, change: Any) -> None: ...
|
def save_related(self, request: Any, form: Any, formsets: Any, change: Any) -> None: ...
|
||||||
@@ -210,19 +229,19 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
add: bool = ...,
|
add: bool = ...,
|
||||||
change: bool = ...,
|
change: bool = ...,
|
||||||
form_url: str = ...,
|
form_url: str = ...,
|
||||||
obj: Optional[Any] = ...,
|
obj: Optional[_ModelT] = ...,
|
||||||
): ...
|
): ...
|
||||||
def response_add(
|
def response_add(
|
||||||
self, request: HttpRequest, obj: Model, post_url_continue: Optional[str] = ...
|
self, request: HttpRequest, obj: _ModelT, post_url_continue: Optional[str] = ...
|
||||||
) -> HttpResponse: ...
|
) -> HttpResponse: ...
|
||||||
def response_change(self, request: HttpRequest, obj: Model) -> HttpResponse: ...
|
def response_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponse: ...
|
||||||
def response_post_save_add(self, request: HttpRequest, obj: Model) -> HttpResponseRedirect: ...
|
def response_post_save_add(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ...
|
||||||
def response_post_save_change(self, request: HttpRequest, obj: Model) -> HttpResponseRedirect: ...
|
def response_post_save_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ...
|
||||||
def response_action(self, request: HttpRequest, queryset: QuerySet) -> Optional[HttpResponseBase]: ...
|
def response_action(self, request: HttpRequest, queryset: QuerySet) -> Optional[HttpResponseBase]: ...
|
||||||
def response_delete(self, request: HttpRequest, obj_display: str, obj_id: int) -> HttpResponse: ...
|
def response_delete(self, request: HttpRequest, obj_display: str, obj_id: int) -> HttpResponse: ...
|
||||||
def render_delete_form(self, request: Any, context: Any): ...
|
def render_delete_form(self, request: Any, context: Any): ...
|
||||||
def get_inline_formsets(
|
def get_inline_formsets(
|
||||||
self, request: HttpRequest, formsets: List[Any], inline_instances: List[Any], obj: Optional[Model] = ...
|
self, request: HttpRequest, formsets: List[Any], inline_instances: List[Any], obj: Optional[_ModelT] = ...
|
||||||
) -> List[Any]: ...
|
) -> List[Any]: ...
|
||||||
def get_changeform_initial_data(self, request: HttpRequest) -> Dict[str, str]: ...
|
def get_changeform_initial_data(self, request: HttpRequest) -> Dict[str, str]: ...
|
||||||
def changeform_view(
|
def changeform_view(
|
||||||
@@ -246,8 +265,8 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
def delete_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> Any: ...
|
def delete_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> Any: ...
|
||||||
def history_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> HttpResponse: ...
|
def history_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> HttpResponse: ...
|
||||||
|
|
||||||
class InlineModelAdmin(BaseModelAdmin):
|
class InlineModelAdmin(BaseModelAdmin[_ModelT]):
|
||||||
model: Type[Model] = ...
|
model: Type[_ModelT] = ...
|
||||||
fk_name: str = ...
|
fk_name: str = ...
|
||||||
formset: BaseFormSet = ...
|
formset: BaseFormSet = ...
|
||||||
extra: int = ...
|
extra: int = ...
|
||||||
@@ -263,13 +282,13 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||||||
parent_model: Any = ...
|
parent_model: Any = ...
|
||||||
opts: Any = ...
|
opts: Any = ...
|
||||||
has_registered_model: Any = ...
|
has_registered_model: Any = ...
|
||||||
def __init__(self, parent_model: Union[Type[Model], Model], admin_site: AdminSite) -> None: ...
|
def __init__(self, parent_model: Union[Type[_ModelT], _ModelT], admin_site: AdminSite) -> None: ...
|
||||||
@property
|
@property
|
||||||
def media(self) -> Media: ...
|
def media(self) -> Media: ...
|
||||||
def get_extra(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> int: ...
|
def get_extra(self, request: HttpRequest, obj: Optional[_ModelT] = ..., **kwargs: Any) -> int: ...
|
||||||
def get_min_num(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> Optional[int]: ...
|
def get_min_num(self, request: HttpRequest, obj: Optional[_ModelT] = ..., **kwargs: Any) -> Optional[int]: ...
|
||||||
def get_max_num(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> Optional[int]: ...
|
def get_max_num(self, request: HttpRequest, obj: Optional[_ModelT] = ..., **kwargs: Any) -> Optional[int]: ...
|
||||||
def get_formset(self, request: Any, obj: Optional[Any] = ..., **kwargs: Any): ...
|
def get_formset(self, request: Any, obj: Optional[_ModelT] = ..., **kwargs: Any): ...
|
||||||
|
|
||||||
class StackedInline(InlineModelAdmin): ...
|
class StackedInline(InlineModelAdmin[_ModelT]): ...
|
||||||
class TabularInline(InlineModelAdmin): ...
|
class TabularInline(InlineModelAdmin[_ModelT]): ...
|
||||||
|
|||||||
@@ -14,7 +14,10 @@
|
|||||||
def an_action(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> None:
|
def an_action(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class A(admin.ModelAdmin):
|
class TestModel(models.Model):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin[TestModel]):
|
||||||
# BaseModelAdmin
|
# BaseModelAdmin
|
||||||
autocomplete_fields = ("strs",)
|
autocomplete_fields = ("strs",)
|
||||||
raw_id_fields = ["strs"]
|
raw_id_fields = ["strs"]
|
||||||
@@ -71,6 +74,11 @@
|
|||||||
actions_selection_counter = True
|
actions_selection_counter = True
|
||||||
admin_site = AdminSite()
|
admin_site = AdminSite()
|
||||||
|
|
||||||
|
# test generic ModelAdmin
|
||||||
|
# https://github.com/typeddjango/django-stubs/pull/504
|
||||||
|
# this will fail if `model` has a type other than the generic specified in the class declaration
|
||||||
|
model = TestModel
|
||||||
|
|
||||||
def a_method_action(self, request, queryset):
|
def a_method_action(self, request, queryset):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -127,4 +135,4 @@
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class A(admin.ModelAdmin):
|
class A(admin.ModelAdmin):
|
||||||
actions = [an_action] # E: List item 0 has incompatible type "Callable[[None], None]"; expected "Union[Callable[[ModelAdmin, HttpRequest, QuerySet[Any]], None], str]"
|
actions = [an_action] # E: List item 0 has incompatible type "Callable[[None], None]"; expected "Union[Callable[[ModelAdmin[Any], HttpRequest, QuerySet[Any]], None], str]"
|
||||||
|
|||||||
Reference in New Issue
Block a user