Flask app not displaying list as expected - flask

I have an app that scrapes a website using beautiful soup. It returns the data in a table for me. I implemented a search bar using wtforms and a sqlite3 query.
Web page sample
This returns the data I want, but not how I want it. As soon as I submit the search it loads a list on a page instead of on the index.
the results after search
This is my flask app (part of):
#app.route('/', methods=('GET', 'POST'))
def index():
products = get_products()
form = SearchForm()
if form.validate_on_submit():
item = '%' + form.searchFor.data + '%'
c.execute("SELECT * FROM supplementInfo WHERE (name) LIKE (?)", (item,))
search_results = c.fetchall()
return str(search_results)
return render_template('index.html', products=products, form=form)
And this is the html:
<div col-12 class="wrap">
<div class="search">
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.searchFor(class_="searchTerm") }}
<button type="submit" class="searchButton">
<i class="icon-search"></i>
</button>
</form>
</div>
</div>
<div class="infoTable">
<table>
{% for row in products %}
<tr>
<td>{{ row[0] }}</td>
<td> {{ row[1] }}</td>
<td> {{ row[2] }}</td>
<td> {{ row[3] }}</td>
</tr>
{% endfor %}
</table>
</div>
</center>
<div>
<table>
{% for item in search%}
<tr>
<td>{{ item.name}}</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

Ok, I figured it out. I had to change the app to read as such:
#app.route('/', methods=('GET', 'POST'))
def index():
products = get_products()
form = SearchForm()
if form.validate_on_submit():
item = '%' + form.searchFor.data + '%'
c.execute("SELECT * FROM supplementInfo WHERE (name) LIKE (?)", (item,))
search_results = c.fetchall()
return render_template('index.html', products=products, form=form, search_results=search_results)
return render_template('index.html', products=products, form=form)
I realized I was returning the list instead of returning the template and passing it the list.

Related

form doesn't submit productbacklogs to database

here is my project code .
I after posting data by form nothing happens.
#model.py
from django.db import models
from projectapp.models import Project
class Productbacklog(models.Model):
project=models.ForeignKey(Project,on_delete=models.CASCADE,default=None)
pbId=models.IntegerField(primary_key=True)
pbTitle=models.CharField(max_length=100)
pbPriority=models.IntegerField(blank=True, null=True)
class Meta:
unique_together=('project','pbId')
def __str__(self):
return self.pbTitle
#forms.py
from django import forms
from productbacklogapp.models import Productbacklog
from projectapp.models import Project
class ProductbacklogForm(forms.ModelForm):
class Meta:
model = Productbacklog
exclude=('pbId','project')
fields=['pbTitle']
#views.py
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})
I think that issue is on forms.py or views.py but I can't find it.
I'm so greatful if anyone can help me.
here is also my html code,when I submit something the method is post but I don't know why it doesn't go to data basse.
#productbacklogall.html
{%extends 'base.html'%}
{%block title%}
<title>backlog all</title>
{%endblock title%}
{%block content%}
</br>
{% if messages %}
{% for message in messages %}
<div class="alert alert-primary" role="alert">
{{ message }}
×
</div>
{% endfor %}
{% endif %}
<div class="container">
<form method="POST" class="row">
{% csrf_token %}
<label class="col-lg-4"></label>
<input type="text" class="form-control" name="Project" placeholder="project title?"/>
<input type="number" class="form-control" name="pbId" placeholder="backlog id?"/>
<input type="text" class="form-control" name="pbTitle" placeholder="pb title?"/>
<button type="submit" class="btn btn-primary col-lg-2">add project</button>
</form>
</div>
</br>
</br>
<table class="table table-bordered text-center">
<thead class="thead-dark">
<tr>
<th scope="col"> backlog-title</th>
<th scope="col">Edit</th>
<th scope="col">delivarable_task</th>
<th scope="col">related project</th>
</tr>
</thead>
<tbody>
{% load static %}
{% if pb_all %}
{% for obj in pb_all %}
<tr>
<td>{{ obj.pbTitle }}</td>
<td><a href='{% url "delete_productbacklog" obj.pbId %}'>delete</a></td>
<td> <a href=#> delivarabletask </a></td>
<td>{{ obj.project.pbTitle }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
{%endblock content%}
Your form data are not valid, to see them, add this code to your views :
else:
messages.error(request, "Error")
like this :
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
else:
messages.error(request, "Error")
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})

