แบบอ่านอย่างเดียวในอินเทอร์เฟซผู้ดูแลระบบ Django?


86

ฉันจะสร้างโมเดลแบบอ่านอย่างเดียวอย่างสมบูรณ์ในอินเทอร์เฟซผู้ดูแลระบบได้อย่างไร เป็นตารางบันทึกประเภทหนึ่งซึ่งฉันใช้คุณลักษณะของผู้ดูแลระบบเพื่อค้นหาเรียงลำดับกรองและอื่น ๆ แต่ไม่จำเป็นต้องแก้ไขบันทึก

ในกรณีที่ดูเหมือนจะซ้ำกันนี่ไม่ใช่สิ่งที่ฉันกำลังพยายามทำ:

  • ฉันไม่ได้มองหาฟิลด์แบบอ่านอย่างเดียว(แม้ว่าการทำให้ทุกฟิลด์เป็นแบบอ่านอย่างเดียวก็ยังให้คุณสร้างเรกคอร์ดใหม่ได้)
  • ฉันไม่ต้องการสร้างผู้ใช้แบบอ่านอย่างเดียวผู้ใช้ทุกคนควรอ่านอย่างเดียว

2
ฟีเจอร์นี้น่าจะพร้อมใช้งานเร็ว ๆ นี้: github.com/django/django/pull/5297
Bosco

2
has_view_permissionในที่สุดก็ถูกนำมาใช้ใน Django 2.1 โปรดดูstackoverflow.com/a/51641149ด้านล่าง
djvg

คำตอบ:


21

ดู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

เทมเพลต / 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 %}

template / admin / view.html (สำหรับ 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 %}

ดูเหมือนว่าเชื่อถือได้. เป็นเวลานานมากแล้วที่ฉันใช้ Django แต่อาจรอดูว่าผู้แสดงความคิดเห็นคนอื่นพูดถึงอะไร
Steve Bennett

นี่คือ mixin สำหรับModelหรือสำหรับModelAdmin?
OrangeDog

สำหรับไฟล์ModelAdmin.
Pascal Polleunus

สำหรับ Django 1.8 และใหม่กว่า get_all_field_names จะเลิกใช้งาน ย้อนกลับทางเข้ากันได้กับพวกเขาได้รับ วิธีที่สั้นเพื่อให้พวกเขา
fzzylogic

คุณสามารถใช้ has_add_permission
rluts

70

ผู้ดูแลระบบมีไว้สำหรับแก้ไขไม่ใช่เพียงแค่ดู (คุณจะไม่พบสิทธิ์ "ดู") เพื่อให้บรรลุสิ่งที่คุณต้องการคุณจะต้องห้ามไม่ให้เพิ่มลบและทำให้ฟิลด์ทั้งหมดเป็นแบบอ่านอย่างเดียว:

class MyAdmin(ModelAdmin):

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

(ถ้าคุณห้ามเปลี่ยนคุณจะไม่ได้เห็นวัตถุ)

สำหรับโค้ดที่ยังไม่ผ่านการทดสอบที่พยายามตั้งค่าฟิลด์ทั้งหมดแบบอ่านอย่างเดียวโดยอัตโนมัติให้ดูคำตอบของฉันสำหรับWhole model เป็นแบบอ่านอย่างเดียว

แก้ไข: ยังไม่ได้ทดสอบ แต่เพิ่งดู LogEntryAdmin ของฉันและมีไฟล์

readonly_fields = MyModel._meta.get_all_field_names()

ไม่รู้ว่าจะใช้ได้ทุกกรณีหรือเปล่า

แก้ไข: QuerySet.delete () อาจยังคงลบวัตถุจำนวนมาก ในการแก้ไขปัญหานี้ให้จัดเตรียมตัวจัดการ "วัตถุ" ของคุณเองและคลาสย่อย QuerySet ที่เกี่ยวข้องซึ่งจะไม่ลบ - ดูการแทนที่ QuerySet.delete () ใน Django


2
PS: และใช่เช่นเดียวกับในคำตอบอื่น ๆ วิธีที่จะไปคือการกำหนดสามสิ่งเหล่านี้ในคลาส ReadOnlyAdmin จากนั้นคลาสย่อยจากที่ใดก็ตามที่คุณต้องการพฤติกรรมนั้น แม้จะได้รับแฟนซีและให้คำนิยามของกลุ่ม / สิทธิ์ที่จะได้รับอนุญาตให้แก้ไขแล้วกลับจริงตามลําดับ (และใช้ get_readonly_fields () ซึ่งมีการเข้าถึงการร้องขอและดังนั้นผู้ใช้ปัจจุบัน)
แดนนี่ดับเบิลยูเอแดร์

เกือบจะสมบูรณ์แบบ. ฉันถามอย่างตะกละตะกลามว่ามีวิธีที่จะไม่มีแถวเชื่อมโยงไปยังหน้าแก้ไขได้หรือไม่? (อีกครั้งไม่จำเป็นต้องขยายแถวใด ๆ และไม่จำเป็นต้องแก้ไขอะไรเลย)
Steve Bennett

