没有那么多的复制/粘贴,很好地重定向错误?

时间:2009-12-14 11:27:51

标签: django validation error-handling views

如果我有一个处理朋友管理的视图,这意味着可以处理添加,删除,阻止,解除阻止和接受/拒绝邀请成为朋友的视图。我遇到的问题是当我尝试向最终找到他们不应该访问的网址的用户提供有意义的错误时。

例如,如果 User1 User2 已经是朋友, User1 会转到用于添加 User2 的网址如果朋友不是朋友,而且表单在unique_together = (('user_from', 'user_to'),)失败,则会显示警告消息,并在显示表单之前将其重定向到适当的页面。

喜欢这个

def add_friend(request, username):
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        messages.error(request, 'A user with the username %s does not exist. \
            Try searching for the user below.' % username)
        return HttpResponseRedirect(reverse('friends_find_friend'))

    if Friend.objects.are_friends(request.user, user):
        messages.error(request, 'You are already friends with %s' % user)
        return HttpResponseRedirect(reverse('profiles_profile_detail', args=[user]))

如果没有这样的用户,这还包括检查和有意义的错误消息(而不是404)。

这很容易处理,但随着其他检查,它会增长到

def add_friend(request, username):
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        messages.error(request, 'A user with the username %s does not exist. \
            Try searching for the user below.' % username)
        return HttpResponseRedirect(reverse('friends_find_friend'))

    if user == request.user:
        messages.error(request, 'You are already friends with yourself')
        return HttpResponseRedirect(reverse('friends_find_friend'))

    if Enemy.objects.is_blocked(request.user, user):
        messages.error(request, '%s has blocked you from adding them as a friend' % user)
        return HttpResponseRedirect(reverse('friends_find_friend'))

    if Enemy.objects.has_blocked(request.user, user):
        messages.error(request, 'You have blocked %s so you cannot add them as a friend' % user)
        return HttpResponseRedirect(reverse('profiles_profile_detail', args=[user]))

    if Friend.objects.are_friends(request.user, user):
        messages.error(request, 'You are already friends with %s' % user)
        return HttpResponseRedirect(reverse('profiles_profile_detail', args=[user]))

    if FriendRequest.objects.invitation_sent(request.user, user):
        messages.error(request, 'You already sent %s a request. You need to \
            wait for them to reply to it.' % user)
        return HttpResponseRedirect(reverse('friends_pending'))

    if FriendRequest.objects.invitation_received(request.user, user):
        messages.error(request, '%s already sent you a request and is waiting \
            for you to respond to them.' % user)
        return HttpResponseRedirect(reverse('friends_pending'))

所有这些在

中再次重复
  • REMOVE_FRIEND
  • block_user
  • unblock_user
  • pending_invitations

如果在shell中绕过视图并且表单是独立使用的,则进一步复制为表单验证错误。

我要问的是,如果没有过多的复制和粘贴,有更多的pythonic方法来实现这一目标吗?

修改

我一直试图解决这个问题,并且想知道这样的事情是不是一个好方法。

tests = ((Enemy.objects.is_blocked, 'This user has blocked you', reverse('friends_find_friend')),
         (Enemy.objects.has_blocked, 'You have blocked this user', reverse('profiles_profile_detail', args=[user])),)

for test in tests:
    if test[0](request.user, user):
        messages.error(request, test[1])
        return HttpResponseRedirect(test[2])

测试将在另一个类似于url模式的文件中定义,并且装饰器将包装视图函数以在所有测试中运行,如果有任何失败则重定向,并且如果一切正常则最终将其传递给视图函数。这是一种有效的方法管理方式,而不会使用数百行样板代码堵塞视图文件吗?

Edit2

我也有兴趣了解其他人如何做类似的事情。我怀疑我是第一个想要向用户显示消息而不是仅仅抛出404页面的人。

2 个答案:

答案 0 :(得分:1)

Python装饰器是完美的。

定义您在装饰器中进行的所有验证,并用它装饰您的所有add_friendremove_friend等。

更新

对于你的情况,它看起来像这样:

def do_friending_validation(fun):
    def validate_function(*args,**kwargs):
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            messages.error(request, 'A user with the username %s does not exist. \
                Try searching for the user below.' % username)
            return HttpResponseRedirect(reverse('friends_find_friend'))

        if user == request.user:
        messages.error(request, 'You are already friends with yourself')
        return HttpResponseRedirect(reverse('friends_find_friend'))

        if Enemy.objects.is_blocked(request.user, user):
        messages.error(request, '%s has blocked you from adding them as a friend' % user)
        return HttpResponseRedirect(reverse('friends_find_friend'))

        if Enemy.objects.has_blocked(request.user, user):
        messages.error(request, 'You have blocked %s so you cannot add them as a friend' % user)
        return HttpResponseRedirect(reverse('profiles_profile_detail', args=[user]))

        if Friend.objects.are_friends(request.user, user):
        messages.error(request, 'You are already friends with %s' % user)
        return HttpResponseRedirect(reverse('profiles_profile_detail', args=[user]))

        if FriendRequest.objects.invitation_sent(request.user, user):
        messages.error(request, 'You already sent %s a request. You need to \
            wait for them to reply to it.' % user)
        return HttpResponseRedirect(reverse('friends_pending'))

        if FriendRequest.objects.invitation_received(request.user, user):
        messages.error(request, '%s already sent you a request and is waiting \
            for you to respond to them.' % user)
        return HttpResponseRedirect(reverse('friends_pending'))
        fun(*args,**kwargs)

    return validate_function

@do_friending_validation
def add_friend(request, username):

    #Do your stuff here

@do_friending_validation    
def remove_friend(request, username):

    #Do your stuff here

答案 1 :(得分:0)

我认为代码在remove_friendblock_user等内部并不是完全重复的(因为消息会有所不同) - 你可以将一些函数重构为单独的辅助函数,这些函数需要两个用户和返回表示成功或失败的某种状态。

如果没有您的pending_invitations视图示例(这似乎与add_friend最不同),则很难提供更多帮助。