¿Modelos de solo lectura en la interfaz de administración de Django?
django-admin readonly (12)
permiso de solo lectura => vistas
-
pipenv install django-admin-view-permission
- agregue ''admin_view_permission'' a INSTALLED_APPS en la configuración.py.me gusta esto: `INSTALLED_APPS = [''admin_view_permission'',
- Python manage.py migrate
- python manage.py runserver
6666
ok. diviértete con el permiso ''vistas''
¿Cómo puedo hacer que un modelo sea de solo lectura en la interfaz de administración? Es para un tipo de tabla de registro, donde estoy usando las funciones de administración para buscar, ordenar, filtrar, etc., pero no es necesario modificar el registro.
En caso de que esto parezca un duplicado, esto no es lo que estoy tratando de hacer:
- No estoy buscando campos de solo lectura (incluso hacer que cada campo de solo lectura le permita crear nuevos registros)
- No busco crear un usuario de solo lectura: cada usuario debe ser de solo lectura.
¡Esto se agregó a Django 2.1 que se lanzó el 8/1/18!
ModelAdmin.has_view_permission()
es como el has_delete_permission, has_change_permission y has_add_permission existentes. Puedes leerlo en los documentos here
De las notas de publicación:
Esto permite dar a los usuarios acceso de solo lectura a los modelos en el administrador. ModelAdmin.has_view_permission () es nuevo. La implementación es compatible con versiones anteriores, ya que no es necesario asignar el permiso "ver" para permitir que los usuarios que tienen el permiso "cambiar" puedan editar objetos.
Aquí hay dos clases que estoy usando para hacer un modelo y / o sus inlines son solo de lectura.
Para el modelo de administrador:
from django.contrib import admin
class ReadOnlyAdmin(admin.ModelAdmin):
readonly_fields = []
def get_readonly_fields(self, request, obj=None):
return list(self.readonly_fields) + /
[field.name for field in obj._meta.fields] + /
[field.name for field in obj._meta.many_to_many]
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
class MyModelAdmin(ReadOnlyAdmin):
pass
Para inlines:
class ReadOnlyTabularInline(admin.TabularInline):
extra = 0
can_delete = False
editable_fields = []
readonly_fields = []
exclude = []
def get_readonly_fields(self, request, obj=None):
return list(self.readonly_fields) + /
[field.name for field in self.model._meta.fields
if field.name not in self.editable_fields and
field.name not in self.exclude]
def has_add_permission(self, request):
return False
class MyInline(ReadOnlyTabularInline):
pass
El administrador es para editar, no solo para ver (no encontrará un permiso para "ver"). Para lograr lo que desea, tendrá que prohibir agregar, eliminar y hacer que todos los campos sean de solo lectura:
class MyAdmin(ModelAdmin):
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
(Si prohibes cambiar, ni siquiera podrás ver los objetos)
Para algunos códigos no probados que intentan automatizar la configuración de todos los campos de solo lectura, vea mi respuesta a Todo el modelo como de solo lectura
EDITAR: también no probado, pero solo eché un vistazo a mi LogEntryAdmin y tiene
readonly_fields = MyModel._meta.get_all_field_names()
No sé si eso funcionará en todos los casos.
EDITAR: QuerySet.delete () todavía puede eliminar objetos de forma masiva. Para solucionar esto, proporcione su propio administrador de "objetos" y la correspondiente subclase QuerySet que no se borra. Consulte Anulación de QuerySet.delete () en Django.
En realidad puedes probar esta solución simple:
class ReadOnlyModelAdmin(admin.ModelAdmin):
actions = None
list_display_links = None
# more stuff here
def has_add_permission(self, request):
return False
-
actions = None
: evita mostrar el menú desplegable con la opción "Delete selected ..." -
list_display_links = None
: evita hacer clic en las columnas para editar ese objeto -
has_add_permission()
devolviendo False evita crear nuevos objetos para ese modelo
He escrito una clase genérica para manejar la vista ReadOnly dependiendo de los permisos del usuario, incluidos los inlines;)
En models.py:
class User(AbstractUser):
...
def is_readonly(self):
if self.is_superuser:
return False
# make readonly all users not in "admins" group
adminGroup = Group.objects.filter(name="admins")
if adminGroup in self.groups.all():
return False
return True
En admin.py:
# read-only user filter class for ModelAdmin
class ReadOnlyAdmin(admin.ModelAdmin):
def __init__(self, *args, **kwargs):
# keep initial readonly_fields defined in subclass
self._init_readonly_fields = self.readonly_fields
# keep also inline readonly_fields
for inline in self.inlines:
inline._init_readonly_fields = inline.readonly_fields
super().__init__(*args,**kwargs)
# customize change_view to disable edition to readonly_users
def change_view( self, request, object_id, form_url='''', extra_context=None ):
context = extra_context or {}
# find whether it is readonly or not
if request.user.is_readonly():
# put all fields in readonly_field list
self.readonly_fields = [ field.name for field in self.model._meta.get_fields() if not field.auto_created ]
# readonly mode fer all inlines
for inline in self.inlines:
inline.readonly_fields = [field.name for field in inline.model._meta.get_fields() if not field.auto_created]
# remove edition buttons
self.save_on_top = False
context[''show_save''] = False
context[''show_save_and_continue''] = False
else:
# if not readonly user, reset initial readonly_fields
self.readonly_fields = self._init_readonly_fields
# same for inlines
for inline in self.inlines:
inline.readonly_fields = self._init_readonly_fields
return super().change_view(
request, object_id, form_url, context )
def save_model(self, request, obj, form, change):
# disable saving model for readonly users
# just in case we have a malicious user...
if request.user.is_readonly():
# si és usuari readonly no guardem canvis
return False
# if not readonly user, save model
return super().save_model( request, obj, form, change )
Entonces, podemos heredar normalmente nuestras clases en admin.py:
class ContactAdmin(ReadOnlyAdmin):
list_display = ("name","email","whatever")
readonly_fields = ("updated","created")
inlines = ( PhoneInline, ... )
La compilación de las excelentes respuestas de @darklow y @josir, además de agregar un poco más para eliminar los botones "Guardar" y "Guardar y continuar" lleva a (en la sintaxis de Python 3):
class ReadOnlyAdmin(admin.ModelAdmin):
"""Provides a read-only view of a model in Django admin."""
readonly_fields = []
def change_view(self, request, object_id, extra_context=None):
""" customize add/edit form to remove save / save and continue """
extra_context = extra_context or {}
extra_context[''show_save_and_continue''] = False
extra_context[''show_save''] = False
return super().change_view(request, object_id, extra_context=extra_context)
def get_actions(self, request):
actions = super().get_actions(request)
if ''delete_selected'' in actions:
del actions[''delete_selected'']
return actions
def get_readonly_fields(self, request, obj=None):
return list(self.readonly_fields) + /
[field.name for field in obj._meta.fields] + /
[field.name for field in obj._meta.many_to_many]
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
y luego usas como
class MyModelAdmin(ReadOnlyAdmin):
pass
Solo he probado esto con Django 1.11 / Python 3.
La respuesta aceptada debería funcionar, pero esto también preservará el orden de visualización de los campos de solo lectura. Tampoco tiene que codificar el modelo con esta solución.
class ReadonlyAdmin(admin.ModelAdmin):
def __init__(self, model, admin_site):
super(ReadonlyAdmin, self).__init__(model, admin_site)
self.readonly_fields = [field.name for field in filter(lambda f: not f.auto_created, model._meta.fields)]
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request, obj=None):
return False
Me encontré con el mismo requisito cuando tuve que hacer que todos los campos fueran de solo lectura para ciertos usuarios en el administrador de django, terminé aprovechando el módulo de django "django-admin-view-permission" sin pasar mi propio código. Si necesita un control más preciso para definir explícitamente qué campos, entonces necesitará extender el módulo. Puedes ver el plugin en acción here
Si desea que el usuario se dé cuenta de que no puede editarlo, faltan 2 piezas en la primera solución. ¡Has eliminado la acción de borrar!
class MyAdmin(ModelAdmin)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def get_actions(self, request):
actions = super(MyAdmin, self).get_actions(request)
if ''delete_selected'' in actions:
del actions[''delete_selected'']
return actions
Segundo: la solución de solo lectura funciona bien en modelos planos. Pero NO funciona si tiene un modelo heredado con claves externas. Desafortunadamente, no sé la solución para eso todavía. Un buen intento es:
Modelo completo como solo lectura
Pero tampoco funciona para mí.
Y una nota final, si desea pensar en una solución amplia, debe hacer cumplir que cada línea debe ser de solo lectura también.
Si la respuesta aceptada no funciona para usted, intente esto:
def get_readonly_fields(self, request, obj=None):
readonly_fields = []
for field in self.model._meta.fields:
readonly_fields.append(field.name)
return readonly_fields
Ver https://djangosnippets.org/snippets/10539/
class ReadOnlyAdminMixin(object):
"""Disables all editing capabilities."""
change_form_template = "admin/view.html"
def __init__(self, *args, **kwargs):
super(ReadOnlyAdminMixin, self).__init__(*args, **kwargs)
self.readonly_fields = self.model._meta.get_all_field_names()
def get_actions(self, request):
actions = super(ReadOnlyAdminMixin, self).get_actions(request)
del actions["delete_selected"]
return actions
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
def save_model(self, request, obj, form, change):
pass
def delete_model(self, request, obj):
pass
def save_related(self, request, form, formsets, change):
pass
templates / admin / view.html
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block submit_buttons_bottom %}
<div class="submit-row">
<a href="../">{% blocktrans %}Back to list{% endblocktrans %}</a>
</div>
{% endblock %}
templates / admin / view.html (para Grappelli)
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block submit_buttons_bottom %}
<footer class="grp-module grp-submit-row grp-fixed-footer">
<header style="display:none"><h1>{% trans "submit options"|capfirst context "heading" %}</h1></header>
<ul>
<li><a href="../" class="grp-button grp-default">{% blocktrans %}Back to list{% endblocktrans %}</a></li>
</ul>
</footer>
{% endblock %}