Django-tables2 send parameters to custom table template

I'm trying to use a custom table template to embed the django-filter fields on my table. So I copied the django-tables2 bootstrap.html template in a new file custom_table.html. Then I added it the following code in the thead section:
{% if filter %}
<tr>
{% for filter_field in filter.form.fields %}
<td>
{{ filter_field }}
</td>
{% endfor %}
<td>
<button class="login100-form-btn" type="submit">Filter</button>
</td>
</tr>
{% endif %}
So the problem is : how can I send the filter to the table template?
I resolved this issue. I have overridden the get_context_data function of my view:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
table.filter = self.filterset
context[self.get_context_table_name(table)] = table
return context
In this way, I can use the filter in my custom table template by the following code:
{% if table.filter %}
<tr>
<form action="" method="get" class="form-inline">
{% csrf_token %}
{% for field_form in table.filter.form %}
<th>
{{field_form}}
</th>
{% endfor %}
<th>
<button class="btn" type="submit">Filter</button>
</th>
</form>
</tr>
{% endif %}
You can send the query paramters to the server, and construct the table using filtered_queryset. For example:
#views.py
def your_view(request):
filter = ModelFilter(request.GET, queryset=Model.objects.all())
filtered_queryset = filter.qs
# pass filtered_queryset to your table
table = SimpleTable(filtered_queryset)
return render(request, 'table.html', {'table': table})

Unable to edit/delete existing formets

