web-dev-qa-db-fra.com

Modèles en lecture seule dans l'interface d'administration de Django?

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:

  • Je ne cherche pas en lecture seule champs (même si chaque champ en lecture seule vous permettait de créer de nouveaux enregistrements)
  • Je ne cherche pas à créer un utilisateur _ en lecture seule: chaque utilisateur devrait être en lecture seule.
68
Steve Bennett

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 %}
13
Pascal Polleunus

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

65
Danny W. Adair

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
41
darklow

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.

12
Josir

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 objet
  • has_add_permission() return False évite de créer de nouveaux objets pour ce modèle
7
Iván Zugnoni

Si 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
4
Wouter

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.

4
grrrrrr

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.

4
Mark Chackerian

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
2
lastoneisbearfood

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

1
Timothy

lecture seule => permission de vue

  1. pipenv install Django-admin-view-permission
  2. ajoutez 'admin_view_permission' à INSTALLED_APPS dans le fichier settings.py.like: `INSTALLED_APPS = [ 'admin_view_permission',
  3. python manage.py migrer
  4. python manage.py runserver 6666

ok.avoir du plaisir avec la permission 'vues'

0
Xianhong Xu

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, ... )
0
Enric Mieza