Como posso tornar um modelo totalmente somente leitura na interface de administração? É para uma espécie de tabela de log, onde estou usando os recursos do administrador para pesquisar, classificar, filtrar etc, mas não há necessidade de modificar o log.
Caso pareça uma duplicata, não é o que estou tentando fazer:
- Não estou procurando campos somente leitura (mesmo tornar cada campo somente leitura ainda permitiria a criação de novos registros)
- Não estou procurando criar um usuário somente leitura : todo usuário deve ser somente leitura.
django
django-admin
readonly
Steve Bennett
fonte
fonte
has_view_permission
foi finalmente implementado no Django 2.1. Consulte também stackoverflow.com/a/51641149 abaixo.Respostas:
Veja 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_action = "delete_selected" if del_action in actions: del actions[del_action] 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 %}
fonte
Model
, ou para oModelAdmin
?ModelAdmin
.O administrador é para edição, não apenas visualização (você não encontrará uma permissão de "visualização"). Para conseguir o que deseja, você terá que proibir a adição, exclusão e tornar todos os campos somente leitura:
class MyAdmin(ModelAdmin): def has_add_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False
(se você proibir a mudança, você nem conseguirá ver os objetos)
Para alguns códigos não testados que tentam automatizar a configuração de todos os campos somente leitura, veja minha resposta para Modelo inteiro como somente leitura
EDITAR: também não testado, mas acabei de dar uma olhada no meu LogEntryAdmin e
readonly_fields = MyModel._meta.get_all_field_names()
Não sei se isso funcionará em todos os casos.
EDIT: QuerySet.delete () ainda pode excluir objetos em massa. Para contornar isso, forneça seu próprio gerenciador de "objetos" e a subclasse QuerySet correspondente que não exclui - veja Substituindo QuerySet.delete () no Django
fonte
Aqui estão duas classes que estou usando para fazer um modelo e / ou em linha somente para leitura.
Para 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
fonte
has_add_permission
inReadOnlyAdmin
leva apenas a solicitação como parâmetroSe você quiser que o usuário saiba que não pode editá-lo, faltam 2 peças na primeira solução. Você removeu a ação de exclusão!
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: a solução somente leitura funciona bem em modelos simples. Mas NÃO funciona se você tiver um modelo herdado com chaves estrangeiras. Infelizmente, ainda não sei a solução para isso. Uma boa tentativa é:
Modelo inteiro como somente leitura
Mas também não funciona para mim.
E uma nota final, se você quiser pensar em uma solução ampla, você deve garantir que cada inline também seja somente leitura.
fonte
Na verdade, você pode tentar esta solução simples:
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 a lista suspensa com a opção "Excluir selecionados ..."list_display_links = None
: evita clicar nas colunas para editar esse objetohas_add_permission()
retornar False evita a criação de novos objetos para esse modelofonte
Isso foi adicionado ao Django 2.1 que foi lançado em 01/08/18!
ModelAdmin.has_view_permission()
é como a has_delete_permission, has_change_permission e has_add_permission existentes. Você pode ler sobre isso nos documentos aquiDas notas de lançamento:
fonte
Se a resposta aceita não funcionar para você, tente o seguinte:
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
fonte
Compilar as excelentes respostas de @darklow e @josir, além de adicionar um pouco mais para remover os botões "Salvar" e "Salvar e continuar", leva a (na sintaxe 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
e então você usa como
class MyModelAdmin(ReadOnlyAdmin): pass
Só tentei fazer isso com Django 1.11 / Python 3.
fonte
A resposta aceita deve funcionar, mas isso também preservará a ordem de exibição dos campos somente leitura. Você também não precisa codificar o modelo com esta solução.
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
fonte
Com Django 2.2 eu faço assim:
@admin.register(MyModel) class MyAdmin(admin.ModelAdmin): readonly_fields = ('all', 'the', 'necessary', 'fields') actions = None # Removes the default delete action in list view def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False
fonte
readonly_fields
eactions
não são necessáriascom o django 2.2, o admin somente leitura pode ser tão simples quanto:
class ReadOnlyAdminMixin(): def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False class LogEntryAdmin(ReadOnlyAdminMixin, admin.ModelAdmin): list_display = ('id', 'user', 'action_flag', 'content_type', 'object_repr')
fonte
Eu encontrei o mesmo requisito ao precisar tornar todos os campos somente leitura para certos usuários no django admin. Acabei aproveitando o módulo django "django-admin-view-permission" sem rolar meu próprio código. Se você precisar de um controle mais refinado para definir explicitamente quais campos, você precisará estender o módulo. Você pode verificar o plugin em ação aqui
fonte
somente leitura => permissão de visualizações
pipenv install django-admin-view-permission
6666
ok.se divirta-se com a permissão 'visualizações'
fonte
Eu escrevi uma classe genérica para lidar com a visualização ReadOnly dependendo das permissões do usuário, incluindo inlines;)
Em 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
Em 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 )
Então, podemos apenas herdar normalmente nossas classes em admin.py:
class ContactAdmin(ReadOnlyAdmin): list_display = ("name","email","whatever") readonly_fields = ("updated","created") inlines = ( PhoneInline, ... )
fonte