pythondjangohttp-redirectdjango-messages

Is there a way to control the messages after redirect or even clear messages?


Is there a way for me to stop the other validations after the first redirect

in views.py

from django.contrib import messages
# from django.core.exceptions import ValidationError
from django.shortcuts import render, redirect

def register(request):
    if request.method == 'POST':
        form = request.POST
        phone_number = validate_phone(request, form.get("phone_number"))
        username = validate_user_name(request, form.get("username"))
        password = validate_password(request, password1=form.get("password1"),password2=form.get("password2"))
        ....other_fields....
    return render(request, 'registration.html',)



def validate_user_name(request, username):
    username = username.strip()
    new = MyUser.objects.filter(username=username)
    if new.count():
        messages.error(request, "User with that Username Already Exist")
        return redirect("pre_register")
        # raise ValidationError("User with that Username Already Exist", code="username_error")
    return username

def validate_password(request, password1, password2):
    if password1 and password2 and password1 != password2:
        messages.error(request, "Passwords don't match")
        return redirect("pre_register")
        # raise ValidationError("Password don't match")
    return password2

    ....other_validations....

in registration.html:

<h3 class="register-heading">Apply as a Member</h3>
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message.tags }} : {{message}}</li>
{% endfor %}
<form method="post">
    <input type="text" class="form-control" placeholder="Username *" value=""
                                               required="required" name="username"/>
    <input type="text" minlength="10" maxlength="10" name="phone_number"
                                               class="form-control"
                                               placeholder="Your Phone *" value="" required="required"/>
    <input type="password" class="form-control" placeholder="Password *" value=""
                                               required="required" , name="password1"/>
    <input type="password" class="form-control" placeholder="Confirm Password *"
                                               value="" required="required" , name="password2"/>

</form>

Let's say a user enters a username that already exists, what I want is:

  1. To keep the already entered post data
  2. Once it hits this line
messages.error(request, "User with that Username Already Exist")
        return redirect("pre_register")

, I want just this as result(In template)

error: User with that Email ALready Exists

Instead, I get all the error messages at once

error: User with that Email ALready Exists
error: Passwords don't match
...all the other errors....

I know it's because technically, this is what I should get but how do I achieve what I want above. I think a way to do this would be to clear all the messages before I set the error messages but that would mean giving the server more work to do as it would still go through all of them before leaving just the last one (And the fact that I don't know how to even do it )

Any help on this would be appreciated immensely


Solution

  • First of all, I think you're confusing when to use render and when to use redirect. The usual pattern is to use redirect when your form validates so the user can't accidentally resubmit data, and to use render when the data is invalid so the errors get rendered. You're doing the opposite. See this antipattern for more details.

    Secondly, the issue is that your validation function returns an HTTP response. The user never gets redirected in your validation functions. You need to return the HTTP response from the view function for the user to get redirected. You could probably use the commented approach with raising ValidationErrors and then catching them in the view. If the error occurs, you add a message and render the template.

    Something like:

    def register(request):
        if request.method == 'POST':
            form = request.POST
            try:
                phone_number = validate_phone(request, form.get("phone_number"))
                username = validate_user_name(request, form.get("username"))
                password = validate_password(request, password1=form.get("password1"),password2=form.get("password2"))
                ....other_fields....
                
                # If all validations pass, you redirect
                return redirect('pre_register')
            except ValidationError as e:
                messages.error(request, str(e))
                # passes down to render
    
        # This gets called when it's GET request or the form validation failed.
        return render(request, 'registration.html',)