mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 20:54:29 +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 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.formsets import BaseFormSet
|
||||
@@ -57,7 +72,11 @@ _T = TypeVar("_T")
|
||||
_ListOrTuple = Union[Tuple[_T, ...], List[_T]]
|
||||
_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] = ...
|
||||
raw_id_fields: Sequence[str] = ...
|
||||
fields: Sequence[Union[str, Sequence[str]]] = ...
|
||||
@@ -69,7 +88,7 @@ class BaseModelAdmin:
|
||||
radio_fields: Mapping[str, _Direction] = ...
|
||||
prepopulated_fields: Mapping[str, Sequence[str]] = ...
|
||||
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] = ...
|
||||
sortable_by: Sequence[str] = ...
|
||||
view_on_site: bool = ...
|
||||
@@ -92,28 +111,28 @@ class BaseModelAdmin:
|
||||
self, db_field: ManyToManyField, request: Optional[HttpRequest], **kwargs: Any
|
||||
) -> ModelMultipleChoiceField: ...
|
||||
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_exclude(self, request: HttpRequest, obj: Optional[Model] = ...) -> Any: ...
|
||||
def get_fields(self, request: HttpRequest, obj: Optional[Model] = ...) -> Sequence[Union[Callable, str]]: ...
|
||||
def get_exclude(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Any: ...
|
||||
def get_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Sequence[Union[Callable, str]]: ...
|
||||
def get_fieldsets(
|
||||
self, request: HttpRequest, obj: Optional[Model] = ...
|
||||
self, request: HttpRequest, obj: Optional[_ModelT] = ...
|
||||
) -> List[Tuple[Optional[str], Dict[str, Any]]]: ...
|
||||
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_prepopulated_fields(self, request: HttpRequest, obj: Optional[Model] = ...) -> Dict[str, Tuple[str]]: ...
|
||||
def get_readonly_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Union[List[str], Tuple]: ...
|
||||
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> Dict[str, Tuple[str]]: ...
|
||||
def get_queryset(self, request: HttpRequest) -> QuerySet: ...
|
||||
def get_sortable_by(self, request: HttpRequest) -> Union[List[Callable], List[str], Tuple]: ...
|
||||
def lookup_allowed(self, lookup: str, value: str) -> bool: ...
|
||||
def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ...
|
||||
def has_add_permission(self, request: HttpRequest) -> bool: ...
|
||||
def has_change_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_delete_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_view_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[_ModelT] = ...) -> bool: ...
|
||||
def has_view_permission(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> bool: ...
|
||||
def has_module_permission(self, request: HttpRequest) -> bool: ...
|
||||
|
||||
class ModelAdmin(BaseModelAdmin):
|
||||
list_display: Sequence[Union[str, Callable[[Model], Any]]] = ...
|
||||
class ModelAdmin(BaseModelAdmin[_ModelT]):
|
||||
list_display: Sequence[Union[str, Callable[[_ModelT], Any]]] = ...
|
||||
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
|
||||
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
|
||||
list_select_related: Union[bool, Sequence[str]] = ...
|
||||
@@ -140,24 +159,24 @@ class ModelAdmin(BaseModelAdmin):
|
||||
actions_on_top: bool = ...
|
||||
actions_on_bottom: bool = ...
|
||||
actions_selection_counter: bool = ...
|
||||
model: Type[Model] = ...
|
||||
model: Type[_ModelT] = ...
|
||||
opts: Options = ...
|
||||
admin_site: AdminSite = ...
|
||||
def __init__(self, model: Type[Model], admin_site: Optional[AdminSite]) -> None: ...
|
||||
def get_inline_instances(self, request: HttpRequest, obj: Optional[Model] = ...) -> List[InlineModelAdmin]: ...
|
||||
def __init__(self, model: Type[_ModelT], admin_site: Optional[AdminSite]) -> None: ...
|
||||
def get_inline_instances(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> List[InlineModelAdmin]: ...
|
||||
def get_urls(self) -> List[URLPattern]: ...
|
||||
@property
|
||||
def urls(self) -> List[URLPattern]: ...
|
||||
@property
|
||||
def media(self) -> Media: ...
|
||||
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_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_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(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
@@ -166,10 +185,10 @@ class ModelAdmin(BaseModelAdmin):
|
||||
orphans: int = ...,
|
||||
allow_empty_first_page: bool = ...,
|
||||
) -> Paginator: ...
|
||||
def log_addition(self, request: HttpRequest, object: Model, message: Any) -> LogEntry: ...
|
||||
def log_change(self, request: HttpRequest, object: Model, message: Any) -> LogEntry: ...
|
||||
def log_deletion(self, request: HttpRequest, object: Model, object_repr: str) -> LogEntry: ...
|
||||
def action_checkbox(self, obj: Model) -> SafeText: ...
|
||||
def log_addition(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ...
|
||||
def log_change(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ...
|
||||
def log_deletion(self, request: HttpRequest, object: _ModelT, object_repr: str) -> LogEntry: ...
|
||||
def action_checkbox(self, obj: _ModelT) -> SafeText: ...
|
||||
def get_actions(self, request: HttpRequest) -> OrderedDict: ...
|
||||
def get_action_choices(
|
||||
self, request: HttpRequest, default_choices: List[Tuple[str, str]] = ...
|
||||
@@ -198,8 +217,8 @@ class ModelAdmin(BaseModelAdmin):
|
||||
fail_silently: bool = ...,
|
||||
) -> None: ...
|
||||
def save_form(self, request: Any, form: Any, change: Any): ...
|
||||
def save_model(self, request: Any, obj: Any, form: Any, change: Any) -> None: ...
|
||||
def delete_model(self, request: HttpRequest, obj: Model) -> None: ...
|
||||
def save_model(self, request: Any, obj: _ModelT, form: Any, change: Any) -> None: ...
|
||||
def delete_model(self, request: HttpRequest, obj: _ModelT) -> 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_related(self, request: Any, form: Any, formsets: Any, change: Any) -> None: ...
|
||||
@@ -210,19 +229,19 @@ class ModelAdmin(BaseModelAdmin):
|
||||
add: bool = ...,
|
||||
change: bool = ...,
|
||||
form_url: str = ...,
|
||||
obj: Optional[Any] = ...,
|
||||
obj: Optional[_ModelT] = ...,
|
||||
): ...
|
||||
def response_add(
|
||||
self, request: HttpRequest, obj: Model, post_url_continue: Optional[str] = ...
|
||||
self, request: HttpRequest, obj: _ModelT, post_url_continue: Optional[str] = ...
|
||||
) -> HttpResponse: ...
|
||||
def response_change(self, request: HttpRequest, obj: Model) -> HttpResponse: ...
|
||||
def response_post_save_add(self, request: HttpRequest, obj: Model) -> HttpResponseRedirect: ...
|
||||
def response_post_save_change(self, request: HttpRequest, obj: Model) -> HttpResponseRedirect: ...
|
||||
def response_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponse: ...
|
||||
def response_post_save_add(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ...
|
||||
def response_post_save_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ...
|
||||
def response_action(self, request: HttpRequest, queryset: QuerySet) -> Optional[HttpResponseBase]: ...
|
||||
def response_delete(self, request: HttpRequest, obj_display: str, obj_id: int) -> HttpResponse: ...
|
||||
def render_delete_form(self, request: Any, context: Any): ...
|
||||
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]: ...
|
||||
def get_changeform_initial_data(self, request: HttpRequest) -> Dict[str, str]: ...
|
||||
def changeform_view(
|
||||
@@ -246,8 +265,8 @@ class ModelAdmin(BaseModelAdmin):
|
||||
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: ...
|
||||
|
||||
class InlineModelAdmin(BaseModelAdmin):
|
||||
model: Type[Model] = ...
|
||||
class InlineModelAdmin(BaseModelAdmin[_ModelT]):
|
||||
model: Type[_ModelT] = ...
|
||||
fk_name: str = ...
|
||||
formset: BaseFormSet = ...
|
||||
extra: int = ...
|
||||
@@ -263,13 +282,13 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||
parent_model: Any = ...
|
||||
opts: 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
|
||||
def media(self) -> Media: ...
|
||||
def get_extra(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> int: ...
|
||||
def get_min_num(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> Optional[int]: ...
|
||||
def get_max_num(self, request: HttpRequest, obj: Optional[Model] = ..., **kwargs: Any) -> Optional[int]: ...
|
||||
def get_formset(self, request: Any, obj: Optional[Any] = ..., **kwargs: Any): ...
|
||||
def get_extra(self, request: HttpRequest, obj: Optional[_ModelT] = ..., **kwargs: Any) -> int: ...
|
||||
def get_min_num(self, request: HttpRequest, obj: Optional[_ModelT] = ..., **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[_ModelT] = ..., **kwargs: Any): ...
|
||||
|
||||
class StackedInline(InlineModelAdmin): ...
|
||||
class TabularInline(InlineModelAdmin): ...
|
||||
class StackedInline(InlineModelAdmin[_ModelT]): ...
|
||||
class TabularInline(InlineModelAdmin[_ModelT]): ...
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
def an_action(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> None:
|
||||
pass
|
||||
|
||||
class A(admin.ModelAdmin):
|
||||
class TestModel(models.Model):
|
||||
pass
|
||||
|
||||
class A(admin.ModelAdmin[TestModel]):
|
||||
# BaseModelAdmin
|
||||
autocomplete_fields = ("strs",)
|
||||
raw_id_fields = ["strs"]
|
||||
@@ -71,6 +74,11 @@
|
||||
actions_selection_counter = True
|
||||
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):
|
||||
pass
|
||||
|
||||
@@ -127,4 +135,4 @@
|
||||
pass
|
||||
|
||||
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