【问题标题】:django admin: separate read-only view and change viewdjango admin:单独的只读视图和更改视图
【发布时间】:2011-10-04 13:40:26
【问题描述】:

我想使用 django 管理员生成一个对象的只读视图,其中包含一个“编辑”按钮,可以将您切换到同一对象的通常更改视图。

我知道如何使用 readonly 属性来生成只读视图,但我不知道如何生成两个视图,一个是只读的,一个是允许更改的。

我想为此尽可能多地重用管理界面,而不是从头开始编写视图。

请注意,此问题与权限无关:所有用户都有权更改对象。只是我希望他们不要使用 change_view,除非他们确实打算进行更改,从而减少意外更改或同时更改的风险。

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    下面的代码是使用proxy models.实现只读管理员

    Models.py

    //真实模型

    class CompetitionEntry(models.Model):
        pass
    

    //代理模型

    class ReviewEntry(CompetitionEntry):
        class Meta:
            proxy = True
        def save(self, *args, **kwargs):
            pass
    

    admin.py

    //可编辑的管理员

    class CompetitionEntryAdmin(admin.ModelAdmin):
         pass
    admin.site.register(CompetitionEntry, CompetitionEntryAdmin)
    

    // 只读管理员(为此仅分配“更改”权限)

    class ReviewEntryAdmin(admin.ModelAdmin):
        pass
    admin.site.register(ReviewEntry, ReviewEntryAdmin)
    

    【讨论】:

    • 看起来这将呈现一个 看起来 可编辑的视图,但会在用户保存页面时默默地丢弃任何更改。
    • 是的。但是你可以使用 modeladmin.save_model() 方法来抛出警告信息。
    【解决方案2】:

    这是一个答案,只用几行代码和几个模板更改就可以满足我的要求:

    class MyModelAdmin(admin.ModelAdmin):
        fieldsets = [...]
    
        def get_readonly_fields(self, request, obj=None):
            if 'edit' not in request.GET:
                return <list all fields here>
            else:
                return self.readonly_fields
    

    现在 change_form 的常用 URL 将生成只读的 change_form,但如果您在 URL 后附加“?edit=1”,您将能够进行编辑。

    还可以根据 URL 中是否包含“?edit=1”来自定义 change_form 模板。为此,请将'django.core.context_processors.request' 放入TEMPLATE_CONTEXT_PROCESSORS 放入settings.py,然后在模板中使用request.GET.edit

    例如,要在不处于编辑模式时添加“编辑”按钮,请插入

      {% if not request.GET.edit %}                                                 
      <li><a href="?edit=1">Edit</a></li>                                           
      {% endif %} 
    

    就在&lt;ul class="object-tools"&gt; 之后的change_form.html

    作为另一个例子,将change_form.html 更改为包含

    {% if save_on_top and request.GET.edit %}{% submit_row %}{% endif %}            
    

    将意味着提交行将仅在编辑模式下显示。也可以使用这种方法隐藏内联等上的删除按钮。

    作为参考,这是我在settings.py中输入的内容:

    TEMPLATE_CONTEXT_PROCESSORS = (                                                 
        'django.contrib.auth.context_processors.auth',                              
        'django.core.context_processors.debug',                                     
        'django.core.context_processors.i18n',                                      
        'django.core.context_processors.media',                                     
        'django.contrib.messages.context_processors.messages',                      
        # Above here are the defaults.                                              
        'django.core.context_processors.request',                                   
    )
    

    【讨论】:

      【解决方案3】:

      我建议重新考虑使用自定义视图。在通用DetailView 的帮助下,您需要编写两行代码。该模板也不需要太多工作。您只需扩展标准 change_form.html 模板,覆盖 field_sets 块。

      我知道如何使用 readonly 属性来生成只读视图,但我不知道如何生成两个视图,一个是只读的,一个是允许更改的。

      您实际上可以使用proxy models 在管理员中注册一个模型两次[1]。 (代理模型的权限存在一些不一致,但在您的情况下可能不是问题。)

      似乎也可以注册多个管理站点[2]。

      我想为此尽可能多地重用管理界面,而不是从头开始编写视图。

      接口重用本身与视图几乎没有关系,主要是与模板和样式相关的东西。但是,正如您正确指出的那样,视图应该提供接口重用所需的模板上下文。

      如果您决定每个ModelAdmin 使用多个视图,那么检查django-reversion 项目如何实现其管理集成可能对您有用:reversion/admin.py

      参考文献

      【讨论】:

      • Django 管理员已经知道如何很好地呈现内容,包括标签、帮助文本、隐藏/显示等。如果我编写自己的模板,我不需要复制所有这些功能吗?而且我看不到如何轻松扩展 change_form.html,因为它会尝试访问通用视图中不存在的变量。
      • 你是对的,视图还需要提供所有必要的上下文,这可能需要一些工作。无论如何,似乎有更好的方法;我已经更新了答案。
      • 谢谢,这看起来是完成我想要的事情的好方法。只要我有足够的代表,我就会投票。我还发布了另一种简单的方法,很快就会发布第三种方法。
      • 你的方法很有趣,不知道只读字段可以根据请求被覆盖。但是,我认为在这里使用两个ModelAdmins 作为模型更合适。它更简单,并且允许轻松实现在这种情况下常见的进一步自定义(例如在任一视图中显示字段子集或内联)。
      【解决方案4】:

      您可以创建一个自定义视图并在那里显示您的对象。

      要在管理模块中创建自定义视图,请覆盖 get_urls() 方法:

      class MyAdmin(admin.ModelAdmin):
          …
          def get_urls(self):
              urls = super(MyAdmin, self).get_urls()
              my_urls = patterns('',
                  url(r'^custom_view/(?P<my_id>\d+)/$', self.admin_site.admin_view(self.custom_viem), name='custom_view')
              )
              return my_urls + urls
      
          def custom_view(self, request, my_id):
              """Define your view function as usual in views.py
      
              Link to this view using reverse('admin:custom_view')
      
              """
              from myapp import views
              return views.custom_view(request, my_id, self)
      

      在views.py中:

      def custom_view(request, object_id, model_admin):
          admin_site = model_admin.admin_site
          opts = model_admin.model._meta
          my_object = get_object_or_404(MyObject, pk=object_id)
      
          # do stuff
      
          context = {
              'admin_site': admin_site.name,
              'opts': opts,
              'title': _('My custom view'),
              'root_path': '%s' % admin_site.root_path,
              'app_label': opts.app_label,
              'my_object': my_object,
          }
      
          return render_to_response('my_template.html', context,
                  context_instance=RequestContext(request))
      

      在您的模板中,使用{% extends "admin/base_site.html" %} 来保持管理员的外观和感觉。

      【讨论】:

      • 这与只使用通用视图有什么不同,后者只需要几行代码(除了模板)?
      • 如果您可以使用通用视图实现相同的结果,那么这应该是要走的路。
      【解决方案5】:

      您将需要更改 django 管理员用于模型表单的模板。将其设为只读并向链接到另一个 url 的原始模板添加一个按钮。

      注意:

      我强烈反对这种方法,您绝对不会阻止同时进行更改。这应该通过锁定来解决。

      另外,我建议使用 django-reversion 来保留对象的历史记录并消除“意外更改”风险。

      【讨论】:

      • 您能否详细说明如何执行此操作? (关于你的其他 cmets:我认为你不能争辩说拥有一个对象的只读视图是一个坏主意,不管其他东西是如何设计的。我在这个问题上的唯一目标是通过利用管理界面已经能够很好地呈现事物的方式。)
      猜你喜欢
      • 1970-01-01
      • 2017-02-24
      • 2011-10-12
      • 1970-01-01
      • 2018-12-06
      • 2016-08-23
      • 2011-01-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多