【问题标题】:Django - how to reuse code in function-based view with decoratorsDjango - 如何使用装饰器在基于函数的视图中重用代码
【发布时间】:2021-05-28 13:04:19
【问题描述】:

我有一些代码必须重复用于多个视图,所以我想创建一个装饰器,这样我就不必复制和粘贴多行代码。

所以我需要在不同视图中重用的代码是:

@login_required
def add_custom_word_song(request, target_word, source_word, pk):
    """
    Add new word
    """
    if request.method == 'POST':
        form = WordForm(request.POST)
        if form.is_valid():
            deck_name = form.cleaned_data['deck_name']
            source_word = form.cleaned_data['source_word']
            target_word = form.cleaned_data['target_word']
            fluency = form.cleaned_data['fluency']
            user = request.user
            Word.objects.create(user=user, target_word=target_word,
                source_word=source_word, deck_name=deck_name, fluency=fluency)
            return HttpResponseRedirect(reverse('vocab:list'))
    if request.method =="GET":
        user = request.user
        request.session['pk'] = pk
        form = CustomWordForm(initial={'user': user, 'target_word': target_word, 'source_word': source_word, 'deck_name': 'My Words', 'fluency': 0})

    return render(request, 'vocab/add_custom_initial_song.html', {'form': form, 'pk': pk})

对于其他视图,唯一会更改的代码部分是最后一行中的模板部分。所以我试着把除了最后一行之外的所有东西都放在装饰器中,但后来我得到了:

TypeError: add_custom_word() missing 3 required positional arguments: 'target_word', 'source_word', and 'pk'

我尝试了不同的变体,但仍然得到相同的错误。

【问题讨论】:

  • 似乎您正在尝试重新发明轮子。看docs.djangoproject.com/en/3.1/topics/class-based-views
  • 我知道基于类的视图,但在这种情况下似乎没有合适的视图? @Oussama
  • 您可以继承并适应您的需求。无论如何,错误消息中的函数名称在您共享的代码中不存在。 add_custom_word() 在哪里被调用?
  • 这就是我所说的装饰器(代码和上面一样,只是名字不同)
  • 你能分享你的装饰器代码吗?问题不在上面的代码中

标签: python python-3.x django django-views decorator


【解决方案1】:

如果我理解正确,您想编写一些代码,例如:

def add_custom_word(request, target_word, second_word, pk):
    ...

@add_custom_word
def add_custom_word_song(request, target_word, second_word, pk):
    return render(request, 'vocab/add_custom_initial_song.html', {'form': form, 'pk': pk})

在这种情况下,如果您调用add_custom_word_song,则意味着:

add_custom_word_song = add_custom_word(add_custom_word_song)
add_custom_word_song()

Python 在模块初始化时首先将add_custom_word_song 作为第一个参数传递给add_custom_word,它们调用新的add_custom_word_song

所以你会得到你所说的错误:add_custom_word() missing 3 required positional arguments: 'target_word', 'source_word', and 'pk'

如果你真的要使用装饰器,你需要重新包装一下:

def add_custom_word(func):
    def wrapper(request, target_word, second_word, pk):
        ...
        return func(request, target_word, second_word, pk)
    return wrapper

@decorator
def add_custom_word_song(request, target_word, second_word, pk):
    return render(request, 'vocab/add_custom_initial_song.html', {'form': form, 'pk': pk})

但如果你只想更改模板文件,考虑使用注册表来管理模板内容!

编辑:

一个最简单的注册表可能像一个字典:

registry = {
    "song": "vocab/add_custom_initial_song.html",
    "image": "vocab/add_custom_initial_image.html",
    ...
}

您可以将某些类型定义为键,然后将模板文件定义为值。所以你可以像这样返回它:

def add_custom_word(...):
    ...
    return render(request, registry[return_type], {'form': form, 'pk': pk})

如果您有一些复杂的条件,您可以有一个自定义注册表类,它总是有registermatch 方法。

class Registry(object):
    def __init__(self):
        self.registry_list = []

    def register(self, conditions, template_file):
        self.registry_list.append([conditions, template_file])

    def match(self, condition):
        for conditions, template_file in self.registry_list:
            if match(condition, conditions):
                return template_file

registry = Registry()

然后你可以使用这个注册表来获取模板文件:

    def add_custom_word(...):
    ...
    return render(request, registry.match(condition), {'form': form, 'pk': pk})

【讨论】:

  • 谢谢!能否请您扩展一下您的最后陈述?
  • @MeL 希望这就是你想要的!
猜你喜欢
  • 1970-01-01
  • 2016-11-09
  • 2023-03-14
  • 2023-02-07
  • 2012-06-11
  • 2011-08-29
  • 2017-03-08
  • 2015-06-23
相关资源
最近更新 更多