Cannot save POST data

I have a page that has a list of questions, in a table, with a radio button to select your choice, with values 0 - 5 assigned to each of the buttons. Each question is assigned a name= the question ID.
I want to use the POST data to populate the answers into my SQLLite3 DB.
I can extract the values and use it for calculations, but if I try to save the form it always comes up Invalid. The problem appears to be the data is somehow blank when I try to retrieve it by question.id; which I think is the key for the values.

model.py

class Question(models.Model):
question = models.CharField(max_length=200)
readonly_fields=(“id”)
#name = models.CharField(max_length=20, unique=True) # THis is probably useless

topic = models.ForeignKey(
    Topic, on_delete=models.CASCADE, null=True
)  # Django will store this as topic_id on database


def __str__(self):
    return f"{self.question}"

class Answer(models.Model):
value = models.IntegerField(null = True)
#name = models.CharField(max_length=20, null = True)
# q_id = models.CharField(max_length=10, default=“XX”) # USed foreignkey instead
question = models.OneToOneField(
Question, on_delete=models.CASCADE, null = True,
)

def __str__(self):
    return f"{self.question} value is {self.value}"

class Comment(models.Model):
name = models.CharField(max_length=20)
comments = models.CharField(max_length=1000, default=“No comment”)
area = models.ForeignKey(
Area, on_delete=models.CASCADE, null = True
)
forms.py

from django import forms
from .models import Answer, Comment, Question, Area, Topic

class AnswerForm(forms.ModelForm):
class Meta:
model = Answer
fields = “all

views.py snippet using the form:

if request.method == “POST”:
keys=request.POST.keys()
values=list(request.POST.values())
print("Keys: ",keys, “values: “, values)
value1=request.POST.get(“question.id[1]”,””)
print("Value1: ", value1)
form=AnswerForm(request.POST)
if form.is_valid():
print(“Valid”)
form.save()
else:
print(“Invalid”)

questions.html: (Edited out repeated for loops to generate table rows)

{% extends “layout.html” %}

{% block content %}

div.item_leadership { background-color: rgb(211,211,211); } {% csrf_token %}

