Django: multiple forms how to save this not hidden - django

I am struggling with validating a form based on damage_type choice field.
I show only one DamageTypeForm in the template (other two are hide by js .hide() function).
Each DamageTypeForm has got some required=True fields, therefore I cannot save the selected form this way:
def createDamage(request):
damage_specify_form = DamageSpecify(request.POST or None)
damage_type_form1 = DamageTypeForm1(request.POST or None)
damage_type_form2 = DamageTypeForm2(request.POST or None)
damage_type_form3 = DamageTypeForm3(request.POST or None)
if request.method == 'POST':
damage_type = request.POST.get('damage_type ')
if damage_type == 'DamageType1':
if damage_type_form1.is_valid():
damage_type_form1.save()
return reverse('damage:type1')
elif damage_type == 'DamageType2':
if damage_type_form2.is_valid():
damage_type_form2.save()
return reverse('damage:type2')
elif damage_type == 'DamageType3':
if damage_type_form3.is_valid():
damage_type_form3.save()
return reverse('damage:type3')
else:
damage_type_form1 = DamageTypeForm1()
damage_type_form2 = DamageTypeForm2()
damage_type_form3 = DamageTypeForm3()
context = {
'damage_specify_form': damage_specify_form,
'damage_type_form1': damage_type_form1,
'damage_type_form2': damage_type_form2,
'damage_type_form3': damage_type_form3,
}
return render(request, 'create_damage.html', context)
How can I get damage_type in View before submitting the form in the template, to save only desired form??

Here are two approaches you could take based on the answer to your previous question. Where it was suggested that you have:
<form>
{% csrf_token %}
{{damage_type_form.as_p}}
<div id="type1">
{{damage_form_1.as_p}}
</div>
<div id="type2">
{{damage_form_2.as_p}}
</div>
<div id="type3">
{{damage_form_3.as_p}}
</div>
<input type="submit" ...>
</form>
I'm not so sure why it was suggested to you that way. But the issue you highlighted is that the other forms (the hidden ones) do have required fields even though their respective forms are hidden.
So one way is to use the novalidate attribute on the form tag:
<form novalidate>
...
</form>
This novalidate is a form-level attribute used to turn off validation for a form, despite the attributes of the inputs it contains (i.e. will override inputs with the required attribute, or that would otherwise fail validation).
NOTE: Do not use novalidate if you simply want to internationalize or otherwise change the content of the default error messages. This can be done with JavaScript.
I personally don't think using the novalidate attribute is a good practice even though it might come in handy for some scenarios.
On the other hand, (my recommendation), you could use JavaScript or Jquery to set/remove the fields required attribute if the respective form is visible or not. For example:
# But let's say you do have separate forms...
<form id="form-1">
...
</form>
<form id="form-2" style="display:none">
...
</form>
# javascript
<script type="text/javascript">
# using jquery here...
$(document).ready(function(){
# Checking for a form's visibility
if($("#form-2").css('display') === 'none'){
# form-2: display is none. Therefore set its field attributes required to false
$('#form-2 input[type="text"]').removeAttr('required');
# or
# $('#form-2 #field_id').removeAttr('required');
}
else{
# form-2: display is not none. Therefore set its field attributes required to true
$('#form-2 input[type="text"]').attr('required','true');
# or
# $('#form-2 #field_id').attr('required','true');
}
});
</script>
Please note that the above JavaScript can be a bit cleaner, even adding some/an event listener to pick up when a form's visibility has changed: to set/remove the required attributes on a specific form.
That's just a basic concept of an approach I'd use.

Related

Django Search functionality: form returns None

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)

Django update boolean field with a form

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'),

view that outputs user input on a different url

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})

Add form returns None as record id, causing URL to error

