Redirect goes to wrong template

In a view that creates a new object, successful creation should redirect to a different page for edit. Instead, redirect remains on the new object page with the data just used. Stepping through the code in Visual Studio code, the expression HttpResponseRedirect(reverse('meal:mealEdit', args=(id, ))) evaluates to .../meal/{id}/edit yet renders .../meal/new. Guess the browser just likes that page?

Some code:

view.py:

def mealNew(request):
    if request.method=='POST':
        form = MealForm(request.POST)
        if form.is_valid():
            m_meal = form.save()
            id = m_meal.id
            HttpResponseRedirect(reverse('meal:mealEdit', args=(id, )))
    else:
        form = MealForm()
    return render(request, 'meal/new_meal_form.html', {
            'title' : 'New Meal',
            'form' : form,
        })

model.py:

class Meal(models.Model):
    meal_type = models.CharField(
        choices=MEAL_CHOICES,
        max_length=9,
        default = None,
        )
    meal_date = models.DateField()
    foods = models.ManyToManyField(Food)

    def __str__(self):
        if self.meal_type == '1':
            return 'Dinner'
        elif self.meal_type == '2':
            return 'Dinner'
        else:
            return 'Breakfast'

    def get_absolute_url(self):
        return reverse('mealEdit', kwargs={'pk': self.pk})

    class Meta:
        db_table = "meal"
        ordering = ['-meal_date', 'meal_type']
        verbose_name = 'Meal type'

class MealForm(ModelForm):
    class Meta:
        model = Meal
        fields = ['meal_type', 'meal_date']    
        widgets = {
            'meal_type' : forms.RadioSelect(),
            'meal_date' : DatePickerInput}
    
    def clean_meal_date(self):
        beginning = Meal.objects.order_by('id')[0].meal_date
        data = self.cleaned_data['meal_date']
        if data < beginning:
            raise ValidationError("Date is earlier than earliest meal")
        return data
 
    def clean(self):
        cleaned_data = super().clean()
        m_date = cleaned_data.get('meal_date')
        m_type = cleaned_data.get('meal_type')

        if Meal.objects.filter(meal_type=m_type, meal_date=m_date ):
            raise ValidationError("Meal already exists for this date")

new_meal_form.html:

{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block body %}
    <h1>Create new Meal</h1>
    <div class="row">
        <div class="col-6">
                <form action="{% url 'meal:mealNew' %}" method="POST">
                    {% csrf_token %}
                    {{ form|crispy }}
                    <input type="submit" value="Save">
                </form>
            <div id='food'></div>
        </div>
    </div>

    <a href="{# path('app_meal_index') #}">back to list</a>
{% endblock %}

edit.html:

{# meal/templates/meal/edit.html #}
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{# requires included, tableName, available #}
{% block body %}
<div class='row'>
    <div class='col-3'>
        <form action="" method="POST">
            {% csrf_token %}
            {{ form|crispy }}
            <input type="submit" value="Save">
        </form>
    </div>
    <div class='col-4'>
        <table class='table' id='ready_foods'>
            <thead>
                <tr>
                    <th>Included in meal</th>
                </tr>
            </thead>
            <tbody>
                {% for food in included %}
                    <tr>
                        <td data-foodid={{ food.id }} >{{ food }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>            
    </div>
    <div class='col-4'>
        {% include 'food/_foods.html' %}         
    </div>
</div>
{% endblock %}

I’m not following what you’re trying to say here, or what you’re looking at when you say

The HttpResponseRedirect does not get rendered into the page. You’ll never see that url in the browser.

It’s used within the server when a valid form is submitted via POST request.

Now, regarding:

Is this with the “new entry” page or the “edit page”? Or both? (The most common reason for this behavior with code structured the way you have it is that the form is failing the is_valid test.)

And a follow-up: You’re not returning cleaned_data from your clean method.

HttpResponseRedirect does not get rendered into the page: I must be misunderstanding what that does. Once the form is saved I want to redirect to an edit page (i.e., .../meal/{id}/edit) with the newly created object’s data.

...form is failing the is_valid test: this, at least, I know is not the case. The new object is created as confirmed by querying the database outside of Django. Or by getting the ValidationError message Meal already exists for this date if I submit again.

Specifically, that response returns an HTTP 302 redirect response. It instructs the browser to go to a different url. So the browser never gets that url in it’s page, it’s instructed to go there upon a successful form submission.

That’s good to know.
Do you have any JavaScript involved in the submission of this form?

If you look at the sequence of events in the network tab of your browser’s developer tools, you would see the GET request for the initial form with the blank form being returned to the browser. That would be followed by the POST submission of the form with the data. You would then see the 302 redirect being returned followed by the GET for the new page.

What I see in network is first a GET for meal/new plus a set of GETs for static, cached items such as jquery.min. Submitting the form shows a POST for meal/new and the same set of GETs. No other file containing meal appears. There is no 302 redirect.

That matches what you identified earlier, but doesn’t jive with what I’m seeing in the code.

First, repeating a couple things:

Did you fix your clean method?

and

Can you confirm that there’s no JavaScript involved in this?

I added return cleaned-data to class MealForm def clean(). There was no apparent difference in behavior when adding a new meal object.

There is bootstrap.js likely to be involved in the new_meal_form.html template. There will be javascript involved in edit.html; it is not yet added - I want to get there first.

Ahh - and something else I overlooked.

(First, it would be return cleaned_data not return cleaned-data, which I suspect you have right, otherwise you would get an error).

But more importantly, you need to return the HttpResponseRedirect…

And that explains why you’re seeing what you’re seeing.

Good grief! Amazing the simple things so easily overlooked. Thank you and your very detail-wary eyes. I’m back on the air - looking forward to (avoiding?) more rookie mistakes.