1
หากคุณตั้งค่า list_display_links ของ ModelAdmin เป็นสิ่งที่ประเมินว่าเป็น False (เช่นรายการว่าง / ทูเปิล) ModelAdmin .__ init __ () จะตั้งค่า list_display_links เป็นคอลัมน์ทั้งหมด (ยกเว้นช่องทำเครื่องหมายการดำเนินการ) โปรดดู options.py ฉันเดาว่าเสร็จแล้วเพื่อให้แน่ใจว่ามีลิงก์ ดังนั้นฉันจะแทนที่ __init __ () ใน ReadOnlyAdmin เรียกผู้ปกครองจากนั้นตั้งค่า list_display_links เป็นรายการว่างหรือทูเปิล เนื่องจากตอนนี้คุณไม่มีลิงก์ไปยังแบบฟอร์มการเปลี่ยนแปลงแบบอ่านอย่างเดียวอาจเป็นการดีที่สุดที่จะสร้างแอตทริบิวต์พารามิเตอร์ / คลาสสำหรับสิ่งนี้ - ฉันไม่คิดว่าจะเป็นพฤติกรรมที่ต้องการโดยทั่วไป Hth
Danny W. Adair

เกี่ยวกับ readonly_fields ที่ถูกตั้งค่าจากโมเดลสิ่งนี้อาจใช้ไม่ได้หากคุณแทนที่แบบฟอร์มและเพิ่มฟิลด์อื่น ๆ ... โดยยึดตามฟิลด์แบบฟอร์มจริงน่าจะดีกว่า
Danny W. Adair

สิ่งนี้ใช้ไม่ได้: def __init __ (self, * args): super (RegistrationStatusAdmin, self) .__ init __ (* args) self.display_links = []
Steve Bennett

50

นี่คือสองชั้นเรียนที่ฉันใช้เพื่อสร้างแบบจำลองและ / หรือเป็นแบบอินไลน์อ่านอย่างเดียว

สำหรับผู้ดูแลรุ่น:

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

สำหรับอินไลน์:

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

วิธีที่คุณใช้ทั้งสองคลาสกับคลาสย่อยหนึ่งคลาส เช่นถ้าฉันมีฟิลด์ปกติและอินไลน์ในชั้นเรียน? ฉันสามารถขยายทั้งสองอย่างได้หรือไม่?
Timo

@timo ใช้คลาสเหล่านี้เป็น
มิกซ์

1
has_add_permissionin ReadOnlyAdminใช้เฉพาะการร้องขอเป็นพารามิเตอร์
MartinM

has_change_permission () จะต้องถูกแทนที่ด้วย def has_change_permission (self, request, obj = None):
david euler

13

หากคุณต้องการให้ผู้ใช้ทราบว่าไม่สามารถแก้ไขได้แสดงว่าไม่มี 2 ชิ้นในโซลูชันแรก คุณได้ลบการดำเนินการลบ!

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

ประการที่สอง: โซลูชันแบบอ่านอย่างเดียวใช้งานได้ดีกับโมเดลธรรมดา แต่จะไม่ได้ผลหากคุณมีโมเดลที่สืบทอดมาพร้อมคีย์ต่างประเทศ น่าเสียดายที่ฉันยังไม่รู้วิธีแก้ปัญหานั้น ความพยายามที่ดีคือ:

ทั้งแบบอ่านอย่างเดียว

แต่มันก็ไม่ได้ผลสำหรับฉันเช่นกัน

และหมายเหตุสุดท้ายหากคุณต้องการคิดวิธีแก้ปัญหาแบบกว้าง ๆ คุณต้องบังคับว่าอินไลน์แต่ละอันจะต้องอ่านอย่างเดียวด้วย


11

จริงๆแล้วคุณสามารถลองวิธีง่ายๆนี้:

class ReadOnlyModelAdmin(admin.ModelAdmin):
    actions = None
    list_display_links = None
    # more stuff here

    def has_add_permission(self, request):
        return False
  • actions = None: หลีกเลี่ยงการแสดงรายการแบบเลื่อนลงด้วยตัวเลือก "ลบรายการที่เลือก ... "
  • list_display_links = None: หลีกเลี่ยงการคลิกในคอลัมน์เพื่อแก้ไขวัตถุนั้น
  • has_add_permission() การส่งคืน False หลีกเลี่ยงการสร้างวัตถุใหม่สำหรับโมเดลนั้น

1
สิ่งนี้ห้ามไม่ให้เปิดอินสแตนซ์ใด ๆ เพื่อดูฟิลด์อย่างไรก็ตามหากมีใครดีเพียงแค่แสดงรายการก็ใช้ได้
Sebastián Vansteenkiste

8

