简体   繁体   中英

Django - how to reuse code in function-based view with decorators

I've got some code that I have to re-use for several views, so I would like to create a decorator so that I don't have to copy and paste many lines of code.

So the code I need to re-use in different views is:

@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})

And the only part of the code that will change for the other views is the template part in the last line. So I tried putting everything except the last line in the decorator, but then I get:

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

I tried different variations of this, but still get the same error.

If I understood correct, you would like to write some code like:

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

In this case, if you called add_custom_word_song it means:

add_custom_word_song = add_custom_word(add_custom_word_song)
add_custom_word_song()

Python while first pass add_custom_word_song as the first argument to add_custom_word when module initialing, they call the new add_custom_word_song .

So you will get the error as you said: add_custom_word() missing 3 required positional arguments: 'target_word', 'source_word', and 'pk'

If you really want to use decorator, you need wrap it again:

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

But if you only want to change the template file, consider use a registry to manage the template content!

Edit:

A simplest registry could like a dict:

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

You could define some types as keys, then define the template files as values. So you can return it like:

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

If you have some complex conditions, you could have a custom registry class, and it would always have register and match methods.

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

Then you could use this registry to get template files:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM