The function on the page:
user enter some information on the form
without refreshing, the right side can show some query result(sales) based on user entry
Before adding the ajax function, the page can display the form and the table. But after using the ajax, the page becomes empty.
Can anyone help check what is the reason? Thanks in advance.
url
url(r'^result_list/$',ResultView.as_view(),name='result'),
models.py
class Input(models.Model):
company=models.CharField(max_length=100)
region=models.CharField(max_length=100)
class Result(models.Model):
sales=models.IntegerField(blank=False,null=False)
views.py
from django.views.generic.list import ListView
from django.core import serializers
class ResultView(ListView):
context_object_name = 'result_list'
template_name = 'result_list.html'
def get_queryset(self):
return Result.objects.all()
def post(self, request, *args, **kwargs):
form = InputForm(request.POST)
if form.is_valid():
if self.request.is_ajax():
company = form.cleaned_data['company']
region = form.cleaned_data['region']
queryset=Result.objects.filter(region=region).aggregate(Sum('sales'))
return HttpResponse(json.dumps(queryset))
else:
return HttpResponse(form.errors)
'''def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context["sales"] = self.get_queryset().aggregate(Sum('sales'))'''
html
<style>...CSS part
</style>
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#InputForm").submit(function() { // catch the form's submit event
var region= $("#id_region").val();
var company= $("#id_company").val();
$.ajax({
data: $(this).serialize(), // get the form data
type: $(this).attr('post'),
dataType: 'json',
url: "dupont_list/",
success: function(data) {
var html = "<table>"
html += "<td>"+data['sales__sum']+"</td>"
html += "</table>"
$("#result").html(html);
html += "</table>"
$("#result").html(html);
}
return false;
});
})
</script>
<form id="InputForm" method="post" action=""> #here is the data entry form
{% csrf_token %}
<!--enter the company name-->
<div class="field">
{{ form.company.errors }}
<label id="id_company" name="company" for="{{ form.company.id_for_label }}">Company:</label>
{{ form.company }}
</div>
<!--select region-->
<div class="field" >
<label> Select the Region:
{{ form.region }}
{% for region in form.region.choices %}
<option value="region" name= "region" id="id_region">{{region}} </option>
{% endfor %}
</label>
</div>
<!--submit-->
<p><input type="button" value="Submit" /></p></div>
</form>
</div>
<div id="result" class="result"> <!--Showing the filtered result in database-->
<table>
<tr><b>Sales</b></tr>
<td> {{sales.sales__sum}}</td>
<tr><b>Employee</b></tr>
<td> {{employee.employee__sum}}</td>
</table>
I would go with a FormView:
class ResultView(FormView):
context_object_name = 'result_list'
template_name = 'result_list.html'
form_class = InputForm
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context["results"] = Result.objects.all()
context["sales"] = context.results.aggregate(Sum('sales'))
return context
def form_valid(self,form):
company = form.cleaned_data['company']
region = form.cleaned_data['region']
queryset = Result.objects.filter(region=region).aggregate(Sum('sales'))
return HttpResponse(json.dumps(queryset))
This way you're bypassing the get_context_data and get_queryset methods if the form is valid and return a custom content response.
Related
I try to upload two forms with one submit button.
A user can select a pdf file and a excel file. And then uploading both files. And then the contents of both are returned.
So I try to upload both files with one submit button.
But the two selected file options are not visible for uploading the files.
So I have the template like this:
{% extends 'base.html' %} {% load static %} {% block content %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create a Profile</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="{% static 'main/css/custom-style.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'main/css/bootstrap.css' %}" />
</head>
<body>
<div class="container center">
<span class="form-inline" role="form">
<div class="inline-div">
<form class="form-inline" action="/controlepunt140" method="POST" enctype="multipart/form-data">
<div class="d-grid gap-3">
<div class="form-group">
{% csrf_token %}
{{ form.0.as_p }}
<button type="submit" name="form_pdf" class="btn btn-warning">Upload!</button>
</div>
<div class="form-outline">
<div class="form-group">
<textarea class="inline-txtarea form-control" id="content" cols="70" rows="25">
{{content}}</textarea>
</div>
</div>
</div>
<div class="d-grid gap-3">
<div class="form-group">
{{ form.1.as_p }}
</div>
<div class="form-outline">
<div class="form-group">
<textarea class="inline-txtarea form-control" id="content" cols="70" rows="25">
{{conten_excel}}</textarea>
</div>
</div>
</div>
</form>
</div>
</span>
</div>
</body>
</html>
{% endblock content %}
and the views.py:
class ReadingFile(View):
def get(self, *args, **kwargs):
return render(self.request, "main/controle_punt140.html", {
"form1": UploadFileForm(),
"form2": ExcelForm()
})
def post(self, *args, **kwargs):
filter_text = FilterText()
types_of_encoding = ["utf8", "cp1252"]
form1 = UploadFileForm(
self.request.POST, self.request.FILES, prefix="form1")
form2 = ExcelForm(self.request.FILES,
self.request.FILES, prefix="form2")
content = ''
content_excel = ''
if form1.is_valid() and form2.is_valid() and self.request.POST:
uploadfile = UploadFile(image=self.request.FILES["upload_file"])
excel_file = self.request.FILES["upload_file"]
uploadfile.save()
for encoding_type in types_of_encoding:
with open(os.path.join(settings.MEDIA_ROOT, f"{uploadfile.image}"), 'r', encoding=encoding_type) as f:
if uploadfile.image.path.endswith('.pdf'):
content = filter_text.show_extracted_data_from_file(
uploadfile.image.path)
else:
content = f.read()
if uploadfile.image.path.endswith('xlsx'):
wb = openpyxl.load_workbook(excel_file)
worksheet = wb['Sheet1']
print(worksheet)
excel_data = list()
for row in worksheet.iter_rows():
row_data = list()
for cell in row:
row_data.append(str(cell.value))
excel_data.append(row_data)
print(excel_data)
content_excel = excel_data
else:
content_excel = f.read()
return render(self.request, "main/controle_punt140.html", {
'form1': ExcelForm(),
'form2': UploadFileForm(),
"content": [content, content_excel]
})
# I've adjusted the indent here to what I think it should be.
return render(self.request, "main/controle_punt140.html", {
"form1": form1,
"form2": form2,
})
and forms.py:
class UploadFileForm(forms.Form):
upload_file = forms.FileField(required=False)
class ExcelForm(forms.Form):
upload_file = forms.FileField(required=False)
urls.py:
urlpatterns = [
path('', views.starting_page, name='starting_page'),
path('controlepunt140', views.ReadingFile.as_view(), name='controlepunt140'),
]
The variable name used in the template is the key of the dictionary, not the value. The value is what is inserted into the template when django renders the page.
You have {{form1.as__p}} in your template, but you send "form": [form1, form2] as your context, so the variable in the template should be {{ form.0.as_p }} and {{ form.1.as_p }}. I haven't tested this, but if it doesn't work, you could just send the two forms separately like:
from django.shortcuts import redirect
class ReadingFile(View):
def get(self, *args, **kwargs):
return render(self.request, "main/controle_punt140.html", {
"form1": UploadFileForm(),
"form2": ExcelForm()
})
def post(self, *args, **kwargs):
filter_text = FilterText()
types_of_encoding = ["utf8", "cp1252"]
form1 = UploadFileForm(self.request.POST, self.request.FILES, prefix="form1")
form2 = ExcelForm(self.request.FILES, self.request.FILES, prefix="form2")
content = ''
content_excel = ''
if form1.is_valid() and form2.is_valid() and self.request.POST:
uploadfile = UploadFile(image=self.request.FILES["upload_file"])
excel_file = self.request.FILES["upload_file"]
uploadfile.save()
for encoding_type in types_of_encoding:
with open(os.path.join(settings.MEDIA_ROOT, f"{uploadfile.image}"), 'r', encoding=encoding_type) as f:
if uploadfile.image.path.endswith('.pdf'):
content = filter_text.show_extracted_data_from_file(
uploadfile.image.path)
else:
content = f.read()
if uploadfile.image.path.endswith('xlsx'):
#Uploading excel form:
#this is just logic.
pass
else:
content_excel = f.read()
# You probably should do a redirect after the form is
# submitted, rather than render the page.
return redirect('main:controlepunt140')
# return render(self.request, "main/controle_punt140.html", {
'form1': ExcelForm(),
'form2': UploadFileForm(),
"content": [content, content_excel]
})
# I've adjusted the indent here to what I think it should be.
return render(self.request, "main/controle_punt140.html", {
"form1": form1,
"form2": form2,
})
You probable should also change to a redirect after the form is submitted and saved successfully. Check out Post/Redirect/Get and/or rendering content after a succesful post request.
Edit
Changed template to use {{ form.0.as_p }} as indicated by #nigel239
You can redirect to the same page where the form was submitted, so if the user hits the refresh button on their browser for some reason, you will not get an alert box asking the user to resend the form.
So I am trying to basically have a check box on an image page which lets me set a boolean field true/false if I want this image to be deemed the "profile image." The problem is that the form field options are not showing up in the template. Any suggestions?
single_image.html
<form method='POST' action="{% url 'set_profile_image' plant_pk=plnt.pk image_pk=img.pk %}">
{% csrf_token %}
<hr>
{{ form.as_p }}
<hr>
<input type="submit" value="Submit">
forms.py
class SetProfileImageForm(forms.ModelForm):
"""A form to set profile image."""
image_main = forms.BooleanField(required=True)
class Meta:
model = Image
fields = ["image_main","image_url",]
views.py
class SingleImageView(DetailView):
""" A view to see a single image."""
template_name = "project/single_image.html"
queryset = Image.objects.all()
def get_context_data(self, **kwargs):
"""Return a dictionary with context data for this template to use."""
# get the default context data:
# this will include the Profile record for this page view
context = super(SingleImageView, self).get_context_data(**kwargs)
img = Image.objects.get(pk=self.kwargs['image_pk'])
plnt = Image.objects.get(pk=self.kwargs['image_pk']).plant
context['img'] = img
context['plnt'] = plnt
form = SetProfileImageForm()
context['set_profile_image'] = form
# return the context dictionary
return context
def get_object(self):
"""Returns the Note Object that should be deleted."""
# read the URL data values into variables
plant_pk = self.kwargs['plant_pk']
image_pk = self.kwargs['image_pk']
# find the StatusMessage object, and return it
return Image.objects.get(pk=image_pk)
def set_profile_image(request, plant_pk, image_pk):
"""A custom view function to set profile image."""
# find the plant for whom we are setting the image
plant = Plant.objects.get(pk=plant_pk)
if request.method == 'POST':
if "cancel" in request.POST:
return redirect('display_plant', pk=plant.pk)
form = SetProfileImageForm(request.POST or None, request.FILES or None)
if form.is_valid():
plant.set_profile_image(image_pk)
return redirect('display_plant', pk=plant.pk)
else:
print("Error: the form was not valid.")
else:
return reverse('gallery', kwargs={'pk':plant.pk})
You are sending the form instance as a set_profile_imgkey in the context.Change it in your HTML or just rename the context key.
...
context['form'] = form
<form method='POST' action="{% url 'set_profile_image' plant_pk=plnt.pk image_pk=img.pk %}">
{% csrf_token %}
<hr>
{{ form.as_p }}
<hr>
<input type="submit" value="Submit">
OR
...
context['set_profile_image'] = form
<form method='POST' action="{% url 'set_profile_image' plant_pk=plnt.pk image_pk=img.pk %}">
{% csrf_token %}
<hr>
{{ set_profile_image.as_p }}
<hr>
<input type="submit" value="Submit">
I am working on a project. Need help in template focus out events on Django.
model.py
class Route(models.Model):
route_no = models.SmallIntegerField(default=0)
xname = models.CharField(max_length=40)
class Booth(models.Model):
booth_no = models.SmallIntegerField(default=0)
route_no = models.ForeignKey(Route,
on_delete=models.CASCADE,
db_column='route_no')
View.py
class BoothCreateListView(CreateView, ListView):
model = models.Booth
form_class = booth.BoothForm
template_name = 'booth/booth_create_list.html'
context_object_name = 'booth_list'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
js_data = list(models.Route.objects.all().values_list('route_no', 'xname'))
context['js_data'] = json.dumps(list(js_data), cls=DjangoJSONEncoder)
return context
template/booth_create_list.html
<div class="col-sm-12 col-md-5">
<form method="post">
{% csrf_token %}
<table class="table table-borderless">
{{ form.as_table }}
</table>
<input class="btn btn-success" type="submit" value="Save">
</form>
{{ form.route.value }}
</div>
<div id='route_no'></div>
<script>
var route_no = document.getElementById('route_no')
var myfunction = function (){
console.log('changing');
route.innerHTML = '{{ check_route_no form.route.value }}'
}
</script>
form/booth.py
class BoothForm(ModelForm):
class Meta:
fields = [
'route_no', 'booth_no',
]
model = models.Booth
widgets = {
'route_no': forms.TextInput(),
}
labels = {
'route_no': 'Route No.',
'booth_no': 'Booth No',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['route_no'].widget.attrs.update(
{'onfocusout':'myfunction()'}
)
for name, field in self.fields.items():
field.widget.attrs.update(
{'class': 'form-control form-control-user'}
)
templatetags/booth_template_tags.py
from booth import models
register = template.Library()
#register.simple_tag
def check_route_no(route_no):
print(route_no)
route = models.Route.objects.filter(route_no=route_no)
if route.count() == 1:
return route.xname
else:
return "not present"
I want to check the route as user types it in the form for the booth. If route_no is present then show the route xname else not present.
My value passed to the template tag is always none. I am not able to pass the textbox value to the template tag to search in DB.
Please help to check runtime if the route no is there in DB as the user type.
I have a form which creates a new category. Previously, the form was in a different template which worked fine but since it's only a simple form I have decided to render it in modal form instead of redirecting to a different page.
The user can add a new category, however the success message and the page rendering after the form submit is not shown. It only shows up if you refresh the page. The response message is 302.
I've done similar method with other forms which worked perfectly fine.
forms.py
class CategoryModelForm(forms.ModelForm):
def clean_name(self):
print(self.cleaned_data['name'])
name = self.cleaned_data['name']
try:
Category.objects.get(name__iexact=name)
except ObjectDoesNotExist:
return name
raise forms.ValidationError('Category Name already exists.')
class Meta:
model = Category
fields = ['name']
views.py
#method_decorator(login_required, name='dispatch')
class CategoryView(TemplateView):
template_name = 'content/category_list.html'
def get_context_data(self, **kwargs):
context = super(CategoryView, self).get_context_data(**kwargs)
categories = Category.objects.all()
user = self.request.user
category_list = []
for category in categories:
article_count = category.article_count(user)
include = category.show or user.usertype_is_staff() or user.is_superuser
requested_by = category.requested_by if category.requested_by else ''
cat = {
'reference': category.pk,
'name': category.name,
'show': category.show,
'article_count': article_count,
'has_articles': article_count > 0,
'requested_by': requested_by,
'requested_by_name': requested_by.profile.full_name if requested_by and requested_by.profile.full_name
else '-'
}
include and category_list.append(cat)
context['categories'] = category_list
context['form'] = CategoryModelForm(self.request.POST or None)
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
if context['form'].is_valid():
context['form'].save()
messages.success(request, 'Successfully created new category.')
return redirect('content:category')
return super(CategoryView, self).render_to_response(context)
category_list.html
<div id="newCategory" data-id="new-account" class="modal fade bd-example-modal-lg"
tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="syn-breadcrumb">
<div class="syndicator-form-container">
<form class="syndicator-form" action="{% url 'content:category' %}"
method="post">
{% csrf_token %}
<div class="">
<h3 class="lighter-weight">
{% if user.usertype_is_supplier %}
Request New Category
{% else %}
Add New Category
{% endif %}
</h3>
</div>
<div class="form-fields">
<div class="non-field-errors">
{{ form.non_field_errors }}
</div>
<div id="{{ form.name.name }}" class="d-flex flex-column fields">
<div class="lighter-weight"><label for="id_name">Name</label></div>
<div>{{ form.name }}</div>
<div class="field-errors">{{ form.name.errors }}</div>
</div>
</div>
<div class="submit-button">
<button type="submit" class="btn btn-primary form-control">{% trans 'Submit' %}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
I want the page to be redirected to an updated list of the categories after the form submit with the success message. As well as show the error message if the category name already exists or if the fields re empty.
You are using self.request instead of request even though request is already passed in post method
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context['form'] = CategoryModelForm(request.POST or None) # use request.POST
I would rather suggest you to use a FormView or rather a generic view. (docs). You won't need to handle the form by yourself.
#method_decorator(login_required, name='dispatch')
class CategoryView(FormView):
template_name = 'content/category_list.html'
form = CategoryModelForm
success_url = reverse("content:category")
def form_valid(self, form):
self.obj = form.save(commit=True)
messages.success(self.request, 'Successfully created new category.')
return super(CategoryView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(CategoryView, self).get_context_data(**kwargs)
categories = Category.objects.all()
user = self.request.user
category_list = []
for category in categories:
article_count = category.article_count(user)
include = category.show or user.usertype_is_staff() or user.is_superuser
requested_by = category.requested_by if category.requested_by else ''
cat = {
'reference': category.pk,
'name': category.name,
'show': category.show,
'article_count': article_count,
'has_articles': article_count > 0,
'requested_by': requested_by,
'requested_by_name': requested_by.profile.full_name if requested_by and requested_by.profile.full_name
else '-'
}
include and category_list.append(cat)
context['categories'] = category_list
# form will be automatically added to context
Here is my form.
class POICreateForm(ModelForm):
def __init__(self, *args, **kwargs):
super(POICreateForm, self).__init__(*args, **kwargs)
self.fields['latitude'].widget = HiddenInput()
self.fields['longitude'].widget = HiddenInput()
self.fields['picture'].required = True
class Meta:
fields = ['name', 'vip', 'category', 'place', 'latitude', 'longitude', 'picture', 'website', 'description',
'phone',
'email']
model = PointOfInterest
def clean(self):
cleaned_data = super(POICreateForm, self).clean()
pic = cleaned_data.get('picture', None)
if not pic:
msg = 'You must Choose an image'
self.add_error('picture', msg)
return cleaned_data
I have given required=True for picture field and also I overridded the clean method. BUt both failed to show the validation error.
Here is my view
class POIFormCreateView(LoginRequiredMixin, CreateView):
login_url = '/login/'
model = PointOfInterest
form_class = POICreateForm
success_url = reverse_lazy('POIs')
def post(self, request, *args, **kwargs):
query_dict = request.POST.dict()
super(POIFormCreateView, self).post(request, *args, **kwargs)
filtered_dict = {i: j for i, j in query_dict.items() if i.endswith('day')}
out = PointOfInterestQuery.create_or_update_open_hours(filtered_dict)
if out:
return HttpResponseRedirect(reverse('POIs'))
return HttpResponse('Error :(')
template.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<script type="text/javascript">
</script>
<h1>New Point Of Interest</h1>
<form role="form" method="POST" action="/poi/add/" class="post-form form-horizontal"
enctype="multipart/form-data" name="poi_create" onsubmit="return validateForm()">{% csrf_token %}
<div class="container row">
<div class="col-lg-6">
<!-- customizing form -->
{{ form|crispy }}
<!-- End of customization -->
</div>
<div class="col-lg-6">
<div id="map"></div>
{% include 'open_hours.html' %}
</div>
</div>
<button type="submit" id="poi-submit" class="save btn btn-default btn-primary center-block">Save
</button>
</form>
<div class="modal-footer"><a href="/poi/">
<button type="button" class="btn btn-default btn-danger center-block">Cancel
</button>
</a></div>
{% endblock %}
You've overridden the post method and avoided using the form at all. All of this logic should be in the form_valid method instead; that allows the CreateView to do the normal job of form validation and re-displaying the page with errors if the form is invalid, while your processing happens if the form is valid.
Note also however that you should be getting your values to populate filtered_dict from the form's cleaned_data dict, not directly from request.POST.
class POIFormCreateView(LoginRequiredMixin, CreateView):
login_url = '/login/'
model = PointOfInterest
form_class = POICreateForm
success_url = reverse_lazy('POIs')
def form_valid(self,form):
super(POIFormCreateView, self).form_valid(form)
filtered_dict = {i: j for i, j in self.request.POST.items() if i.endswith('day')}
out = PointOfInterestQuery.create_or_update_open_hours(filtered_dict)
if out:
return HttpResponseRedirect(reverse('POIs'))
return HttpResponse('Error :(')