{{ areaheader.1}}

    {% for question in questions1 %}
            <tr>
              <td class="question">{{question}}</td>
                  {% for n in nchoices %}
                      {% if n == 0 %} 
                      <td>
                      
                        <input name= {{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> Not Sure</lable>
                  
                      </td>
                      {% else %}
                      <td>
                      
                      <input name={{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> {{n}}</lable>
                      </td>
                      {% endif %}
                  {% endfor %}
              </tr>
        {% endfor%}
</table>

<h4>Enter any comments about your responses to the questions</h4>
<textarea name="{{ textname }}">"Enter Comments Here"</textarea >

<input type="submit">

{% endblock %}

Results from print statements:

Keys: dict_keys([‘csrfmiddlewaretoken’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘10’, “[‘L_Text’]”]) values: [‘Gll91n4FY9UrF10WjlnKeEiHhJCFCIFEzZvGZVLQK5aoppfAQgsHRzcjuxuWGcxt’, ‘1’, ‘2’, ‘3’, ‘3’, ‘4’, ‘4’, ‘4’, ‘5’, ‘5’, ‘5’, ‘“Enter Comments Here”’]
Value1:
Invalid

{{ columnheader.0}} {{ columnheader.1}} {{ columnheader.2}} {{ columnheader.3}} {{ columnheader.4}} {{ columnheader.5}} {{ columnheader.6}}
{{ topic.0 }}

Welcome @jlc1978 !

You’re doing a lot of work yourself on constructing the HTML that Django would do for you, along with trying to handle the post submissions.

If you have not already done so, I suggest you work your way through the Official Django Tutorial. It teaches you how to work with forms.

For more details about that, you can also read the docs starting with Working with forms | Django documentation | Django.

You will find doing this to be a whole lot easier if you learn how to take advantage of the features that Django provides for working with forms, instead of trying to manually handle them yourself.

Thanks. I’ve been working through it but it often just leaves me more confused. I’ll keep at it, though.

For example, I’ve gone through it again to the forms section and it doesn’t display a radio button. Clearly I have done something wrong but I’ve redone the code from step 1 to no avail.

Templates may be cleaner and less work, but personally, I prefer to do the HTML as I can get it exactly like I want that way. I just like to see what’s under the hood.

I’ll give them a try if not using them is what is preventing me from getting post data into the SQL DB; but I suspect that is not the case.

You would want to define that form field as using a RadioSelect widget.

It’s not preventing you from doing so. It’s just going to make everything a whole lot easier.

Ok, but unless. missed it the tutorial doesn’t talk about creating a form to use RadioSelect, it just creates a standard <input type = radio … button:

{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>

I’m not sure what I am missing.
I appreciate your help.

You’re not missing anything here. That’s only as far as the tutorial takes you. Forms are far too deep a topic to be covered in any real detail in the tutorial.

This is where you need to go beyond the tutorial, and start working from the full docs, specifically, the Working with forms and the Creating forms from models pages.

The Working with Forms page gets more into the details of what a Django form is, how they work, how they validate input (an absolutely critical step), how they can be customized to use different widget templates (e.g., radio buttons instead of a select list or checkboxes), and some other topics.

The Creating Forms from Models shows how Django can create a form from a model. Django will create the fields and assign a default set of widgets (which can be overriden), along with directly saving the data entered in that form to the database.

Thanks, I suspected that; although it is confusing when the tutorial doesn’t work or refers to items not in the example, such as referring to ListView when it seems they mean IndexView.

I’ll keep pressing on until I get it to work.

Can you clarify what portions of the tutorial you’re referring to here?

Sure. Both are in Part 4

The HTML code I used was that in the “Write a minimal form” section¶
That code doesn’t render a radio button so submit always returns teh error message. I must have done something else wrong but have gone over the earlier parts to check and fin no errors.

Where it refers to ListView is in the section “Amend views”

I am making progress on getting blues in the db since Django supports writing SQL queries

Jim

I was actually looking for the specifics code or quotes from the docs here, to be able to help identify the mistakes or misunderstandings. (I can assure you that the tutorial is 100% functional.)

ListView is the proper name of the generic view. The IndexView is a ListView. The reason that the docs make the reference to the ListView is that the code shown is modifying the behavior of the parent class, it is not something specific to, nor modifying the behavior of, IndexView.

Almost never necessary, and definitely not necessary for the type of situation you’re facing here. You should be creating and saving the model instances. (The ModelForms do assist you with that.) That is the purpose of the models and the ORM.

Sorry.

Here is the code:

<!doctype html>
<html lang="en-US">
    <head>
    </head>
    <body>

  <form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    <fieldset>
        <legend><h1>{{ question.question_text }}</h1></legend>
        {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
        {% for choice in question.choice_set.all %}
            <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
            <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
        {% endfor %}
    </fieldset>
    <input type="submit" value="Vote">
    </form>

</body>
</html>

I think my confusion comes from the model names being the same as teh Django view names; is that required? If not, why use the same names? Would it not be better to use names not the same as Django names for views? Or add your explanation as to why ListView uses generic.IndexView?

My comment isn’t a complaint, just an observation from some who has written documentation and tutorials.

As for SQL, I could not get models to input data so I fall back to what I can get working rather than continuing to beat my head against a wall. To quote Patton: “A good plan violently executed now is better than a perfect plan executed next week.”

I’d prefer to use models and the ORM, but I’ve been trying to get it to work for over a week and been unsuccessful.

Your template looks right. You mention an error:

What error message are you getting? (It’s possible that the issue is in the view that is rendering the template, and not the template itself.)

Where do you see this? The models being referenced are Question and Choice. The views being created include detail, results, vote, index, IndexView, DetailView, ResultsView. I see no such identical naming.

I understand. But if improvements are to be made, we need to understand what you find confusing.

And we’re here to try and help you with that - but things do need to be handled in an appropriate order. It doesn’t make much sense to try and save a form into a model, if you aren’t starting with a proper form.

The error message is rendered on the page, and is the one from the vote function where it checks for an answer and returns “You didn’t select a choice.” since there is no radio button to chose. The views.py code is working properly just not rendering a button to make a choice.

"error_message": "You didn't select a choice."

Here is the code on part 4 that uses the names in a way I found confusing:

class IndexView(generic.ListView):
    template_name = "polls/index.html"
    context_object_name = "latest_question_list"

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by("-pub_date")[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = "polls/detail.html"


class ResultsView(generic.DetailView):
    model = Question
    template_name = "polls/results.html" "error_message": "You didn't select a choice."

Maybe it’s just me, but I saw DesignView and DetailView used the same name for generic.; but not IndexView. After some digging and thought, I figured the generic.ListView was what needed to be referenced but IndexView was just any name like any other class statement.

As for the ORM, that’s my problem: I can’t figure out what I have done wrong with form. I have a models.py and HTML code in tags; following what was done in the tutorial.

Maybe I need fields tags inside of form?

I realize I could make the HTML simpler using a template, but I wanted to get the layout to work and the data get inputed before I try to make the code using templates to avoid multiple errors causing more confusion.

This implies that you have no data in the Choice model for the Question being displayed.

The data for the Choice model is entered back in step 2. You need to verify that the data exists for the selected Question.

These are subclasses. The generic.DetailView is the parent class. The subclasses, DetailView and ResultsView are both subclasses of that parent class.

Briefly, a subclass of a generic.DetailView will display information about one instance of one model. A subclass of a generic.ListView is designed to provide summary information about a list of instances of a model.

The docs at Built-in class-based generic views | Django documentation | Django give a good overview with examples of usage of these generic view classes.

(Side note: If you’re not very familiar with Python classes and class inheritance, that might be a topic you would want to review. Comfort with these topics is something that is assumed by Django.)

I don’t think you’ve done anything wrong with the form - at least not what you’ve shown here so far. I thing what you’re dealing with in the tutorial is not having the right data in your database.

Thanks for your patience.

I forgot register choice, which I discovered when I went the Admin Console and there were no Choices. I suspect because of the way VS Code shell works I thought I was entering them but wasn’t. Fixed that, entered choices in Admin C and all works.

I’ll review inheritance.

Question:
the forloop.counter knows how many times to go through from the code:

 {% for choice in question.choice_set.all %}

based on the number of choices in question.choice_set.all.
So, more generically any for x in Y a for loop.counter will run for the number of x in y, correct?

Now I’m back to figuring out why my original question form doesn’t work. I noticed the tutorial includes fieldset tags, which appears to be used to group buttons vertically under 1 question in the legend tag. I’ve laid out my buttons inline with the question to render a table, rather than vertically under a question…

I can brute force the data into the tables, but as you point out that is suboptimal, if only becasue it doesn’t create unique IDs like Django so I have to deal with that as well.

If you’re looking at rendering multiple questions (with choices) on the same page, that raises a couple of other issues. In this situation, you’d want to be looking at formsets.

I would suggest that you get the handling of one question (with choices) as you wish before adding that next level of complexity.

You can do this with the RadioSelect widget. The docs for that widget show how you can render the individual options within a tag. (In this case, perhaps a <td> tag?)

That is correct. More generically, what the for loop makes available to you is detailed in the docs for the for tag.

Thanks again.

My form renders just like I want, in a nice table with headers, etc. Flexbox works fine when I change screen sizes.

I also change the gray to green/yellow/red based on the answers score.

I’ll look at the RadioWidget and formsets in more detail.

I just can’t figure out why I can’t get the data into my database tables. Maybe it’s becasue I’m numbering and not using question name as the key. I’ll just have to play with it until it works, I guess.

It becomes very straight-forward and easy, once you have the forms defined properly. If you create this table as a proper formset with forms, then you don’t need to try manually assigning html attributes to the individual elements - that’s work that Django does for you.

Thanks,

I’ll reduce it to one radio button to get it to work and build from there.