Перенаправление ошибок без лишних копий / вставок? - PullRequest
1 голос
/ 14 декабря 2009

Если у меня есть представление, которое обрабатывает управление друзьями, то есть имеется представление для добавления, удаления, блокировки, разблокировки и принятия / отклонения приглашений стать друзьями. Проблема, с которой я столкнулся, заключается в том, что я пытаюсь предоставить значимые ошибки пользователям, которые в конечном итоге получают URL, которого не должно быть.

Например, если Пользователь1 и Пользователь2 уже являются друзьями, а Пользователь1 переходит на URL для добавления Пользователь2 в качестве друга вместо друга форма представляется так, как если бы они не были друзьями, а форма не работает 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

И далее дублируется как ошибки проверки формы в случае, если представление обойдено в оболочке и форма используется независимо.

Я спрашиваю, есть ли более питонский способ сделать это без чрезмерного копирования и вставки?

Редактировать

Я пытался решить эту проблему и задавался вопросом, будет ли что-то подобное хорошим способом.

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.

Ответы [ 2 ]

1 голос
/ 14 декабря 2009

Python декораторы идеально подходят для этого.

Определите все проверки, которые вы делаете в декораторе, и украсьте все ваши add_friend, remove_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
0 голосов
/ 14 декабря 2009

Я полагаю, что код не полностью дублируется внутри remove_friend и block_user и т. Д. (Поскольку сообщения будут другими) - вы, вероятно, можете преобразовать некоторые из ваших функций в отдельные вспомогательные функции, которые принимают двух пользователей и возвращают некоторый вид статус, указывающий на успех или неудачу.

Трудно быть более полезным без примера, скажем, вашего pending_invitations вида (который, вероятно, наиболее отличается от add_friend).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...