I'm using the code found here (SO.com) to use the same template to both add and edit a record, but when I add a new record and click Submit, I get a 404 on the URL http://192.168.1.3:5678/app/student/edit/None/, and I'm not exactly sure why.
Here is the relevant portion of my urls.py:
url(r'^app/lesson/new/$', 'edit_lesson', {}, 'lesson_new'),
url(r'^app/lesson/edit/(?P<id>\d+)/$', 'edit_lesson', {}, 'lesson_edit'),
Here is the relevant portion of my views.py:
def edit_lesson(request, id=None, template_name='lesson_edit_template.html'):
if id:
t = "Edit"
lesson = get_object_or_404(Lesson, pk=id)
stu = get_object_or_404(Student, pk=sid)
if stu.teacher != request.user:
raise HttpResponseForbidden()
else:
t = "Add"
lesson = Lesson()
if request.POST:
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
# If the save was successful, redirect to another page
return view_lessons(request)
else:
form = LessonForm(instance=lesson)
return render_to_response(template_name, {
'form': form,
't': t,
'lesson': lesson,
}, context_instance=RequestContext(request))
And finally, here is my template:
<h1>{{ t }} Lesson</h1>
<form action="/app/lesson/edit/{{ lesson.id }}/" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I'm certain that I'm missing something really easy, but I can't seem to put my finger on it. I'm using Django 1.3.1 if that makes any difference.
Thanks,
MC
There's no need to specify any URL in the form's action attribute. Just do
<form action="" method="post">
and it will POST back to the URL that you originally used to access it, which is what you want.
In add case {{ lesson.id }} is None, because lesson is unsaved Lesson() instance, without pk, so your form is being fired to nonexistent URL.
I recommend separating create and edit views and processing them in different ways (or even inherit generic views - with new class-based generic views it's easy and pleasant).
Also, use {% url %} template tag everywhere instead of hard-coded urls.

Django/jQuery Cascading Select Boxes?

I want to build a Country/State selector. First you choose a country, and the States for that country are displayed in the 2nd select box. Doing that in PHP and jQuery is fairly easy, but I find Django forms to be a bit restrictive in that sense.
I could set the State field to be empty on page load, and then populate it with some jQuery, but then if there are form errors it won't be able to "remember" what State you had selected. I'm also pretty sure that it will throw a validation error because your choice wasn't one of the ones listed in the form on the Python side of things.
So how do I get around these problems?
Here is my solution. It uses the undocumented Form method _raw_value() to peek into the data of the request. This works for forms, which have a prefix, too.
class CascadeForm(forms.Form):
parent=forms.ModelChoiceField(Parent.objects.all())
child=forms.ModelChoiceField(Child.objects.none())
def __init__(self, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
parents=Parent.objects.all()
if len(parents)==1:
self.fields['parent'].initial=parents[0].pk
parent_id=self.fields['parent'].initial or self.initial.get('parent') \
or self._raw_value('parent')
if parent_id:
# parent is known. Now I can display the matching children.
children=Child.objects.filter(parent__id=parent_id)
self.fields['children'].queryset=children
if len(children)==1:
self.fields['children'].initial=children[0].pk
jquery Code:
function json_to_select(url, select_selector) {
/*
Fill a select input field with data from a getJSON call
Inspired by: http://stackoverflow.com/questions/1388302/create-option-on-the-fly-with-jquery
*/
$.getJSON(url, function(data) {
var opt=$(select_selector);
var old_val=opt.val();
opt.html('');
$.each(data, function () {
opt.append($('<option/>').val(this.id).text(this.value));
});
opt.val(old_val);
opt.change();
})
}
$(function(){
$('#id_parent').change(function(){
json_to_select('PATH_TO/parent-to-children/?parent=' + $(this).val(), '#id_child');
})
});
Callback Code, which returns JSON:
def parent_to_children(request):
parent=request.GET.get('parent')
ret=[]
if parent:
for children in Child.objects.filter(parent__id=parent):
ret.append(dict(id=child.id, value=unicode(child)))
if len(ret)!=1:
ret.insert(0, dict(id='', value='---'))
return django.http.HttpResponse(simplejson.dumps(ret),
content_type='application/json')
You could set a hidden field to have the real "state" value, then use jQuery to create the <select> list and, on .select(), copy its value to the hidden field. Then, on page load, your jQuery code can fetch the hidden field's value and use it to select the right item in the <select> element after it's populated.
The key concept here is that the State popup menu is a fiction created entirely in jQuery and not part of the Django form. This gives you full control over it, while letting all the other fields work normally.
EDIT: There's another way to do it, but it doesn't use Django's form classes.
In the view:
context = {'state': None, 'countries': Country.objects.all().order_by('name')}
if 'country' in request.POST:
context['country'] = request.POST['country']
context['states'] = State.objects.filter(
country=context['country']).order_by('name')
if 'state' in request.POST:
context['state'] = request.POST['state']
else:
context['states'] = []
context['country'] = None
# ...Set the rest of the Context here...
return render_to_response("addressform.html", context)
Then in the template:
<select name="country" id="select_country">
{% for c in countries %}
<option value="{{ c.val }}"{% ifequal c.val country %} selected="selected"{% endifequal %}>{{ c.name }}</option>
{% endfor %}
</select>
<select name="state" id="select_state">
{% for s in states %}
<option value="{{ s.val }}"{% ifequal s.val state %} selected="selected"{% endifequal %}>{{ s.name }}</option>
{% endfor %}
</select>
You'll also need the usual JavaScript for reloading the states selector when the country is changed.
I haven't tested this, so there are probably a couple holes in it, but it should get the idea across.
So your choices are:
Use a hidden field in the Django form for the real value and have the select menus created client-side via AJAX, or
Ditch Django's Form stuff and initialize the menus yourself.
Create a custom Django form widget, which I haven't done and thus will not comment on. I have no idea if this is doable, but it looks like you'll need a couple Selects in a MultiWidget, the latter being undocumented in the regular docs, so you'll have to read the source.
Based on Mike's suggestion:
// the jQuery
$(function () {
var $country = $('.country');
var $provInput = $('.province');
var $provSelect = $('<select/>').insertBefore($provInput).change(function() {
$provInput.val($provSelect.val());
});
$country.change(function() {
$provSelect.empty().addClass('loading');
$.getJSON('/get-provinces.json', {'country':$(this).val()}, function(provinces) {
$provSelect.removeClass('loading');
for(i in provinces) {
$provSelect.append('<option value="'+provinces[i][0]+'">'+provinces[i][1]+'</option>');
}
$provSelect.val($provInput.val()).trigger('change');
});
}).trigger('change');
});
# the form
country = CharField(initial='CA', widget=Select(choices=COUNTRIES, attrs={'class':'country'}))
province = CharField(initial='BC', widget=HiddenInput(attrs={'class':'province'}))
# the view
def get_provinces(request):
from django.utils import simplejson
data = {
'CA': CA_PROVINCES,
'US': US_STATES
}.get(request.GET.get('country', None), None)
return HttpResponse(simplejson.dumps(data), mimetype='application/json')