สิ่งนี้ถูกเพิ่มเข้ามาใน Django 2.1 ซึ่งเปิดตัวเมื่อ 1/8/18!

ModelAdmin.has_view_permission()ก็เหมือนกับ has_delete_permission ที่มีอยู่ has_change_permission และ has_add_permission คุณสามารถอ่านได้ในเอกสารที่นี่

จากบันทึกประจำรุ่น:

ซึ่งช่วยให้ผู้ใช้สามารถเข้าถึงแบบจำลองได้ในผู้ดูแลระบบ ModelAdmin.has_view_permission () ใหม่ การใช้งานเข้ากันได้แบบย้อนหลังโดยที่ไม่จำเป็นต้องกำหนดสิทธิ์ "ดู" เพื่ออนุญาตให้ผู้ใช้ที่มีสิทธิ์ "เปลี่ยนแปลง" แก้ไขวัตถุได้


ผู้ใช้ขั้นสูงจะยังคงสามารถแก้ไขวัตถุในอินเทอร์เฟซผู้ดูแลระบบได้ใช่ไหม?
Flimm

ถูกต้องเว้นแต่คุณจะแทนที่หนึ่งในวิธีการเหล่านี้เพื่อเปลี่ยนพฤติกรรมเพื่อไม่อนุญาตการเข้าถึงของ superusers
grrrrrr

6

หากคำตอบที่ยอมรับไม่ได้ผลสำหรับคุณให้ลองทำดังนี้:

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

5

การรวบรวมคำตอบที่ยอดเยี่ยมของ @darklow และ @josir รวมถึงการเพิ่มอีกเล็กน้อยเพื่อลบปุ่ม "บันทึก" และ "บันทึกและดำเนินการต่อ" จะนำไปสู่ ​​(ในไวยากรณ์ 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

แล้วคุณใช้เช่น

class MyModelAdmin(ReadOnlyAdmin):
    pass

ฉันได้ลองกับ Django 1.11 / Python 3 เท่านั้น


เป็นเวลานานมากแล้วที่ฉันใช้ Django มีใครรับรองเรื่องนี้อีกไหม?
Steve Bennett

@SteveBennett ㄹมีข้อกำหนดมากมายสำหรับที่อยู่นี้ ... คำตอบนี้ไม่แน่นหนา ... แนะนำคำอธิบายที่นี่: stackoverflow.com/a/36019597/2586761และคำตอบที่คุณแสดงความคิดเห็นในstackoverflow.com / a / 33543817/2586761สมบูรณ์กว่าคำตอบที่ยอมรับ
ptim

3

คำตอบที่ยอมรับควรใช้งานได้ แต่จะยังคงรักษาลำดับการแสดงของฟิลด์แบบอ่านอย่างเดียว คุณไม่จำเป็นต้องฮาร์ดโค้ดโมเดลด้วยโซลูชันนี้

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

3

ด้วย Django 2.2 ฉันทำเช่นนี้:

@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

ด้วย django 2.2 ไม่จำเป็นต้องใช้readonly_fieldsและactionsเส้น
cheng10

3

ด้วย django 2.2 ผู้ดูแลระบบแบบอ่านอย่างเดียวสามารถทำได้ง่ายๆดังนี้:

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')

1

ฉันพบข้อกำหนดเดียวกันนี้เมื่อต้องการทำให้ทุกฟิลด์เป็นแบบอ่านอย่างเดียวสำหรับผู้ใช้บางรายในผู้ดูแลระบบ django ลงเอยด้วยการใช้ประโยชน์จากโมดูล django "django-admin-view-permission" โดยไม่ต้องรีดโค้ดของฉันเอง หากคุณต้องการการควบคุมที่ละเอียดมากขึ้นเพื่อกำหนดฟิลด์ที่ชัดเจนคุณจะต้องขยายโมดูล คุณสามารถตรวจสอบการทำงานของปลั๊กอินได้ที่นี่


0

อ่านอย่างเดียว => ดูสิทธิ์

  1. pipenv install django-admin-view-permission
  2. เพิ่ม 'admin_view_permission' ใน INSTALLED_APPS ใน settings.py.like สิ่งนี้: INSTALLED_APPS = ['admin_view_permission',
  3. python Manage.py โยกย้าย
  4. python Manage.py รันเนอร์เวอร์ 6666

โอเคขอให้สนุกกับการอนุญาต 'ดู'


0

ฉันได้เขียนคลาสทั่วไปเพื่อจัดการมุมมองแบบอ่านอย่างเดียวขึ้นอยู่กับสิทธิ์ของผู้ใช้รวมถึงอินไลน์;)

ใน 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

ใน 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 )

จากนั้นเราสามารถสืบทอดคลาสของเราได้ตามปกติใน admin.py:

class ContactAdmin(ReadOnlyAdmin):
    list_display = ("name","email","whatever")
    readonly_fields = ("updated","created")
    inlines = ( PhoneInline, ... )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.