GenericView¶
Generated view class for ViewAction.
GenericView
¶
djadmin.factories.base.GenericView
ViewAction View class for site.None generated by ViewFactory.
Base class: django.views.generic.detail.DetailView Mixins: PermissionMixin, DjAdminViewMixin Active plugins: djadmin.plugins.contrib_auth, examples.webshop, djadmin_formset, djadmin_classy_doc, djadmin_filters, djadmin.plugins.permissions, djadmin.plugins.core, djadmin.plugins.theme
View Information ¶
- Action
ViewAction- ModelAdmin
djadmin.options.ModelAdmin
View Construction ¶
Base Class
django.views.generic.detail.DetailView
Mixins
-
djadmin.plugins.permissions.mixins.PermissionMixindjadmin.plugins.permissions.djadmin_hooks -
djadmin.plugins.core.mixins.DjAdminViewMixindjadmin.plugins.core.djadmin_hooks
Bound Methods from Action
-
get_template_names()from ViewAction -
get_layout()from ViewAction -
get_context_data()from ViewAction
Method Resolution Order ¶
-
djadmin.factories.base.GenericView -
djadmin.plugins.permissions.mixins.PermissionMixindjadmin.plugins.permissions.djadmin_hooks -
django.contrib.auth.mixins.UserPassesTestMixin -
django.contrib.auth.mixins.AccessMixin -
djadmin.plugins.core.mixins.DjAdminViewMixindjadmin.plugins.core.djadmin_hooks -
django.views.generic.detail.DetailViewbase class from ViewAction -
django.views.generic.detail.SingleObjectTemplateResponseMixin -
django.views.generic.base.TemplateResponseMixin -
django.views.generic.detail.BaseDetailView -
django.views.generic.detail.SingleObjectMixin -
django.views.generic.base.ContextMixin -
django.views.generic.base.View
Attributes ¶
| Attribute | Value | Defined in |
|---|---|---|
action |
ViewAction() |
djadmin.factories.base.GenericView
|
admin_site |
AdminSite(name='docs_temp') |
djadmin.factories.base.GenericView
|
content_type |
None |
django.views.generic.base.TemplateResponseMixin
|
context_object_name |
None |
django.views.generic.detail.SingleObjectMixin
|
extra_context |
None |
django.views.generic.base.ContextMixin
|
http_method_names |
['get', 'post', 'put', 'patch', 'delete', 'head', 'option... |
django.views.generic.base.View
|
login_url |
None |
django.contrib.auth.mixins.AccessMixin
|
model |
None |
django.views.generic.detail.SingleObjectMixin
|
model_admin |
ModelAdmin() |
djadmin.factories.base.GenericView
|
permission_denied_message |
|
django.contrib.auth.mixins.AccessMixin
|
pk_url_kwarg |
pk |
django.views.generic.detail.SingleObjectMixin
|
query_pk_and_slug |
False |
django.views.generic.detail.SingleObjectMixin
|
queryset |
None |
django.views.generic.detail.SingleObjectMixin
|
raise_exception |
False |
django.contrib.auth.mixins.AccessMixin
|
redirect_field_name |
next |
django.contrib.auth.mixins.AccessMixin
|
response_class |
django.template.response.TemplateResponse |
django.views.generic.base.TemplateResponseMixin
|
slug_field |
slug |
django.views.generic.detail.SingleObjectMixin
|
slug_url_kwarg |
slug |
django.views.generic.detail.SingleObjectMixin
|
template_engine |
None |
django.views.generic.base.TemplateResponseMixin
|
template_name |
None |
django.views.generic.base.TemplateResponseMixin
|
template_name_field |
None |
django.views.generic.detail.SingleObjectTemplateResponseMixin
|
template_name_suffix |
_detail |
django.views.generic.detail.SingleObjectTemplateResponseMixin
|
view_is_async |
False |
django.views.generic.base.View
|
Methods ¶
__init__(self, **kwargs)
Defined in:
<class 'django.views.generic.base.View'>
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
Source code
in base.py
line 54
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
for key, value in kwargs.items():
setattr(self, key, value)
as_view(**initkwargs)
@classmethod
Defined in:
<class 'django.views.generic.base.View'>
Main entry point for a request-response process.
Source code
in base.py
line 81
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
"The method name %s is not accepted as a keyword argument "
"to %s()." % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError(
"%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key)
)
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# __name__ and __qualname__ are intentionally left unchanged as
# view_class should be used to robustly determine the name of the view
# instead.
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.__annotations__ = cls.dispatch.__annotations__
# Copy possible attributes set by decorators, e.g. @csrf_exempt, from
# the dispatch method.
view.__dict__.update(cls.dispatch.__dict__)
# Mark the callback if the view class is async.
if cls.view_is_async:
markcoroutinefunction(view)
return view
build_form(self, form=None)
Defined in:
<class 'djadmin.plugins.core.mixins.DjAdminViewMixin'>
Build form class from layout or fields. Override in plugins to customize.
This method is called by get_form_class() when no explicit form_class is provided. The default implementation uses FormBuilder to create forms from layouts or fields.
Returns: ModelForm class ready for instantiation
Source code
in mixins.py
line 246
def build_form(self, form=None):
"""
Build form class from layout or fields. Override in plugins to customize.
This method is called by get_form_class() when no explicit form_class is provided.
The default implementation uses FormBuilder to create forms from layouts or fields.
Returns:
ModelForm class ready for instantiation
"""
layout = self.get_layout()
if layout:
# Build from layout using FormBuilder
print('using FormBuilder.from_layout')
return FormBuilder.from_layout(layout, self.model, form_class=None)
print('using FormBuilder.create_form')
# No layout - build from fields
fields = getattr(self, 'get_fields', lambda: '__all__')()
return FormBuilder.create_form(self.model, fields=fields)
dispatch(self, request, *args, **kwargs)
Defined in:
<class 'django.views.generic.base.View'>
Source code
in base.py
line 134
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(
self, request.method.lower(), self.http_method_not_allowed
)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
Source code
in mixins.py
line 131
def dispatch(self, request, *args, **kwargs):
user_test_result = self.get_test_func()()
if not user_test_result:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
filtered_actions
Defined in:
<class 'djadmin.plugins.core.mixins.DjAdminViewMixin'>
Filtered action lists by user permissions, cached on view instance.
For object-based views (UpdateView, DetailView, DeleteView), filters actions based on the specific object. For list views, filters at model level.
Results are cached on the view instance, which is per-request since Django creates a new view instance for each request. Combined with request-level caching in ModelAdmin.filter_actions(), this ensures actions are filtered only once per request.
Performance Impact: Without caching: Actions filtered multiple times per request With caching: Actions filtered once per request (view instance)
Returns: dict: Dictionary with filtered action lists: - filtered_general_actions - filtered_bulk_actions - filtered_record_actions
get(self, request, *args, **kwargs)
Defined in:
<class 'django.views.generic.detail.BaseDetailView'>
Source code
in detail.py
line 111
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
get_context_data(self, **kwargs)
Defined in:
<class 'django.views.generic.base.ContextMixin'>
Source code
in actions.py
line 332
def get_context_data(self, **kwargs):
"""
Add layout_items to context for template rendering.
Returns:
Context dict with layout_items
"""
context = super().get_context_data(**kwargs)
# Get layout
layout = self.get_layout()
if layout:
# Render layout for display
obj = self.get_object()
layout_items = layout.render_for_display(obj)
context['layout_items'] = layout_items
return context
Source code
in detail.py
line 92
def get_context_data(self, **kwargs):
"""Insert the single object into the context dict."""
context = {}
if self.object:
context["object"] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return super().get_context_data(**context)
Source code
in mixins.py
line 126
def get_context_data(self, **kwargs):
"""
Add standard admin context to all views.
When bound to the view, self is the view instance which has:
- self.action: Action instance
- self.model: Model class
- self.model_admin: ModelAdmin instance
- self.admin_site: AdminSite instance
- self.request: Current request
"""
from djadmin.plugins import pm
# Call super to get base context from Django CBV
context = super().get_context_data(**kwargs)
# Add request to context (needed for inclusion tags)
context['request'] = self.request
# Add standard admin context
context['model'] = self.model
context['opts'] = self.model._meta if self.model else None
context['action'] = self.action
context['model_admin'] = self.model_admin
context['admin_site'] = self.admin_site
# Add version information
context['djadmin_version'] = djadmin.__version__
context['django_version'] = django.get_version()
# Add site-wide URLs
try:
context['index_url'] = reverse(f'{self.admin_site.name}:index')
except Exception:
context['index_url'] = '/'
# Add site configuration
context['site_header'] = self.admin_site.site_header
context['site_title'] = self.admin_site.site_title
context['index_title'] = self.admin_site.index_title
# Add list URL (using admin_site namespace)
# Skip for site-level actions (model=None)
if self.model is not None:
list_url_name = f'{self.model_admin.list_url_name}'
context['list_url'] = self.admin_site.reverse(list_url_name)
else:
context['list_url'] = context.get('index_url', '/')
# Add namespaced URL names for actions
namespace = self.admin_site.name
context['action_namespace'] = namespace
# Add breadcrumbs
context['breadcrumb_list'] = self._get_breadcrumbs()
# Add assets from plugins
assets = self._get_assets_from_plugins()
context['assets'] = assets
# Add sidebar widgets for template rendering
sidebar_widgets = self._get_sidebar_widgets_for_template()
context['sidebar_widgets'] = sidebar_widgets
# Add column header icons for list views
column_header_icons = self._get_column_header_icons()
context['column_header_icons'] = column_header_icons
# Add filtered actions to context (permission-checked)
# This ensures all views (ListView, UpdateView, CreateView, etc.) get filtered actions
if self.model is not None and self.model_admin is not None:
# Add unfiltered action lists (for template tag use in ListView)
context['general_actions'] = self.model_admin.general_actions
context['bulk_actions'] = self.model_admin.bulk_actions
context['record_actions'] = self.model_admin.record_actions
# Add filtered action lists (for use in templates)
context.update(self.filtered_actions)
# Allow plugins to add more context
plugin_contexts = pm.hook.djadmin_add_context_data(
context=context,
request=self.request,
view=self,
)
for plugin_context in plugin_contexts:
if plugin_context:
context.update(plugin_context)
if settings.DEBUG:
# List loaded plugins for debugging
# djp.PluginManager wraps pluggy, access the underlying manager
try:
# Get plugins from the underlying pluggy manager
if hasattr(pm, '_pm'):
# djp stores the pluggy manager as _pm
plugins = pm._pm.get_plugins()
elif hasattr(pm, 'get_plugins'):
plugins = pm.get_plugins()
else:
# Fallback: just note that we couldn't list plugins
plugins = ['(plugin listing not available)']
context['debug_loaded_plugins'] = [str(plugin) for plugin in plugins]
except Exception as e:
context['debug_loaded_plugins'] = [f'Error listing plugins: {e}']
# Debug: Show view's MRO (Method Resolution Order) to see which mixins are applied
context['debug_view_mro'] = [cls.__name__ for cls in self.__class__.__mro__[:10]]
# Debug: Show form class being used
if hasattr(self, 'form_class'):
context['debug_form_class'] = str(self.form_class)
# Debug: Show action class hierarchy
context['debug_action_mro'] = [cls.__name__ for cls in self.action.__class__.__mro__[:10]]
return context
Source code
in view_mixins.py
line 332
def get_context_data(self, **kwargs):
"""
Add layout_items to context for template rendering.
Returns:
Context dict with layout_items
"""
context = super().get_context_data(**kwargs)
# Get layout
layout = self.get_layout()
if layout:
# Render layout for display
obj = self.get_object()
layout_items = layout.render_for_display(obj)
context['layout_items'] = layout_items
return context
get_context_object_name(self, obj)
Defined in:
<class 'django.views.generic.detail.SingleObjectMixin'>
Get the name to use for the object.
Source code
in detail.py
line 83
def get_context_object_name(self, obj):
"""Get the name to use for the object."""
if self.context_object_name:
return self.context_object_name
elif isinstance(obj, models.Model):
return obj._meta.model_name
else:
return None
get_layout(self)
bound from ViewAction
Defined in:
<class 'djadmin.factories.base.GenericView'>
Get layout for display.
Fallback order: 1. view_layout (action-specific) 2. update_layout (shares with edit view) 3. layout (generic) 4. Auto-generate from model fields
Returns: Layout instance or None
Source code
in actions.py
line 281
def get_layout(self):
"""
Get layout for display.
Fallback order:
1. view_layout (action-specific)
2. update_layout (shares with edit view)
3. layout (generic)
4. Auto-generate from model fields
Returns:
Layout instance or None
"""
# Try view_layout first (most specific)
layout = getattr(self.model_admin, 'view_layout', None)
if layout:
return layout
# Fallback to update_layout (share with edit view)
layout = getattr(self.model_admin, 'update_layout', None)
if layout:
return layout
# Fallback to generic layout
layout = getattr(self.model_admin, 'layout', None)
if layout:
return layout
# No layout defined - auto-generate from model fields
return None
get_login_url(self)
Defined in:
<class 'django.contrib.auth.mixins.AccessMixin'>
Override this method to override the login_url attribute.
Source code
in mixins.py
line 21
def get_login_url(self):
"""
Override this method to override the login_url attribute.
"""
login_url = self.login_url or settings.LOGIN_URL
if not login_url:
raise ImproperlyConfigured(
f"{self.__class__.__name__} is missing the login_url attribute. Define "
f"{self.__class__.__name__}.login_url, settings.LOGIN_URL, or override "
f"{self.__class__.__name__}.get_login_url()."
)
return str(login_url)
get_object(self, queryset=None)
Defined in:
<class 'django.views.generic.detail.SingleObjectMixin'>
Return the object the view is displaying.
Require self.queryset and a pk or slug argument in the URLconf.
Subclasses can override this to return any object.
Source code
in detail.py
line 21
def get_object(self, queryset=None):
"""
Return the object the view is displaying.
Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
Subclasses can override this to return any object.
"""
# Use a custom queryset if provided; this is required for subclasses
# like DateDetailView
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
# Next, try looking up by slug.
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{slug_field: slug})
# If none of those are defined, it's an error.
if pk is None and slug is None:
raise AttributeError(
"Generic detail view %s must be called with either an object "
"pk or a slug in the URLconf." % self.__class__.__name__
)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(
_("No %(verbose_name)s found matching the query")
% {"verbose_name": queryset.model._meta.verbose_name}
)
return obj
Source code
in mixins.py
line 39
def get_object(self, queryset=None):
"""Override to check object permissions after fetching."""
obj = super().get_object(queryset)
# Get permission class from action or model_admin
permission_class = self._get_permission_class()
if permission_class is not None:
# Check object permission
if not permission_class.has_object_permission(self, obj):
raise PermissionDenied(f'You do not have permission to access this {obj._meta.verbose_name}.')
return obj
get_permission_denied_message(self)
Defined in:
<class 'django.contrib.auth.mixins.AccessMixin'>
Override this method to override the permission_denied_message attribute.
Source code
in mixins.py
line 34
def get_permission_denied_message(self):
"""
Override this method to override the permission_denied_message attribute.
"""
return self.permission_denied_message
get_queryset(self)
Defined in:
<class 'django.views.generic.detail.SingleObjectMixin'>
Return the QuerySet that will be used to look up the object.
This method is called by the default implementation of get_object() and may not be called if get_object() is overridden.
Source code
in detail.py
line 61
def get_queryset(self):
"""
Return the `QuerySet` that will be used to look up the object.
This method is called by the default implementation of get_object() and
may not be called if get_object() is overridden.
"""
if self.queryset is None:
if self.model:
return self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {"cls": self.__class__.__name__}
)
return self.queryset.all()
get_redirect_field_name(self)
Defined in:
<class 'django.contrib.auth.mixins.AccessMixin'>
Override this method to override the redirect_field_name attribute.
Source code
in mixins.py
line 40
def get_redirect_field_name(self):
"""
Override this method to override the redirect_field_name attribute.
"""
return self.redirect_field_name
get_slug_field(self)
Defined in:
<class 'django.views.generic.detail.SingleObjectMixin'>
Get the name of a slug field to be used to look up by slug.
Source code
in detail.py
line 79
def get_slug_field(self):
"""Get the name of a slug field to be used to look up by slug."""
return self.slug_field
get_template_names(self)
Defined in:
<class 'django.views.generic.base.TemplateResponseMixin'>
Return a list of template names to be used for the request. Must return a list. May not be called if render_to_response() is overridden.
Source code
in actions.py
line 175
def get_template_names(self):
"""
Get template names as a list (Django CBV expects plural method).
This wraps get_template_name() and ensures the result is a list.
Returns:
List of template path strings
"""
template_names = []
if model_name := getattr(getattr(self.model, '_meta', None), 'model_name', None):
template_names.append(f'djadmin/{self.model._meta.app_label}/{model_name}_{self.action.action_name}.html')
template_names.append(f'djadmin/actions/{self.action.action_name}.html')
return template_names
Source code
in detail.py
line 121
def get_template_names(self):
"""
Return a list of template names to be used for the request. May not be
called if render_to_response() is overridden. Return a list containing
``template_name``, if set on the value. Otherwise, return a list
containing:
* the contents of the ``template_name_field`` field on the
object instance that the view is operating upon (if available)
* ``<app_label>/<model_name><template_name_suffix>.html``
"""
try:
names = super().get_template_names()
except ImproperlyConfigured:
# If template_name isn't specified, it's not a problem --
# we just start with an empty list.
names = []
# If self.template_name_field is set, grab the value of the field
# of that name from the object; this is the most specific template
# name, if given.
if self.object and self.template_name_field:
name = getattr(self.object, self.template_name_field, None)
if name:
names.insert(0, name)
# The least-specific option is the default <app>/<model>_detail.html;
# only use this if the object in question is a model.
if isinstance(self.object, models.Model):
object_meta = self.object._meta
names.append(
"%s/%s%s.html"
% (
object_meta.app_label,
object_meta.model_name,
self.template_name_suffix,
)
)
elif getattr(self, "model", None) is not None and issubclass(
self.model, models.Model
):
names.append(
"%s/%s%s.html"
% (
self.model._meta.app_label,
self.model._meta.model_name,
self.template_name_suffix,
)
)
# If we still haven't managed to find any template names, we should
# re-raise the ImproperlyConfigured to alert the user.
if not names:
raise ImproperlyConfigured(
"SingleObjectTemplateResponseMixin requires a definition "
"of 'template_name', 'template_name_field', or 'model'; "
"or an implementation of 'get_template_names()'."
)
return names
Source code
in base.py
line 175
def get_template_names(self):
"""
Get template names as a list (Django CBV expects plural method).
This wraps get_template_name() and ensures the result is a list.
Returns:
List of template path strings
"""
template_names = []
if model_name := getattr(getattr(self.model, '_meta', None), 'model_name', None):
template_names.append(f'djadmin/{self.model._meta.app_label}/{model_name}_{self.action.action_name}.html')
template_names.append(f'djadmin/actions/{self.action.action_name}.html')
return template_names
get_test_func(self)
Defined in:
<class 'django.contrib.auth.mixins.UserPassesTestMixin'>
Override this method to use a different test_func method.
Source code
in mixins.py
line 125
def get_test_func(self):
"""
Override this method to use a different test_func method.
"""
return self.test_func
handle_no_permission(self)
Defined in:
<class 'django.contrib.auth.mixins.AccessMixin'>
Source code
in mixins.py
line 46
def handle_no_permission(self):
if self.raise_exception or self.request.user.is_authenticated:
raise PermissionDenied(self.get_permission_denied_message())
path = self.request.build_absolute_uri()
resolved_login_url = resolve_url(self.get_login_url())
# If the login url is the same scheme and net location then use the
# path as the "next" url.
login_scheme, login_netloc = urlsplit(resolved_login_url)[:2]
current_scheme, current_netloc = urlsplit(path)[:2]
if (not login_scheme or login_scheme == current_scheme) and (
not login_netloc or login_netloc == current_netloc
):
path = self.request.get_full_path()
return redirect_to_login(
path,
resolved_login_url,
self.get_redirect_field_name(),
)
http_method_not_allowed(self, request, *args, **kwargs)
Defined in:
<class 'django.views.generic.base.View'>
Source code
in base.py
line 146
def http_method_not_allowed(self, request, *args, **kwargs):
response = HttpResponseNotAllowed(self._allowed_methods())
log_response(
"Method Not Allowed (%s): %s",
request.method,
request.path,
response=response,
request=request,
)
if self.view_is_async:
async def func():
return response
return func()
else:
return response
options(self, request, *args, **kwargs)
Defined in:
<class 'django.views.generic.base.View'>
Handle responding to requests for the OPTIONS HTTP verb.
Source code
in base.py
line 165
def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb."""
response = HttpResponse()
response.headers["Allow"] = ", ".join(self._allowed_methods())
response.headers["Content-Length"] = "0"
if self.view_is_async:
async def func():
return response
return func()
else:
return response
render_to_response(self, context, **response_kwargs)
Defined in:
<class 'django.views.generic.base.TemplateResponseMixin'>
Return a response, using the response_class for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
Source code
in base.py
line 192
def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault("content_type", self.content_type)
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs,
)
setup(self, request, *args, **kwargs)
Defined in:
<class 'django.views.generic.base.View'>
Initialize attributes shared by all view methods.
Source code
in base.py
line 126
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
if hasattr(self, "get") and not hasattr(self, "head"):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
test_func(self)
Defined in:
<class 'django.contrib.auth.mixins.UserPassesTestMixin'>
Source code
in mixins.py
line 118
def test_func(self):
raise NotImplementedError(
"{} is missing the implementation of the test_func() method.".format(
self.__class__.__name__
)
)
Source code
in mixins.py
line 16
def test_func(self):
"""
Permission check method for UserPassesTestMixin.
This method is called by UserPassesTestMixin to check if the user
has permission to access the view.
Returns:
bool: True if user has permission, False otherwise
"""
# Get permission from action or model_admin
perm = self._get_permission_class()
if perm is None:
# None means no permission enforcement (allow all access)
# This happens when:
# 1. Explicitly set to None on ModelAdmin or Action
# 2. No model_admin exists (site-level views like DashboardAction)
return True
# Execute permission with view (self is the view)
return perm(self)
Fields ¶
| Field | Type | Related To |
|---|---|---|
__dict__ |
getset_descriptor |
- |
__weakref__ |
getset_descriptor |
- |