4

I HAD this situation:

Clicking on a html submit button, I call views.stream_response which "activates" views.stream_response_generator which "activates" stream.py and return a StreamingHttpResponse and I see a progressive number every second up to m at /stream_response/:

1
2
3
4
5
6
7
8  //e.g. my default max value for m

stream.py

from django.template import Context, Template
import time         


def streamx(m):
    lista = []
    x=0
    while len(lista) < m:      
        x = x + 1
        time.sleep(1)
        lista.append(x)
        yield "<div>%s</div>\n" % x  #prints on browser
        print(lista)     #print on eclipse
    return (x)

views.py

def stream_response(request):   // unified the three functions as suggested

if request.method == 'POST':
    form = InputNumeroForm(request.POST)
    if form.is_valid():
        m = request.POST.get('numb', 8)
        resp = StreamingHttpResponse(stream.streamx(m))
        return resp

forms.py

from django.db import models
from django import forms
from django.forms import ModelForm

class InputNumero(models.Model):
    m = models.IntegerField()


class  InputNumeroForm(forms.Form):    
    class Meta:
        models = InputNumero
        fields = ('m',)

urls.py

...
url(r'^homepage/provadata/$', views.provadata),    
url(r'^stream_response/$', views.stream_response, name='stream_response'),
...

homepage/provadata.html

<form id="streamform" action="{% url 'stream_response' %}" method="POST">
  {% csrf_token %}
  {{form}}
  <input id="numb" type="number"  />
  <input type="submit" value="to view" id="streambutton" />
</form>

If I delete "8" and use only m = request.POST.get('numb') I obtain:

ValueError at /stream_response/ The view homepage.views.stream_response didn't return an HttpResponse object. It returned None instead.

So, if I try to submit, it takes only the default value 8 (and works) but it not takes my form input. What is it wrong?

-->UPDATE: with @Tanguy Serrat suggestions:

views.py

def stream_response(request):
    form = InputNumeroForm()
    if request.method == 'POST':
        form = InputNumeroForm(data=request.POST)
        if form.is_valid():
            #Accessing the data in cleaned_data
            m = form.cleaned_data['numero']

            print("My form html: %s" % form)      
            print ("My Number: %s" % m) #watch your command line
            print("m = ", m) 
            resp = StreamingHttpResponse(stream.streamx(m))
            return resp

    #If not post provide the form here in the template :
    return render(request, 'homepage/provadata.html', {'form': form,})

forms.py

class  InputNumeroForm(forms.Form):
    numero = models.IntegerField()

homepage/provadata.py

<form  action="/stream_response/" method="POST">
    {% csrf_token %}
    {{form}}                                <!--issue: does not appear on the html !!!!!-->
    <input type="number" name="numero" />   <!--so I write this-->
    <input type="submit" value="to view" />
</form>

If I give as input e.g. 7 from keyboard:

KeyError at /stream_response/

'numero' It takes the input number

WHILE

If I write m = request.POST.get('numero'), in command line I have:

...
My form html: 
My Number: 7
m =  7
...
   while len(lista) < m:
   TypeError: unorderable types: int() < str()
Trix
  • 587
  • 1
  • 6
  • 27
  • 1
    Just curious why isn't all this in one function? Also, where is `stream_response_generator_bis()` defined? – onyeka May 06 '15 at 14:46
  • Sorry an error during copy and paste! stream_response_generator() without _bis! I use several functions because it's more readable and controllable for me...I don't know how to do a "all-in-one" type function. – Trix May 06 '15 at 14:51
  • 1
    Is the view above the same as the one currently giving you the error? Doesn't look like it is. – onyeka May 08 '15 at 10:46
  • No it refers to suggestions.. ok I update the view, you are right, thanks. – Trix May 08 '15 at 11:35

3 Answers3

6

EDIT : ModelForm part removed, no need to save the data in DB so using classic form this way :

Method 1 : Classic Form without Model using Django

in your forms.py

from django import forms

class InputNumeroForm(forms.Form):

    numero = forms.IntegerField()

in your views.py

from django.shortcuts import render

def stream_response(request):
    form = InputNumeroForm()
    if request.method == 'POST':
        form = InputNumeroForm(data=request.POST)
        if form.is_valid():
            #Accessing the data in cleaned_data
            m = form.cleaned_data['numero']      
            print "My Number %s" % m #watch your command line 
            resp = StreamingHttpResponse(stream.streamx(m))
            return resp

    #If not post provide the form here in the template :
    return render(request, 'homepage/provadata.html', {
        'form': form,
    })

in your template :

<form id="streamform" action="{% url 'stream_response' %}" method="POST">
  {% csrf_token %}
  {{ form }}
  <input type="submit" value="to view" id="streambutton" />
</form>

To clarify a little there are two types of form in Django :

  • Classic Forms which do not require saving in a database
  • Model Forms which will allow you to create forms which are based on a Database Model i.e. (you can add a row to your db or edit one)

Here you don't need to save your number in your database so you use classical forms: https://docs.djangoproject.com/en/1.8/topics/forms/

