I have created a program which does data entry and returns a pandas table (the info is saved in a list, called JOB_INFO, at the top of the views.py file - I will attach this below) with some results which can be downloaded. I also have the table rendering on the html page that has the data entry form - the table renders below the form and updates(appends) with each user input(form submit). This is works perfectly in development on my local machine, but when it's been successfully deployed to heroku, it starts acting up and not behaving as expected.
Examples include:
Not updating the table - the form seems to submit but the table doesn't update. If I keep trying, it suddenly works. But then I'll try another and the previous one disappears and only the latest one will show in the table. It just seems so random.
Not emptying the list - I have a button to clear the table but that doesn't work either. If I click the button a bunch of time it might work suddenly. But then randomly some information will show up in the table again that I thought was cleared!
Info in downloaded file when I thought I cleared it - this one is kind of self-explanatory.
In a nutshell, calling it random is probably the best way to describe it. It's just not doing what I want it to do and what is expected of it.
views.py
from django.shortcuts import render, redirect, get_object_or_404, reverse, HttpResponse
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from .models import Data
from .forms import DataForm, SearchAndUpdateForm
from io import BytesIO
import pandas as pd
import xlsxwriter
JOB_INFO = {
"Plentific Job Number": [],
"WorkOrder Number": [],
"Invoice Number Found": [],
"Payments": [],
"Job Number Exists": [],
}
def data_entry(request):
"""
Creates new instance of data entry form and renders it on html page
along with a report table, which updates with each data entry input
"""
form = SearchAndUpdateForm()
df = pd.DataFrame(JOB_INFO,
columns=[
"Plentific Job Number",
"WorkOrder Number",
"Invoice Number Found",
"Payments",
"Job Number Exists",
])
if not df.empty:
df_html = df.to_html(classes="table table-striped table-hover")
return render(request, "data_entry.html", {
"form": form, "df_html": df_html})
else:
return render(request, "data_entry.html", {
"form": form})
def export_job_info_report(request):
"""
Exports the job_info list as an Excel spreadsheet
and saves it to user's local directory
"""
df = pd.DataFrame(JOB_INFO)
output = BytesIO()
writer = pd.ExcelWriter(output, engine='xlsxwriter')
df.to_excel(writer, sheet_name='report', index=False)
writer.save()
output.seek(0)
response = HttpResponse(output,
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=%s.xlsx' % 'Download'
return response
def clear_report_info(request):
"""Empties the job_info dictionary"""
JOB_INFO["Plentific Job Number"].clear()
JOB_INFO["WorkOrder Number"].clear()
JOB_INFO["Invoice Number Found"].clear()
JOB_INFO["Payments"].clear()
JOB_INFO["Job Number Exists"].clear()
return redirect(reverse(data_entry))
html page
{% extends "base.html" %}
{% block content %}
{% load crispy_forms_tags %}
<div class="jumbotron">
<h1>Data Entry Tool:</h1>
<form action="{% url 'search_update_feedback' %}" method="GET">
{{ form|crispy }}
<button class="btn btn-success" type="submit" data-toggle="tooltip" data-placement="right" title="Updates the database with the info provided above">
Update and Fetch Info
</button>
</form>
<br>
<form action="{% url 'export_job_info_report' %}" method="GET">
<button class="btn btn-success" type="submit" data-toggle="tooltip" data-placement="right" title="Downloads the report below in .xlsx format">
Download Report
</button>
</form>
<br>
<form action="{% url 'clear_report_info' %}" method="GET">
<button class="btn btn-success" type="submit" data-toggle="tooltip" data-placement="right" title="Warning! You will lose all of the data below (does not affect database data)">
Clear Report - hover over me!
</button>
</form>
</div>
<hr>
{{ df_html | safe }}
{% endblock %}
urls
urlpatterns = [
path("", data_entry, name="data_entry"),
path("export_job_info_report/", export_job_info_report, name="export_job_info_report"),
path("clear_report_info/", clear_report_info, name="clear_report_info"),
]
Of course I have left out a whole lot of functionality to save reading time but please let me know if anything else can/should be added to help understand the issue.
Any help will be greatly appreciated.
I managed to solve this issue by replacing the JOB_INFO list with a new Django Model with the same field names, like this:
from django.db import models
class JobInfoReport(models.Model):
"""The following are prefixed with 'r' to represent report"""
r_plentific_job_number = models.CharField(max_length=7)
r_wo_number = models.CharField(max_length=6)
r_invoice_number_found = models.CharField(max_length=5)
r_payments = models.CharField(max_length=100)
r_job_number_exists = models.CharField(max_length=50)
def __str__(self):
return self.r_plentific_job_number
Related
I am trying to implement newsletter/email subscription for my project.
I created a model which only stores the email and the timestamp and uses SendGrid to send emails to the users who subscribed.
I want to include an unsubscribe button inside the emails I send them. When the user clicks unsubscribe link in the mail it appends the id of the value in db to the url and redirects to cancelsub.html where I am accessing it.
In cancelsub.html I have a form with a submit button which when a user clicks should delete the value from db. It is not working for some reason.
Models.py--
class NewsletterUser(models.Model):
email = models.EmailField(null=True)
date_added = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.email
Views.py--
def NewsLetter(request):
if request.method == 'POST':
email_input = request.POST.get('email_value')
new = NewsletterUser(email=email_input)
new.save()
sendEmail(email_input)
return render(request,"pages/index.html")
def DeleteNewsLetter(request):
if request.method == 'POST':
del_id = request.POST.get('id_value')
NewsletterUser.objects.filter(id= del_id).delete()
return render(request, "newsletter/CancelSubscription.html")
cancelsub.html--
<form id="cancel-subscription-form" method="POST">
{% csrf_token %}
<div class="email-and-btn">
<button class="btn btn-danger mb-2 art-digest-btn" id="cancel-btn" type="submit" value="">Yes, Cancel It</button>
</div>
</form>
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<script>
var current_url = window.location.href
var id = current_url.split('?')[1]
id_int = parseInt(id)
$("#cancel-btn").val(id_int);
$(document).on('submit','#cancel-subscription-form',function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'{% url "DeleteNewsLetter" %}',
data:
{
id_value: parseInt($("#cancel-btn").val()),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
},
success:function(){
}
})
});
</script>
</div>
urls.py--
urlpatterns = [
path('', views.NewsLetter, name='NewsLetter'),
path('CancelSubscription', views.CancelSubscription, name='CancelSubscription'),
path('', views.DeleteNewsLetter, name='DeleteNewsLetter'),
]
When I execute this code out, instead of deleting the value from database, it adds a blank value into the db. I'm confused as to why this is happening.
It'd be really helpful if anyone guide me where I went wrong!.
Thanks in advance!!
I understand that the URL that you send on the email is something like this: http://mywebsite.com/unsubscribe/?9
So you get the "9" with the javascript code. You don't need the javascript if you give a name to your value like this: http://mywebsite.com/unsubscribe/?user_id=9
Now, you can just doing this:
<form id="cancel-subscription-form" method="POST" action="{% url "DeleteNewsLetter" %}">
{% csrf_token %}
<div class="email-and-btn">
<button name="id_value" class="btn btn-danger mb-2 art-digest-btn" id="cancel-btn" type="submit" value="{{request.GET.user_id}}">Yes, Cancel It</button>
</div>
</form>
I think that your problem is in the javascript code, so simplify and deleting it probably your system works.
I am trying to delete an object from database using simple html button. Also, trying to implement "are you sure" message? BUt I am getting this error everytime and I am not able to crack.
This is my view function.
def customerdel(request,pk):
objs = Customer.objects.filter(id=pk)
if request.method == 'POST':
objs.delete()
# messages.success(request, 'Successfully deleted')
return render(request,'records.html')
else:
content ={
'items':Customer.objects.all
}
return render(request,'delete.html', content)
This is record.html page
<h1>Record's page</h1>
{% for abc in Customerdata %}
{{abc.name}}
{{abc.pk}}
<form >
<button class="btn btn-danger btn-sm">
Delete</button>
</form>
{% endfor %}
This is delete.html page
<h1>Welcome to Delete page</h1>
<p>Are you sure want to del {{items.name}} ??</p>
<form action="{% url 'customerdel' items.pk %}" method="post">
{% csrf_token %}
Cancel
<input name="confirm" type="submit" >
</form>
This is my URL.
path('dashboard/records/customerdel/<int:pk>', views.customerdel, name='customerdel'),
You are doing wrong while template rendering as you are not sending any data to the template.But inside the record.html you are trying to iterate over CustomerData which is not defined.So send it to the template or another way is to simply redirect to the route which is made for rendering record.html.
Here is the full code which you should apply in your django project to make it work.
from django.http import HttpresponseRedirect
from django.urls import reverse #these two lines are required for redirecting to a previous route(url)
As you must have set a url which takes to the records function something like this;
path('records', views.records, name='records')
If you haven't added the name, add it.
Then in views.py, in customerdel function;
if request.method == 'POST':
#write your code which you already wrote and instead of return render(...) write this;
return HttpResponseRedirect(reverse('records')) #where records is the name of the url which takes us to the records page.
What I really want to do is , if a user click on "ADD more" button then a same form repeat itself and the values should store in database, if he/she doesn't click of that button then only the values from first form should be stored.
I am not able to get this, I just created a form , and a table in database for those details but can't loop though the form neither in data.
please help.
This is the form and the button:
This is the model.py code:
from django.db import models
class experience(models.Model):
company_name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
startdate = models.Datefield(default = 01-01-2020)
lastdate = models.DateField(default = 01-01-2020)
profile = models.CharField(max_length=100)
description = models.TextField(max_length = 250)
This is the views.py code:
from django.shortcuts import render, redirect
import requests
from django.contrib.auth.models import User, auth
# Create your views here.
def profile(request):
return render(request, 'profile.html')
Unfortunately, there's no built-in way (as far as I know) in Django to do that without Javascript, but here's an approach:
HTML:
<div class="container" id="experiencesContainer">
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<button type="button" id="addMoreButton">Add more</button>
<button type="submit">Save Changes</button>
</div>
Django POST method:
# Get a list of submitted forms
experiences = request.POST.getlist('experienceForm')
for experience in experiences:
# this is how you loop throuh every form
experience.get('company_name)
Your javascript something like:
// clonning his childs as well
let cloneForm = document.querySelector('form[name=experienceForm]').cloneNode(true);
document.querySelector('div#experiencesContainer').appendChild(cloneForm);
// see this https://www.w3schools.com/jsref/met_node_clonenode.asp
Of course this code is not tested but I've done this in several projects before, hope it works!
A simple way would be to request the same view from the "Add", just make sure your form view saves the data when request method is POST.
<form action="{% url 'your-form-url' %}" method="GET">
{% csrf_token %}
<input type="submit" value="Add">
</form>
one other way to repeat forms would be using formsets. Formsets allow you to repeat the same form 'extra' times. Check out the documentation for more about this.
def repeat_form(request):
ExpFormSet = formset_factory(ExperienceForm, extra=3)
#extra defines the no. of forms you want to display
if request.method == 'POST':
formset = ExpFormSet(request.POST, request.FILES)
if formset.is_valid():
# do something with the formset.cleaned_data
#loop through each form in the formser
for form in formset.cleaned_data:
obj = form.save()
else:
formset = ExpFormSet()
return render(request, 'exp_form.html', {'formset': formset})
The corresponding template should be:-
<form method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
</form>
Make sure you add form.management_form. Using the combination of the above might solve your problem of taking and saving several inputs.
Firstly, I am getting a csv file from the user.
(Template file:)
<form method="post" action="{% url 'rowcol' %}" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file" accept=".csv">
<button type="submit">Upload File</button>
</form>
Then i am generating a list of all the columns present in the file and then calling another html file. (views.py:)
def rowcol(request):
if request.method == 'POST':
file = request.FILES['file']
dataset=pd.read_csv(file)
lst=list(dataset)
return render(request, 'Link5_rc.html', {'arr':lst})
return HttpResponse('')
In that html file, i am creating buttons for all the columns present.(Link5_rc.html:)
{% for link in arr %}
<form action=" " method="post">
<button name="{{link}}" type="submit" value="{{link}}">{{link}}</button>
</form>
{% endfor %}
Now the following is the part where i am stuck: I want these buttons to redirect to another html page or maybe a view in views.py , where i can show to the user which column he/she selected and then perform further actions on that particular column.
You can pass one or more values to the request as in the following example:
Some text
You can use variables like:
Some text
In your app urls.py you should set the following to receive the extra data in the request:
url(r'^your_name/(?P<value_1>[\d\D]+)$', views.your_view, name="your_url_alias")
Then, in your view function you should receive the data as in:
def your_view(request, value_1):
An then you can use the value to filter the queryset.
I am super new to Django and web development. Right now my objective is to create a google like interface and take the text from search box and write it to a file (in other words just want to access text data in the search box). I have created a search page like below
search.html
{% extends "header.html" %}
{% block content %}
<div style="display: flex; justify-content: center;">
<img src="/static/images/logo.jpg" class="responsive-img" style='max-height:300px;' alt="face" >
</div>
<form method="get" action="">
{% csrf_token %}
<div style="display: flex; justify-content: center;">
<input type="text" name="query" placeholder="Search here..." required size="70" >
<button type="submit">Go!</button>
</div>
<button type="submit">Search</button>
</form>
{% endblock %}
views.py
from django.shortcuts import render
def index(request):
return render(request, 'search.html')
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index')
]
Please give me a hint/example of how to go forward from here ? Thanks.
Your search field looks like this:
<input type="text" name="query">
The name of the input is query. Since it's a GET form, when you submit it, you must have noticed, the url looks something like this:
/?query=<value of the input>
The part after ? is called querystring. For every request, Django maintains a dictionary of the querystring. The request object has a dictionary called GET for GET requests. If you make a POST request, Django will save the form data in a dict called POST.
To access the value of the request querystring in Django, you can do this:
query = request.GET.get('query')
If it's a POST request, you'd do the same but use the POST dictionary this time:
some_value = request.POST.get('some_key')
Full docs on this can be found at - Request and response objects.
This should do it
views.py
def index(request):
query = request.GET.get('query')
# do a check here to make sure search_term exists before attempting write
with open('/path/to/file', 'rw') as f:
f.write(query)
return render(request, 'search.html')