How to read non-Django HTML form data?

In the past I’ve used an idiom like this to retrieve Django Form data:

  if request . method == 'POST':
    form = MyForm ( request . POST )
    if form . is_valid ():
      user_name = form . cleaned_data [ "user_name" ]
      user_email = form . cleaned_data [ "user_email" ]
      # etc.

Recently I needed to write a dynamic form, using Javascript. (It’s a table with a user-expandable number of rows. Later there will be logic such that the second cell in each row is equal to the first cell in the next row).

There’s no Django Form for this form; it’s just HTML and Javascript. Therefore there’s nothing to play the role that MyForm plays in the snippet above. How can I extract the data from that? Ideally as JSON?

If it’s being submitted as HTML form-data, you can access it directly from request.POST
.
If it’s being submitted as the POST body as Json, you can access it using request.body. (Actually, if you need raw access to the posted data, you can still access it through request.body.)

Hello again Ken! Sometimes I wonder how much of the internet runs on your advice.

you can access it directly from request.POST

Thanks!

So … I thought it was being submitted as HTML form data, but I’m not finding it.

Here’s the template. Isn’t that submitting HTML form data? (I think you can ignore the Javascript – I use that to insert and delete rows. That dynamic aspect is the reason I had to write Javascript instead of using a Django Form.)

The view onto that template hardly does anything:

def dynamic_form ( request ):
  if request . method == 'POST':
    filename = 'dynamic_table.pickle'
    with open(filename,'wb') as f:
      f.write(
        pickle.dumps(request.body) )
    return HttpResponseRedirect (
      reverse (
        'run_make:thank-you ) )
  else: return render (
      request,
      'run_make/dynamic_form.html' )

As you can see, the view now pickles the request.POST object, so that I can check it out in the REPL. But the result is gibberish. It seems to only contain a CSRF token:

appuser@127:/mnt/django$ python3 ./manage.py shell
Python 3.8.2 (default, Mar 26 2020, 15:53:00)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.22.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import pickle
   ...: filename = '/home/appuser/dynamic_table.pickle'
   ...: with open(filename,'rb') as file_object:
   ...:   req = pickle.loads( file_object . read () )
   ...:

In [2]: req
Out[2]: <QueryDict: {'csrfmiddlewaretoken': ['N6YhYNlk8TTp97NykhGBAUrDwsyVQeFi
rMbSRikjKgsH42rVAtQxkZUaMCvlwMHl']}>

So now I have two questions: (1) Is my form data not in fact being delivered, and (2) is the pickling dance overly complex? It would be great if I didn’t have to take the intermediate pickling and unpickling steps, and could instead somehow access objects the webpage creates directly from the REPL.

Hi Jeffrey,

For question 1, you can view what’s being submitted over the wire using your browser’s Developer Tools and the Network Panel. Open it up, then submit the form. You’ll see the request appear in the panel and be able to inspect it. That said, I’m pretty sure your forms data is not being included because there aren’t any name attributes on the inputs.

For example, you should use something like:

<input type="number" step="any" id="Min income" name="min_income" />

Then in your view you could access that via:

request.POST['min_income']

Things get a bit trickier when you start using the same form multiple times in the same request. You can use a formset or prefix the name of the input yourself, similar to what you’re doing with the id attribute.

In regards to question 2, you should look into using a debugger. There’s the pdb module and larger IDE’s have them built in such as VSCode and PyCharm. There’s also the classic print(request.POST), however you can’t interact with that. You only get to see the results printed out.

Hopefully that helps unstick you.

-Tim

@CodenameTim is spot on with the core of your questions. Just a couple side points I’d like to address as well.

The two actually work quite well together. The Django Formsets combined with just a little bit of JavaScript form a very useful and usable combination.

Very little actually - this isn’t even the most active of the available communications channels for Django. (It is IMO, however, the “user-friendliest” mode, as I’m no fan of IRC and not much a fan of mailing lists as a Q&A / support vehicle.) I’m exceedingly grateful to @andrewgodwin & co for making this available to us.

I’m unstuck! I didn’t realize those name attributes were important. As soon as I added them, it worked. Thanks, @CodenameTim!

I had no idea you could use pdb with Django. That’s cool! But will it work if I’m using Apache? I start running the server as a background service via service apache2 start, so I can’t imagine how the shell that the debugger starts would find me. I think I have to use Apache in order for some file upload/download static file stuff to work, for reasons that are over my head but Ken explained them once](Do I need Apache? If so, I'm lost configuring it. - #4 by JeffreyBenjaminBrown).

Thanks for recommending Formsets, @KenWhitesell. Now that this works it’s hard for me to imagine learning about them right now but if I ever need to do something bigger I will.

I’m not aware of a way to use a debugger with apache or nginx for that matter. I’ve only used debuggers for local development. Can you clone your environment to your local machine (if using a remote environment) and use the runserver command?

What you’re looking for is remote-pdb. However, to effectively use it, you’ll probably want to configure uwsgi to only run a single instance.

Also see:

1 Like

Ah, that’s pretty neat. Thank you for sharing that.