Comment créer un modèle en lecture seule dans l'interface d'administration? C'est pour une sorte de table de journal, où j'utilise les fonctionnalités d'administration pour rechercher, trier, filtrer, etc., mais il n'est pas nécessaire de modifier le journal.
Si cela ressemble à un doublon, voici non ce que j'essaie de faire:
Voir 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 (pour 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 %}
L'administrateur est destiné à l'édition, pas seulement à l'affichage (vous ne trouverez pas d'autorisation de "visualisation"). Pour obtenir ce que vous voulez, vous devez interdire l’ajout, la suppression et rendre tous les champs en lecture seule:
class MyAdmin(ModelAdmin):
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
(si vous interdisez de changer, vous ne verrez même pas les objets)
Pour du code non testé qui tente d'automatiser la définition de tous les champs en lecture seule, voyez ma réponse à Modèle entier en lecture seule
EDIT: également non testé mais j’ai jeté un œil à mon LogEntryAdmin et il a
readonly_fields = MyModel._meta.get_all_field_names()
Je ne sais pas si cela fonctionnera dans tous les cas.
EDIT: QuerySet.delete () peut encore supprimer des objets en bloc. Pour résoudre ce problème, fournissez votre propre gestionnaire "d'objets" et la sous-classe QuerySet correspondante qui ne supprime pas - voir Remplacement de QuerySet.delete () dans Django
Voici deux classes que j'utilise pour créer un modèle et/ou une lecture en ligne uniquement.
Pour l'administrateur du modèle:
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
Pour les 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
Si vous souhaitez que l'utilisateur sache qu'il ne peut pas l'éditer, il manque 2 éléments dans la première solution. Vous avez supprimer l'action de suppression!
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
Deuxièmement: la solution readonly fonctionne bien sur les modèles standard. Mais cela fonctionne PAS si vous avez un modèle hérité avec des clés étrangères. Malheureusement, je ne connais pas encore la solution. Une bonne tentative est:
Modèle entier en lecture seule
Mais cela ne fonctionne pas pour moi non plus.
Enfin, si vous souhaitez réfléchir à une solution globale, vous devez vous assurer que chaque inline doit également être en lecture seule.
En fait, vous pouvez essayer cette solution simple:
class ReadOnlyModelAdmin(admin.ModelAdmin):
actions = None
list_display_links = None
# more stuff here
def has_add_permission(self, request):
return False
actions = None
: évite d'afficher le menu déroulant avec l'option "Supprimer la sélection ..."list_display_links = None
: évite de cliquer dans les colonnes pour éditer cet objethas_add_permission()
return False évite de créer de nouveaux objets pour ce modèleSi la réponse acceptée ne fonctionne pas pour vous, essayez ceci:
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
Cela a été ajouté à Django 2.1 qui est sorti le 01/08/18!
ModelAdmin.has_view_permission()
est identique à has_delete_permission, has_change_permission et has_add_permission existants. Vous pouvez lire à ce sujet dans la documentation ici
À partir des notes de publication:
Cela permet aux utilisateurs d’avoir un accès en lecture seule aux modèles de l’administrateur . ModelAdmin.has_view_permission () est nouveau. L'implémentation est rétrocompatible en ce sens qu’il n’est pas nécessaire d’attribuer la «vue» autorisation permettant aux utilisateurs disposant de l'autorisation «modifier» de modifier objets.
Compiler les excellentes réponses de @darklow et de @josir et ajouter un peu plus pour supprimer les boutons "Enregistrer" et "Enregistrer et continuer" donne lieu (en syntaxe 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
et puis vous utilisez comme
class MyModelAdmin(ReadOnlyAdmin):
pass
Je n'ai essayé cela qu'avec Django 1.11/Python 3.
La réponse acceptée devrait fonctionner, mais cela préservera également l'ordre d'affichage des champs en lecture seule. Vous n'avez pas non plus à coder en dur le modèle avec cette solution.
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
J'ai rencontré le même besoin lorsque je devais rendre tous les champs en lecture seule pour certains utilisateurs de l'administrateur Django et que je devais tirer parti du module Django "Django-admin-view-permission" sans lancer mon propre code. Si vous avez besoin d'un contrôle plus fin pour définir explicitement les champs, vous devrez étendre le module. Vous pouvez voir le plugin en action ici
pipenv install Django-admin-view-permission
6666
J'ai écrit une classe générique pour gérer la vue en lecture seule en fonction des autorisations de l'utilisateur, y compris des inlines;)
Dans 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
Dans 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 )
Ensuite, nous pouvons simplement hériter normalement de nos classes dans admin.py:
class ContactAdmin(ReadOnlyAdmin):
list_display = ("name","email","whatever")
readonly_fields = ("updated","created")
inlines = ( PhoneInline, ... )