i created a view + form that creates two widgets + a button for a user. One to select a choice and another to type something in. Now i want to redirect the user after the clicking the button to another webpage displaying his input. (Generally i want to know how to access the userinput and further use it).
This is my form:
class Eingabefeld(forms.Form):
eingabefeld = forms.CharField(label="Flight Number",max_length=20)
a = Auswahlmoeglichkeiten.objects.all()
flughafenname = forms.ModelChoiceField(label="Target Airport",queryset=a,empty_label="-------")
source = forms.CharField(
max_length=50,
widget=forms.HiddenInput(),
required=False
)
This is my views.py:
def get_eingabe(request):
log = logging.getLogger(__name__)
if request.method =="POST":
eingabe = Eingabefeld(request.POST)
log.warn(eingabe)
if eingabe.is_valid():
return HttpResponseRedirect("answerrequest")
else:
eingabe = Eingabefeld()
return render(request, "app_one/labels.html", {"eingabe": eingabe})
def answerrequestseite(request):
return render(request, "app_one/answerrequest.html")
and this is my html ( the included html in this one is just for layout):
<head>
<title>Home</title>
</head>
<form method="post" novalidate>
{% csrf_token %}
{% include "app_one/bootstrap_layout2.html" with form=eingabe %}
<div class="row">
<div class="col-sm-5"></div>
<div class="col-sm-2">
<button type="submit" class="btn btn-primary btn-block">Let's Go!</button>
</div>
<div class="col-sm-5"></div>
</div>
</form>
So basically when opening my webpage "get_eingabe" gets called, and the template gets rendered, now when clicking the button the input is validated and after successfull validation a different URL is opened which will trigger the method "answerrequestseite". Now how do i pass the userinput (eingabefeld and flughafenname) into the other method which will render the template for the second URL?
I read alot about using "request.GET" but i am not quite sure where exactly to place it and how.
After if eingabe.is_valid(): create some variable containing the values you want.
then in you redirect you need to pass those values as get argument like:
your_url/?id=123
Then you can retrieve your variable in your views.py via
request.GET.get('id')
But in your case, you don't want to pass simple id, you want to pass user_input.
One way will be to sanitize this input to make it url compatible.
Otherwise the more flexible solution is to store the values in the session.
Session (via cookie)
# views.py
# Set the session variable
request.session['you_variable_name_here'] = 'the value'
# Retrieve the session variable
var = request.session.get['you_variable_name_here']
https://docs.djangoproject.com/en/2.2/topics/http/sessions/
For your exemple in the first view:
if eingabe.is_valid():
eingabefeld = eingabe.cleaned_data.get('eingabefeld')
flughafenname = eingabe.cleaned_data.get('flughafenname')
request.session['eingabefeld'] = eingabefeld
request.session['flughafenname'] = flughafenname.pk
return HttpResponseRedirect("answerrequest")
In the second view:
def answerrequestseite(request):
eingabefeld = request.session.get('eingabefeld')
flughafenname_pk = request.session.get('flughafenname')
flughafenname = YourFlughafennameModel.objects.get(pk=flughafenname_pk)
return render(request, "app_one/answerrequest.html",{'eingabefeld':eingabefeld,'flughafenname':flughafenname})
Related
i am trying to build a django search functionality for my app but the input form keeps returning a none
views.py
def search(request):
if request.method == 'POST':
query = request.POST.get('text')
houses = Product.objects.filter(name__contains='query')
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
search.html
<form>
<input type='text' placeholder='search houses>
<button type='submit'>Search</button>
</form>
First off, your python indentation is invalid, and your HTML is also invalid on the input line. I will assume this is a typo in the question, but if not, you have issues there.
Your main problem is the filter for houses:
houses = Product.objects.filter(name__contains='query')
is looking for a name containing the string "query". You need the variable you've just defined.
houses = Product.objects.filter(name__contains=query)
You have an indentation issue in the code you have posted.
You need to add action and method in your Form.
<form action="/url_of_search/" method="post">
Missing quote in input line.
<input type='text' placeholder='search houses'>
You need to use query instead of 'query' in the filter.
Product.objects.filter(name__contains=query)
Things missing in html code:
form action attribute
form method attribute
input field name attribute
<!-- add form attributes method and action -->
<form method="POST" action="{% url '<url_name>' %}">
<!-- add input attribute name to identify the field and pass the value in request body -->
<input type='text' placeholder='search houses' name='search_text'>
<button type='submit'>Search</button>
</form>
update views for search
def search(request):
if request.method == 'POST':
# use input field name to get the search text
query = request.POST.get('search_text')
houses = Product.objects.filter(name__contains=query)
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
My simple web-application has two models that are linked (one to many).
The first model (Newplate) has a boolean field called plate_complete. This is set to False (0) at the start.
questions:
In a html page, I am trying to build a form and button that when pressed sets the above field to True. At the moment when I click the button the page refreshes but there is no change to the database (plate_complete is still False). How do I do this?
Ideally, once the button is pressed I would also like to re-direct the user to another webpage (readplates.html). This webpage does not require the pk field (but the form does to change the specific record) Hence for now I am just refreshing the extendingplates.html file. How do I do this too ?
My code:
"""Model"""
class NewPlate(models.Model):
plate_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
plate_complete = models.BooleanField()
"""view"""
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
newplate.plate_complete = True
newplate.save()
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete = True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
"""URLS"""
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('extendplates/<pk>/', views.publish_plates, name='publishplates'),
"""HTML"""
<form method="POST" action="{% url 'tablet:publishplates' newplate.plate_id %}">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button></form>
-------Added show plates view:---------
def show_plates(request,pk):
mod = NewPlate.objects.all()
newplate= get_object_or_404(mod, pk=pk)
add2plate= Add2Plate.objects.filter(Add2Plateid=pk)
return render(request, 'tablet/show_plates.html', {'newplate': newplate,'add2plate': add2plate})
Thank you
The problem is two of your urls have the same pattern 'extendplates/<pk>/'. Django uses the first pattern that matches a url. I suppose that one of these view views.show_plates is meant to display the form and the other views.publish_plates is meant to accept the posted form data.
This means that simply both of these views should simply be a single view (to differentiate if the form is submitted we will simply check the requests method):
from django.shortcuts import redirect, render
def show_plates(request, plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save()
return redirect('tablet:extendplates', plate_id)
context = {'newplate': newplate}
return render(request, 'your_template_name.html', context)
Now your url patterns can simply be (Note: Also captured arguments are passed as keyword arguments to the view so they should be consistent for your view and pattern):
urlpatterns = [
...
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<uuid:plate_id>/', views.show_plates, name='showplates'),
...
]
In your form simply forego the action attribute as it is on the same page:
<form method="POST">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button>
</form>
You should avoid changing state on a get request like your view does currently.
Handle the POST request and change the data if the request is valid (ensuring CSRF protection).
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save(update_fields=['plate_complete']) # a more efficient save
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete=True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
You could also put a hidden input in the form, or make a form in Django to hold the hidden input, which stores the plate_id value and that way you can have a generic URL which will fetch that ID from the POST data.
Now the real problem you've got here, is that you've got 2 URLs which are the same, but with 2 different views.
I'd suggest you change that so that URLs are unique;
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('publish-plates/<pk>/', views.publish_plates, name='publishplates'),
Hellow. I have a document database and flask app that gives me web-based opportunity to see the db's docs, add them and delete. Every doc has only it's number and name.
Usually I add the documents one by one, cause i have the WTForm -
class addDocForm(FlaskForm):
doc_name = StringField('Название документа', validators=[DataRequired()])
doc_number = StringField('Исходящий номер', validators=[DataRequired()])
the .html code -
<form action="" method="post" >
{{ form.hidden_tag() }}
<div class="row">
<label>{{ form.doc_name.label }}</label>
{{ form.doc_name(size=32) }}
</div>
<div class="row">
<label>{{ form.doc_number.label }}</label>
{{ form.doc_number(size=32) }}
</div>
<div class="row">
<button type="submit">Добавить</button>
</div>
</form>
and some /route logic -
#app.route('/add_doc', methods=['GET', 'POST'])
#login_required
def add_doc():
form = addDocForm()
if form.validate_on_submit():
doc = Doc(doc_name=form.doc_name)
if Doc.query.filter_by(doc_name=form.doc_name.data).first() == None:
db.session.add(doc)
db.session.commit()
So I add each document one by one filling this form and submitting it again and again. Now i've been tired. I want to save my energy by reducing number of clicking on submit button. Of course it's a joke, but the question is really about thing like this:
how can i add several copies of this 'addDocForm' on one page, fill the fields of these copies and click submit only once?
Is there any clever way to do that? I want to add for example 5-7 docs at once without the necessity to add them one by one. Let's suppose i've load the page with my form (one form) fill the fields, and than clicked '+' button and there appear another form.. fill the fields-> '+' button .. again. After all click the 'submit' button and all the data from filled fields by turns go to the data base. Is it real? any ideas? p.s. i have an idea on how to make it using clear sql + html + js, without flask-wtforms, sqalchemy and so on.. but i guess this is wrong way cause half of my app is already written using them. ) so many text, don't sure if anyone reach this point.. but still - help me, pls (((((
You could construct a MegaForm using field enclosures.
For example (untested):
from wtforms import StringField, FormField, FieldList
class AddDocForm(FlaskForm):
doc_name = StringField('Название документа', validators=[DataRequired()])
doc_number = StringField('Исходящий номер', validators=[DataRequired()])
class MegaForm(FlaskForm):
documents = FieldList(FormField(AddDocForm), min_entries=7, max_entries=7)
#app.route('/add_doc', methods=['GET', 'POST'])
#login_required
def add_doc():
form = AddDocForm()
if form.validate_on_submit():
for idx, data in enumerate(form.documents.data):
doc = Doc(doc_name=data["doc_name"])
if Doc.query.filter_by(doc_name=data["doc_name"]).first() == None:
db.session.add(doc)
db.session.commit()
I've created a form which by submit uploads an item to the database. The problem is that if I press f5 it'll submit the form again, because of the URL is now different.
I have these two url patterns
urlpatterns = [
url(r'(?i)^CMS/$', views.CMS, name='CMS'),
url(r'^createItem/$', views.createItem, name='createItem')
]
and my view looks like this
def CMS(request):
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
def createItem(request):
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
pass
form = itemCreateForm()
context = {
'form' : form,
'message' : 'ItemCreated!'
}
return render(request, 'CMS.html', context)
the CMS.html
{% if message %}
{{ message }}
{% endif %}
<div class='newItemFields'>
<form action="{% url 'kar:createItem' %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
my form
class itemCreateForm(ModelForm):
class Meta:
model = item
fields = ['name', 'type', 'price']
I start at homepage/CMS/ and fill in the form and press submit, and view function createItem runs and creates and saves the object in the database. And sends the user to homepage/CMS/createItem. And now everytime the user press f5 the createItem function will run again and insert another object into the database with the same values as the previous one, even though the input fields are empty (can't wrap my head around that).
I also twice write form = itemCreateForm() which I believe is dubious?
What I'd like to do is after createItem is run, it should send the user back to homepage/CMS/ and not homepage/CMS/createItem. Would that be the proper way to do it? Or is there a smart way of doing this.
At the end of your createItem function, you are rendering HTML of the page rather than redirecting. Instead, you need to do
return HttpResponseRedirect(reverse('kar:index'))
You will need to import HttpResponseRedirect and reverse which is used to resolve the URL through its name.
Check this out: https://docs.djangoproject.com/en/1.10/topics/forms/#the-view
What I'd like to do is after createItem is run, it should send the
user back to homepage/CMS/ and not homepage/CMS/createItem. Would that
be the proper way to do it? Or is there a smart way of doing this.
That would indeed be the proper and smart way to do it. Have one view handle both GET and POST and then redirect after successful form submission. This ensures that the user can't resubmit the form merely by refreshing. And you address your concern about repeating your code.
urlpatterns = [
url(r'(?i)^$', views.index, name='index'),
url(r'^createItem/$', views.createItem, name='createItem')
]
Then combine your views
def createItem(request):
if request.method == 'POST':
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
return HttpResponseRedirect('/homepage/CMS/')
else :
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
Note that the code is now shorter, it gives proper feedback to the user when the form is not valid. And you can't refresh to submit the for twice. We need a small change to the template
<div class='newItemFields'>
<form action=method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
The message display part isn't needed anymore
I have a form on my website, that creates an entry in database. So every time when I refresh a page I got this message first:
The page that you're looking for used information that you entered.
Returning to that page might cause any action you took to be repeated.
Do you want to continue?
Obviously I don't want have the same information more than once in my database.
just in case: this is my code (I know there is a lot of crap that needs to be deleted):
#views.py
#login_required
def subject(request,username, subject_name):
subject_id = Subjects.objects.filter(user = request.user).get(name=subject_name)
#Upload form
if request.method == "POST":
if "upload-b" in request.POST:
form = ContentForm(request.POST, request.FILES, instance=subject_id)
if form.is_valid(): # need to add some clean functions
up_f = FileDescription.objects.get_or_create(subject=subject_id,
subject_name=subject_name,
file_type=request.POST['file_type'],
file_uploaded_by = username,
file_name=request.POST['file_name'],
file_description=request.POST['file_description'],
image = request.FILES['image'],
)
form = ContentForm()
#Show uploaded files with respect to clicked session (Homework, Class , Random ... )
homework_files = Homework.homework.filter(subject_name__exact=subject_name,
file_uploaded_by__exact=username)
class_files = ClassPapers.classpapers.filter(subject_name__exact=subject_name)
random_files = RandomPapers.randompapers.filter(subject_name__exact=subject_name,
file_uploaded_by__exact=username)
return render_to_response('subject_content.html', {'form':form,
'subject_name': subject_name,
'class_files': class_files,
'homework_files': homework_files,
'class_files': class_files,
'random_files': random_files,
},
context_instance=RequestContext(request))
#forms.py:
class ContentForm(forms.ModelForm):
file_name =forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':20}))
file_description = forms.CharField(widget=forms.Textarea(attrs={'rows':4, 'cols':25}))
class Meta:
model = FileDescription
exclude = ('subject', 'subject_name', 'file_uploaded_by')
#template
<div id="sbj-creation-frm">
<h3>Upload File</h3>
<form action="." method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" name="upload-b" class="btn-create" />
</form>
</div>
This message is from the browser; and it will display anytime you try to refresh a page that was displayed as the result of a POST request.
It has no bearing on your code, the browser will display the same message on all websites where you try to refresh the page (hit F5 for example) which was displayed as a result of a previous POST request.
To prevent this from happening, make sure all POST requests redirect to a different view upon completion; and not render templates themselves.
redirect to same page working for me :
header("Location: #");
Just redirect your page to current page after inserting
, it will clear all the values and avoid adding the Duplicate records !
example:
protected void btnAdd_Click(object sender, EventArgs e)
{
//your code
Response.Redirect("Currentpage.aspx",true);
//or
Response.Redirect(Request.Url.AbsoluteUri);
}