I am new here. I have been working on this issue for the past couple of days. Could someone help me, please?
Problem: Unable to edit/delete existing formsets
I have 1 form and 2 formsets in a view. I can create all 3, successfully.
Template rendering 1 form and 2 formsets
Also, I can edit/delete the form, but can't with the 2 formsets.
models.py
class Component(models.Model):
...
class ComponentImage(models.Model):
component = models.ForeignKey(Component, on_delete=models.CASCADE, related_name='componentimage')
image = models.ImageField(upload_to='component_pics')
caption = models.CharField(max_length=100, null=True, blank=True)
class ComponentWeblink(models.Model):
component = models.ForeignKey(Component, on_delete=models.CASCADE, related_name='componentweblink')
url = models.URLField()
caption = models.CharField(max_length=100, null=True, blank=True)
views.py
def component_create(request):
template = 'component/component_add.html'
if request.method == 'GET':
form = ComponentForm()
images = ImageFormset(queryset=ComponentImage.objects.none(), prefix='images')
links = WeblinkFormset(queryset=ComponentWeblink.objects.none(), prefix='links')
elif request.method == 'POST':
form = ComponentForm(request.POST)
images = ImageFormset(request.POST, request.FILES, prefix='images')
links = WeblinkFormset(request.POST, prefix='links')
if form.is_valid() and images.is_valid() and links.is_valid():
component = form.save(commit=False)
component.updated_by = request.user
component.save()
for form in images:
if form.has_changed():
componentimage = form.save(commit=False)
componentimage.component = component
componentimage.updated_by = component.updated_by
try:
componentimage.save()
except ValueError:
pass
for form in links:
if form.has_changed():
componentweblink = form.save(commit=False)
componentweblink.component = component
componentweblink.updated_by = component.updated_by
componentweblink.save()
messages.success(request, 'Component successfully created.')
return HttpResponseRedirect(reverse_lazy('component:component_detail', args=(component.pk,)))
return render(request, template, {
'form': form,
'images': images,
'links': links,
})
def component_update(request, pk):
obj = get_object_or_404(Component, id=pk)
template = 'component/component_update.html'
if request.method == 'GET':
form = ComponentForm(instance=obj)
images = ImageFormset(instance=obj, prefix='images')
links = WeblinkFormset(instance=obj, prefix='links')
elif request.method == 'POST':
form = ComponentForm(request.POST or None, instance=obj)
images = ImageFormset(request.POST or None, request.FILES or None, instance=obj, prefix='images')
links = WeblinkFormset(request.POST, instance=obj, prefix='links')
if form.is_valid() and images.is_valid() and links.is_valid():
component = form.save(commit=False)
component.updated_by = request.user
component.save()
for form in images:
if form.has_changed():
componentimage = form.save(commit=False)
componentimage.component = component
componentimage.updated_by = component.updated_by
try:
componentimage.save()
except ValueError:
pass
for form in links:
if form.has_changed():
componentweblink = form.save(commit=False)
componentweblink.component = component
componentweblink.updated_by = component.updated_by
componentweblink.save()
messages.warning(request, 'Component successfully updated.')
return HttpResponseRedirect(reverse_lazy('component:component_detail', args=(component.pk, ))) #trailing comma is required for single-item tuples to disambiguate defining a tuple or an expression surrounded by parentheses
return render(request, template, {
'form': form,
'images': images,
'links': links,
})
template.html (for update view)
{% block content %}
<script type="text/javascript" src="{% static 'js/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$('#images tbody tr').formset({
prefix: '{{ images.prefix }}',
formCssClass: 'dynamic-images'
});
$('#links tbody tr').formset({
prefix: '{{ links.prefix }}',
formCssClass: 'dynamic-links'
});
})
</script>
<div class="row justify-content-center">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend style="text-align:center" class="border-bottom mb-4">
Update Component
</legend>
<h5 style="text-align:center" >Data</h5>
{% include 'snippets/form.html' %}
<hr>
<h5 style="text-align:center" >Images</h5>
<table class="col col-md-12" id="images">
<tbody>
{{ images.management_form }}
{{ images.non_form_errors }}
{% for form in images.forms %}
<tr>
<td class="px-0 py-0 form-control{% if form.image.errors %} is-invalid{% else %} border-0{% endif %}">{{ form.image }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.image.non_field_errors }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.image.errors }}</td>
<td class="px-0 py-0 form-control{% if form.image.errors %} is-valid{% else %} border-0{% endif %}">{{ form.caption }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.caption.non_field_errors }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.caption.errors }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr>
<h5 style="text-align:center" >Weblinks</h5>
<table class="col col-md-12" id="links" border="0" cellpadding="0">
<tbody>
{{ links.management_form }}
{{ links.non_form_errors }}
{% for form in links.forms %}
<tr>
<td class="px-0 py-0 form-control{% if form.url.errors %} is-invalid{% else %} border-0{% endif %}">{{ form.url }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.url.errors }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.url.non_field_errors }}</td>
<td class="px-0 py-0 form-control{% if form.url.errors %} is-valid{% else %} border-0{% endif %}">{{ form.caption }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.caption.errors }}</td>
<td class="border-0 px-0 py-0 form-control" style="color:red">{{ form.caption.non_field_errors }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<button class="btn btn-primary" type="submit">
Update
</button>
<a class="btn btn-outline-secondary" href="{% if request.GET.next %}{{ request.GET.next }}{% else %}{% url 'component:components_list' %}{% endif %}">
Cancel
</a>
</fieldset>
</form>
</div>
{% endblock content %}
Please help. Thank you
Used VSCode debugger, as per a friend's suggestion. The problem was with not adding id (hidden_fields).
I added this in the update template:
{% for form in images.forms %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for form in links.forms %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
Also added the image.has_changed() to views.py to avoid accepting empty image fields.
for image in images:
if image.is_valid() and image.has_changed():
try:
image.save()
except ValueError:
pass

Django paginator not providing content on next page

I use Django paginator to break my search results to few pages
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
In my view I perform the search based on the output from the form
#login_required
def payment_range_list(request):
#import pdb; pdb.set_trace()
title = 'payment list'
#import pdb; pdb.set_trace()
if request.method == "POST":
form = PaymentRangeForm(request.POST)
#import pdb; pdb.set_trace()
if form.is_valid():
start_date = form.cleaned_data['start_date']
end_date = form.cleaned_data['end_date']
building = form.cleaned_data['building']
payment_list = LeasePaymentFilter(request.GET, queryset=LeasePayment.objects.filter(payment_date__range=[start_date, end_date],lease__unit__building = building ))
paginator = Paginator(payment_list, 25) # Show 25 contacts per page
page = request.GET.get('page')
try:
payment_page = paginator.page(page)
except PageNotAnInteger:
payment_page = paginator.page(1)
except EmptyPage:
payment_page = paginator.page(paginator.num_pages)
else:
payment_list = None
payment_page = None
start_date = None
end_date = None
building = None
else:
payment_list = None
payment_page = None
start_date = None
end_date = None
building = None
#form = PaymentRangeForm()
form = PaymentRangeForm(initial = {'building': 0 })
return render(request,'payment/payment_range.html', {'form':form, 'payment': payment_list,'page_payment':payment_page, 'start_date':start_date, 'end_date':end_date, 'building':building })
On the top of the page I have a form that I use for search and 2nd part is used to display the results.
{% extends "rent_base.html" %}
{% load crispy_forms_tags %}
{% block title %}
Lease List
{% endblock title %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1 hid">
<h4 class="display-4">List > Payments> by Date Range </h4><div style="float:right;"> CSV for current range payments</div>
</div>
</div>
<div class="row">
<div class="col-sm-10 col-sm-offset-1 hid">
<form method="POST" class="form" action="" method="get">
{% csrf_token %}
{{ form|crispy}}
<br>
<button type="submit" class="btn btn-primary btn-primary">Search</button>
</form>
<br>
<b>Start date:</b> {{ start_date }} <b>End date:</b>{{ end_date }}
<br>
<table class="table table-striped">
<tr>
<th># Payment</th>
<th>Payment type</th>
<th>Amount </th>
<th>Deposit</th>
<th>Lease</th>
<th>Unit</th>
<th>Building</th>
<th>Term</th>
<th>Payment date</th>
<th>Payment method</th>
</tr>
{% for payment in page_payment %}
<tr>
<td> {{ payment.id }}</td>
<td> {{ payment.get_payment_type_display }}</td>
<td> {{ payment.amount }}</td>
<td>
{% if payment.is_deposit %}
<span class="glyphicon glyphicon-ok text-success" aria-hidden="true" ></span>
{% else %}
<span class="glyphicon glyphicon-remove text-warning" aria-hidden="true" ></span>
{% endif %}
</td>
<td> {{ payment.lease }}</td>
<td> {{ payment.lease.unit.number }}</td>
<td> {{ payment.lease.unit.building.name }}</td>
<td> {{ payment.leaseterm }}</td>
<td> {{ payment.payment_date }}</td>
<td> {{ payment.get_method_display }}</td>
</tr>
{% endfor %}
</table>
<div class="pagination">
<span class="step-links">
{% if page_payment.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ page_payment.number }} of {{ page_payment.paginator.num_pages }}.
</span>
{% if page_payment.has_next %}
next
{% endif %}
</span>
</div>
</div>
</div></div>
{% endblock content %}
My problem is that when my search returns amount of results more than 1 page and I click on next pages. The next pages are always empty.
Any feedback where my problem is?
The problem is that your serch form is POST. So when you click next button you send GET request and if request.method == "POST": part of you view is not triggered.
I suggest you to change serch form to GET:
<form method="GET" action="">
<input type="text" name="q" placeholder="Search" value="{{ request.GET.q }}">
<input type="submit" value="Search">
</form>
And change next button to this:
{% if payment.has_next %}
next
{% endif %}
This will save search value in address bar even after you move to the next page.

Django formset is not getting saved

I have a formset and I am trying to save it back. But, when I try to check if formset.is_valid() and save, validation always fails even if nothing in the formset has been changed. I am displaying already existing data using formset and trying to edit it. I don't know where I am going wrong. Can someone please help? :(
views.py
def product(request, product_id):
product = get_object_or_404(Product, pk=product_id)
productform = ProductForm(instance=product)
itemformset = modelformset_factory(Item, form = ItemForm, extra=0)
items = itemformset(queryset = Item.objects.filter(Product=product_id), prefix='items', )
if request.method == 'POST':
productform = ProductForm(request.POST, instance=product)
items = itemformset(request.POST, queryset = Item.objects.filter(Product=product_id), prefix='items', )
if productform.is_valid():
productform.save()
if items.is_valid():
items.save()
else:
print("fail")
context = {
'productform':productform,
'product_id': product_id,
'items':items,
}
return render(request, 'product.html', context )
.html
{% bootstrap_form productform %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#Items"> Items List </a>
</h4>
</div>
<div id="Items" class="panel-collapse collapse">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
</tr>
<thead>
<tbody>
{{ items.management_form }}
{% for form in items %}
<tr>
{{ form.id}}
<td> {{ form.Name }} </td>
<td> {{ form.Tag }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
You are rendering the form fields individually, but not displaying the errors. First, allow Django to render the forms and get the view working. Once you've done that, you can customise the template if required.
<tbody>
{{ items.management_form }}
{% for form in items %}
{{ form }}
{% endfor %}
</tbody>
See the docs on using formsets in templates rendering form fields manually for more information.
Doing it this way should allow you to see errors in the template. Another option is to print or log productform.errors and items.errors. Your current print("fail") tells you there is a problem, but not what the problem is.