【问题标题】:Redirecting errors nicely without so much copy/paste?无需太多复制/粘贴即可很好地重定向错误?
【发布时间】:2009-12-14 11:27:50
【问题描述】:

如果我有一个处理朋友管理的视图,这意味着有一个视图来处理添加、删除、阻止、取消阻止以及接受/拒绝成为朋友的邀请。我遇到的问题是,当我尝试向用户提供有意义的错误时,这些错误最终会出现在他们不应该出现的 url 上。

例如,如果 User1User2 已经是朋友,并且 User1 转到将 User2 添加为的 url一个朋友而不是表单,就像他们不是朋友一样呈现,并且表单在 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

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

我要问的是,是否有一种更 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 模式的文件中定义,装饰器将包装视图函数以运行所有测试,如果有任何失败则重定向,如果一切正常,最后将其传递给视图函数。这会是一种有效的管理方法,不会用数百行样板代码阻塞视图文件吗?

编辑2

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

【问题讨论】:

    标签: django validation error-handling views


    【解决方案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
    

    【讨论】:

    • 我想过使用装饰器,但唯一保持完全相同的验证是第一个用户验证。其他人的消息会非常轻微(即您已经是add_friends中的朋友,而您还不是remove_friends中的朋友。
    • 然后只需在装饰器中设置错误标志并根据视图中引发的标志装饰器选择消息。
    【解决方案2】:

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

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

    【讨论】:

    • pending_invitations 仅检查按下了哪个提交按钮(确认或拒绝),其行为与另外两个称为 acceptdeny 的视图非常相似,具有相同的检查以确保用户存在,有一个待处理的请求,您没有尝试接受您发送的请求等。
    猜你喜欢
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多