【问题标题】:DRY views in DjangoDjango 中的 DRY 视图
【发布时间】:2012-07-12 18:38:00
【问题描述】:

我已经构建了一个应用程序,该应用程序具有许多应该能够使用相同基本代码的相似视图。然而,每种方法在方法内的不同拐点处都具有某些独特的特征,因此我无法找到一种方法来构造它以实际重用任何代码。相反,我创建了一种剪切和粘贴方法,并单独调整了每种方法。应用程序的这一部分是我编写的第一个 Python 代码,并且知道必须有更好的方法来做到这一点,但我被锁定在这样做并且“它有效”,所以我看不到出路.

下面是基本视图模板的基本外观:

def view_entity(request, entity_id=None):
    if request.method == 'POST':
        return _post_entity(request, entity_id)
    else:
        return _get_entity(request, entity_id)

def _get_entity(request, entity_id):
    data = _process_entity(request, entity_id)
    if 'redirect' in data:
        return data['redirect']
    else:
        return _render_entity(request, data['form'])

def _post_entity(request, entity_id):
    data = _process_entity(request, entity_id)
    if 'redirect' in data:
        return data['redirect']
    elif data['form'].is_valid():
        # custom post processing here
        instance = data['form'].save()
        return HttpResponseRedirect(reverse('entity', args=[instance.id]))
    else:
        return _render_entity(request, data['form'])


def _process_entity(request, entity_id):
    data = {}

    if entity_id != 'new':  # READ/UPDATE
        # sometimes there's custom code to retrieve the entity
        e = entity_id and get_object_or_404(Entity.objects, pk=entity_id)
        # sometimes there's custom code here that deauthorizes e
        # sometimes extra values are added to data here (e.g. parent entity)
        if e:
            if request.method == 'POST':
                data['form'] = EntityForm(request.POST, instance=e)
                # sometimes there's a conditional here for CustomEntityForm
            else:
                data['form'] = EntityForm(instance=e)
        else:  # user not authorized for this entity
            return {'redirect': HttpResponseRedirect(reverse('home'))}
        # sometimes there's custom code here for certain entity types

    else:  # CREATE
        if request.method == 'POST':
            data['form'] = EntityForm(request.POST)
        else:
            data['form'] = EntityForm()

    # sometimes extra key/values are added to data here
    return data

我什至没有包括所有可能的变化,但正如您所见,_process_entity 方法需要根据正在处理的实体类型进行大量个性化定制。这是我无法找到处理此问题的 DRY 方法的主要原因。

感谢任何帮助,谢谢!

【问题讨论】:

  • 你使用的是什么版本的 Django?如果是 Django 1.3 或更高版本,您可以使用基于类的视图来使您的视图更加 DRY。
  • 我使用的是 Django 1.3。你能详细说明一下吗?
  • 参见docs.djangoproject.com/en/dev/topics/class-based-views - 每个视图都是一个类,它允许您使用和重用其他视图的功能。但是您的视图似乎做了很多事情:它创建对象、加载对象、呈现多种不同的形式、执行重定向——这太多了。这一切都需要在 URL 上完成吗?否则我会将其重构为多个视图。
  • 感谢您的参考。如果没有更深入的洞察力的人碰巧提供有用的基本起点,我将尝试弄清楚如何自己重构为多个视图。 :)

标签: python django model-view-controller dry


【解决方案1】:

使用基于类的视图。您可以使用类中的继承和其他功能来使您的视图更具可重用性。您还可以使用内置的通用视图来简化一些基本任务。

检查class-based views documentation。你也可以阅读这篇this

【讨论】:

    【解决方案2】:

    所以我最终将代码重构为我所有视图都继承自的基类。我最终并没有重构为多个视图(还),而是通过在处理方法中插入钩子来解决具有自定义处理方法的问题。

    这是继承自DetailView 的基类的要点:

    class MyDetailView(DetailView):
        context = {}
    
        def get(self, request, *args, **kwargs):
            self._process(request, *args, **kwargs)
    
            if 'redirect' in self.context:
                return HttpResponseRedirect(self.context['redirect'])
    
            else:
                return self._render(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            self._process(request, *args, **kwargs)
    
            if 'redirect' in self.context:
                return HttpResponseRedirect(self.context['redirect'])
    
            elif self.context['form'].is_valid():
                self._get_hook('_pre_save')(request, *args, **kwargs)
                return self._save(request, *args, **kwargs)
    
            else:
                return self._render(request, *args, **kwargs)
    
        def _process(self, request, *args, **kwargs):
            form = getattr(app.forms, '%sForm' % self.model.__name__)
    
            if kwargs['pk'] != 'new':  # READ/UPDATE
                self.object = self.get_object(request, *args, **kwargs)
    
                self._get_hook('_auth')(request, *args, **kwargs)
    
                if not self.object:  # user not authorized for this entity
                    return {'redirect': reverse(
                        '%s_list' % self.model.__name__.lower())}
    
            self.context['form'] = form(
                data=request.POST if request.method == 'POST' else None,
                instance=self.object if hasattr(self, 'object') else None)
    
            self._get_hook('_post_process')(request, *args, **kwargs)
    
        def _get_hook(self, hook_name):
            try:
                return getattr(self, '%s_hook' % hook_name)
            except AttributeError, e:
                def noop(*args, **kwargs):
                    pass
                return noop
    

    要注意的关键部分是_get_hook 方法以及我使用它的其他方法中的位置。这样,在一些复杂的视图中,我可以像这样注入自定义代码:

    class ComplexDetailView(MyDetailView):
        def _post_process_hook(self, request, *args, **kwargs):
            # here I can add stuff to self.context using 
            # self.model, self.object, request.POST or whatever
    

    这使我的自定义视图很小,因为它们继承了大部分功能,但我可以添加该特定视图所需的任何调整。

    【讨论】:

      猜你喜欢
      • 2010-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 2011-02-16
      • 2018-12-16
      相关资源
      最近更新 更多