Method 2 : Not using the Django Form For very simple forms like your case :

in views.py

from django.shortcuts import render

def stream_response(request):
    if request.method == 'POST':
        if request.POST.get('numero', False):
            m = int(request.POST['numero'])      
            print "My Number %s" % m #watch your command line 
            resp = StreamingHttpResponse(stream.streamx(m))
            return resp

    return render(request, 'homepage/provadata.html')

in your template :

<form id="streamform" action="{% url 'stream_response' %}" method="POST">
  {% csrf_token %}
  <input type="number" name="numero" />
  <input type="submit" value="to view" id="streambutton" />
</form>
Tanguy Serrat
  • 322
  • 1
  • 7
  • Yes I DON'T want to save the values of *InputNumeroForm*... I understand that it may seem strange, but I'm studying some Django things and I am very interested to understand how they work. Now I try with your suggestions. – Trix May 07 '15 at 14:06
  • Oh okay! I'll edit my answer to give you the classic form solution. Btw : when you define your models, make sure to declare them in models.py and not in forms.py – Tanguy Serrat May 07 '15 at 14:11
  • Thanks for the clarifications about Forms and ModelForms. I observed that print needs parenthesis () (I'm working with Python 3.4) but this is not a trouble.. while I don't understand **m = form.cleaned_data['numero']**... where do I have to define *'numero'* ? It gives me *KeyError at /stream_response/*. And last, it's necessary to put **otherwise I'm not able to type any number as input "m". – Trix May 07 '15 at 14:44
  • 1
    Sorry I'm on 2.7! No Django will handle the html input for you, when you define : `numero = forms.IntegerField()` and you add {{ form }} in your html it will create an input with `name="numero"` Are you sure that you are passing the form to your template ? Try printing this `print("my form %s" % form.cleaned_data.__dict__)` And also `print("My form html: %s" % form)` you'll see how django builds your form – Tanguy Serrat May 07 '15 at 14:58
  • Ok for the explanation about input... I am a dumb: this is not the first time that I create an input form as you told... BUT now.... the submit button appears while the input field not and KeyError persists... :-/ I can pass the form to the template with action = " view ulr ", is it right? – Trix May 07 '15 at 15:32
  • 2
    The KeyError is normal as the form isn't rendering on your template. Make sure your are doing what I gave you in the views.py with `return render(request, 'homepage/provadata.html', { 'form': form, })`. If you can't achieve this you can add this : `` But this won't help you to figure out why you can't render the {{ form }} – Tanguy Serrat May 07 '15 at 15:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77216/discussion-between-tanguy-serrat-and-trix). – Tanguy Serrat May 07 '15 at 15:49
0

Django views MUST return a HttpResponse object. Considering that the other two functions you're referencing have only one line each, it would serve you better to just put everything in one function, or at least move the StreamingHttpResponse to the main function.

Edit: The StreamingResponseHttp must be given an iterable so, going back to the stream.py, try taking out the return and print functions (I've taken out excess stuff for experimentation's sake). This worked for me in the shell.

def streamx(m):
    lista = []
    x=0
    while len(lista) < m:      
        x = x + 1
        time.sleep(1)
        lista.append(x)
        yield "<div>%s</div>\n" % x  #prints on browser

        #c = Context({'x': x})
        #yield Template('{{ x }} <br />\n').render(c)  


def stream_response(request):

    if request.method == 'POST':
        form = InputNumeroForm(request.POST)
        if form.is_valid():
            m = request['numb']

            resp = StreamingHttpResponse(streamx(m))
            return resp
onyeka
  • 1,517
  • 13
  • 18
  • 1
    I tried and it returns: _TypeError at /stream_response/ 'WSGIRequest' object is not callable_ – Trix May 07 '15 at 07:55
  • 1
    Sorry, but I don't understand how it can work written in this way; it is necessary to initialize lista = [ ] ("lista" corresponds to "list" in english), to put *lista.append(x)* to add values and also it is necessary to return something ( *return (x)* ), isn't it? I'm new in programming and maybe "I'm losing something on the way" :-/. – Trix May 07 '15 at 09:28
  • 1
    You're right, I was looking at the example I ran in my shell. I added the list back. – onyeka May 07 '15 at 11:08
0

I see a handful of issues with the code you posted:

Your form field is missing the 'name' attribute, and thus the value set for this field will not be passed to your code. This is why your request.POST.get('numb') returns None unless you provide your default (8). Try this:

<input id="numb" name="numb" type="number"  />

Also, I noticed in your form you use models.IntegerField - why are you trying to use a model field here?

A better approach for you may be to add the numb field to your form and then retrieve the value from the form's cleaned data: form.cleaned_data['numb'] instead of the POST data.

Hopefully this gets you going.

Esteban
  • 2,444
  • 2
  • 19
  • 19
  • 1
    Adding `<... name="numb" ... />` it does not work also with the default value... What is the difference between `name=" "` and `id= " "`? Ok, I change the form definition... but I dont' know why it works with `models.IntegerField` and not with `forms.IntegerField`. – Trix May 07 '15 at 07:40