I have an HTML table which pulls data from an SQLAlchemy database.
In this table, for each row, I have a Delete button, which should delete that row and database entry when I click on it.
The issue I have, is that if I try to delete any row in this table, it deletes only the first row (see image below) and the associated data in the database.
The row and the data I wanted deleted remain unchanged (the 3rd row in the image below).
If I click Delete on the first row in the table, it works without issues.
The Update button works as indented (updating the correct entry)
This is the database model I have:
class ActualPost(db.Model):
__tablename__ = 'actualpost'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title_actual = db.Column(db.String(30), nullable=False, default='actual')
category_actual = db.Column(db.String(30), nullable=False, default=None)
actual_amount_name = db.Column(db.String(30), nullable=True)
actual_amount = db.Column(db.Float, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
comments = db.Column(db.Text, nullable=True)
def __repr__(self):
return f"ActualPost('{self.title_actual}, '{self.category_actual}'\
, '{self.actual_amount_name}', '{self.actual_amount}'\
, '{self.date_posted}', '{self.comments}')"
Here are my routes (Update and Delete)
#posts.route("/post/<int:post_id>/update_actual", methods=['GET', 'POST'])
#login_required
def update_actual_post(post_id):
post = ActualPost.query.get_or_404(post_id)
if post.actual_author != current_user:
abort(403)
form = PostActualForm()
if form.validate_on_submit():
post.title_actual = form.title_actual.data
post.category_actual = form.category_actual.data
post.actual_amount_name = form.actual_amount_name.data.name
post.actual_amount = form.actual_amount.data
post.comments = form.comments.data
db.session.commit()
flash('Your post has been updated!', 'success')
return redirect(url_for('main.actual', post_id=post.id))
elif request.method == 'GET':
form.title_actual.data = post.title_actual
form.category_actual.data= post.category_actual
form.actual_amount_name.data = post.actual_amount_name
form.actual_amount.data = post.actual_amount
form.comments.data = post.comments
return render_template('create_actual_post.html', title='Update Actual',
form=form, legend='Update Actual')
#posts.route("/post/<int:post_id>/delete_actual", methods=['POST'])
#login_required
def delete_actual_post(post_id):
post = ActualPost.query.get_or_404(post_id)
if post.actual_author != current_user:
abort(403)
db.session.delete(post)
db.session.commit()
flash('Your post has been deleted!', 'success')
return redirect(url_for('main.actual', post_id=post.id))
This is the HTML code I use for the Delete button:
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form action="{{ url_for('posts.delete_actual_post', post_id = post.id) }}" method="post">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
Here is my code to delete particular row by id. You can co-relate it with your code
SQL-alchemy Modal :
class Componants(db.Model):
__tablename__ = 'componants'
id = db.Column(db.Integer, primary_key=True)
cmp_options = db.Column(db.Text, default=None)
updated_at = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, nullable=True)
def __init__(self , cmp_options ):
self.cmp_options = cmp_options
self.created_at = datetime.now()
self.updated_at = datetime.now()
def __repr__(self):
return '<id {}>'.format(self.id)
Controller :
#componants_blueprint.route('/delete_component/<component_id>')
#login_required
def delete_component(component_id):
component = Componants.query.filter_by(id=component_id).first_or_404()
db.session.delete(component)
db.session.commit()
return True
View:
<tbody>
{% for item in all_components %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.cmp_options }}</td>
<td>{{ item.created_at }}</td>
<td class="text-center">
</td>
</tr>
{% endfor %}
</tbody>
The above did not help me.
But the comments in this post did help me.
In short: the problem is inside the HTML tags. I used a 'modal' from the Bootstrap.
The comments in this page suggested to inspect the page elements.
Also in your chrome/firefox developer tools, make sure you check that form's action property and ensure that its correct in the case of that delete button. –
Prahlad Yeri
Jun 10, 2018 at 23:48
The inspection showed that each row in the table did create 'some' unique features such as the name of the item I like to delete. However, during 'inspect' I clicked on my delete button. This showed that the action was stuck in the first line of the table. It did not jump to the line where I clicked 'delete'.
What is inside the HTML generating the issue? Is it the row or is it the modal?
I noticed that the html tag for the modal 'id' was the same for all rows in my table. Question: shouldn't it be unique for each row? I guess, yes.
So, I added the jinja tag to my ID. Would it work? Yes, for me it did...
{% block table %}
<table id="data" class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Latitude</th>
<th scope="col">Longitude</th>
<th scope="col">Jars</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for map in maps %}
<tr>
<td>{{ map.map_name }}</td>
<td>{{ map.latitude }}</td>
<td>{{ map.longitude }}</td>
<td><span style="white-space: pre-line">{% for jar in map.jar_id %}
{{ jar.jar_name }}
{% endfor %}
</span>
</td>
<td><!-- Button trigger modal -->
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#{{map.map_name}}Modal">
Delete
</button>
<!-- Modal -->
<div class="modal fade" id="{{map.map_name}}Modal" tabindex="-1" aria-labelledby="{{map.map_name}}ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="{{map.map_name}}ModalLabel">Are you sure?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure to delete the item : {{ map.map_name }}</p>
</div>
<div class="modal-footer">
Yes
No
</div>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Related
I would like when I add a file in the table it automatically adds a number from 1. I cannot use the primary key in the model because I already use it. anyone have a solution. thank you.
====Page.html====
<h5>Documents</h5>
</div>
<div class="row">
<div class="col-md">
<div class="card card-body">
<table class="table table-sm">
<tr>
<th>#</th>
<th>Nom du fichier</th>
<th>Fichier</th>
<th></th>
</tr>
{% for d in document %}
<tr>
<td>{{d.Number}}</td>
<td>{{d.Description}}</td>
<td><a download href="{{d.Fichier.url}}">{{d.Fichier}}</a></td>
<td><a class="btn btn-outline-danger" href="{% url 'supprimerdocument' d.id %}">Supprimer</a>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
====view.py====
# Button to add docs
#login_required(login_url='loginpage')
#allowed_users(allowed_roles=['admin', 'livraison'])
def createDocument(request):
forms = documentForm()
if request.method == 'POST':
forms = documentForm(request.POST, request.FILES)
if forms.is_valid():
forms.save()
return redirect('/employe')
context = {'forms':forms}
return render(request, 'accounts/document_form.html', context)
====Models.py====
class Document(models.Model):
employe = models.ForeignKey(Employe, null=True, on_delete=models.SET_NULL)
Number = models.IntegerField(default=1)
Description = models.CharField(max_length=100, null=True)
Fichier = models.FileField(upload_to='documents/')
data_created = models.DateTimeField(auto_now_add=True, null=True)
====Form.py====
class documentForm(ModelForm):
class Meta:
model = Document
fields = '__all__'
exclude = ['Number']
You can use forloop.counter in your template to access the (1-indexed) iteration of the for loop
{% for d in document %}
<tr>
<td>{{ forloop.counter }}</td>
...
</tr>
{% endfor %}
It looks like you can do an AutoField as per How do I make an auto increment integer field in Django?
But as a fallback: You could just do an Integer field and override the modelform's .save() method and auto fill it like self.some_field = Document.objects.all().count()
..if you do that, best to add a flag for if it's an edit form or not.. I believe you can do that with if self.instance: if true, is edit form
use callable default
def get_next_number():
max_val = Document.objects.aggregate(models.Max('Number'))['Number__max'] or 0
return max_val + 1
class Document(models.Model):
Number = models.IntegerField(default=get_next_number)
...
I have been trying to make an 'update function' in my table so that my data can be changed by a user.
So here is my html with the form where i want my data to go back in to, so when the user presses submit again it saves that value to the database:
<h3>Add a location </h3></br></br>
<div class="addition">
<form method="POST" action="" autocomplete="off">
{% csrf_token %}
<div class="row">
<div class="col">
<input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Location name" name="name" autocomplete="off">
</div>
<div class="col">
<input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Description" name="desc" autocomplete="off">
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Submit</button>
</div></br></br>
</form> </br></br>
<h3>Overview locations</h3>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Description</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for item in all_locations %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ item.name }}</td>
<td>{{ item.desc }}</td>
<td><a class="btn btn-sm btn-info" href="{% url 'update_location' item.id %}" >update</a></td>
</tr>
my views.py with my creation function and my update function
def location(request):
all_locations = Location.objects.all()
if request.method == "POST":
loc_form = LocationForm(request.POST or None)
if loc_form.is_valid():
loc_form.save()
return render(request, 'location.html', {"all_locations": all_locations})
else:
return render(request, 'location.html', {"all_locations": all_locations})
return render(request, 'location.html', {"all_locations": all_locations})
def updateLocation(request, pk):
all_locations = Location.objects.all()
location = Location.objects.get(id=pk)
loc_form= LocationForm(instance=location)
name = loc_form.instance.name
desc = loc_form.instance.desc
print(name)
print(desc)
return render(request, 'location.html', {"all_locations": all_locations, "name": name, "desc": desc})
my models.py
class Location(models.Model):
creation_date = models.DateTimeField(auto_now = True)
name = models.CharField(max_length=50, default=None)
desc = models.CharField(max_length=250, default="", null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
and my urls.py
urlpatterns = [
path('', views.index, name="index"),
path('locations', views.location, name="location"),
path('locations/<str:pk>', views.updateLocation, name="update_location"),
]
So basically i'm stuck at putting the old data back into the html input.
I've tried with a different view as well but i had basically the same issue.
Any suggestions? Thanks a lot!
I figured it out whilest doing something else:
add value="{{ name }}" to the html file on the input line. Do the similar thing for description
This is my first coding python/flask/sqlalchemy project.
I want to create a page where a HR can assign a selected project to a selected employee.
One employee can work with many projects. Many employees can work in one project (many to many relation).
My problem, I couldn't get the selected project for the selected employee and fill out the association table. I tried to search but couldn't find relevant topic. Thank's for response!
Code for database part
my_db.py:
Employee_Task = db.Table('employee_task',
db.Column('employee_id', db.Integer, db.ForeignKey('employee.employee_id')),
db.Column('task_id', db.Integer, db.ForeignKey('task.task_id'))
)
class Employee(db.Model):
__tablename__ = 'employee'
employee_id = db.Column(db.Integer, primary_key=True)
employee_name = db.Column(db.String(100), nullable=True)
rel_task = db.relationship("Task", secondary=Employee_Task)
# class Task
class Task(db.Model):
__tablename__ = 'task'
task_id = db.Column(db.Integer, primary_key=True)
task_name = db.Column(db.String(100), nullable=True)
def __repr__(self):
return "<Task %r>" % self.name
Part of main code, As you can see it adds new row with same employee_id and task_id, but I want to add new row with choosen employee_id with choosen task_id
Let's say when HR assign to 5th employee 8th project, I want to see in one row of association table:
5 in employee_id column
8 in project_id column
main.py:
#app.route("/employee", methods=['POST', 'GET'])
def employee():
tasks_all = Task.query.all()
workers_all = Employee.query.all()
if request.method == 'POST'
idS = str(int(request.form.get("Assigned_Task")))
EmployeeID = idS[0] #this is choosen employee, I want add this employee to association table
TaskID = idS[-1] #this is choosen project, I want add this project to association table
e = Employee()
t = Task()
e.rel_task.append(t)
db.session.add(e)
db.session.commit()
return render_template('my.html', tasks_all=tasks_all, workers_all=workers_all, form=search,
title='List of Employee')
part of the html page (as you can see it's table, for given list of employee HR can choose a project from drop-down menu and assign it)
<table class="table table-hover">
<thread>
<tr class="table-info font-italic">
<th>Employees</th>
<th>Projects</th>
</tr>
</thread>
<tbody>
{% for worker in workers_all %}
<tr>
<td rowspan="1">{{ worker.employee_name }}</td>
<td rowspan>
<form action="/employee" method="POST">
<div class="input-group">
<select class="form-control" name="Assigned_Task">
{% for task in tasks_all %}
<option value="{{ worker.employee_id }} , {{ task.task_id }}">
{{ task.task_name }}
</option>
{% endfor %}
</select>
<div class="input-group-append">
<button type=submit value="" class="btn btn-sm btn-info">
<i class="fas fa-user-check"></i>
<a class="mr-1">assign</a>
</button>
</div>
</div>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
I guess you're sending an string representation of a tuple. You must convert it. You can do it by importing the python built-in ast library.
import ast
#app.route("/employee", methods=['POST', 'GET'])
def employee():
tasks_all = Task.query.all()
workers_all = Employee.query.all()
if request.method == 'POST'
idS = ast.literal_eval(request.form.get("Assigned_Task"))
Employee.id, Task.id = idS
e = Employee()
t = Task()
e.rel_task.append(t)
db.session.add(e)
db.session.commit()
return render_template('my.html', tasks_all=tasks_all, workers_all=workers_all, form=search,
title='List of Employee')
main.py:
#app.route("/employee", methods=['POST', 'GET'])
def employee():
tasks_all = Task.query.all()
workers_all = Employee.query.all()
if request.method == 'POST'
idS = str(int(request.form.get("Assigned_Task")))
EmployeeID = idS[0]
TaskID = idS[-1]
e_qry = db.session.query(Employee).filter(Employee.employee_id.contains(EmployeeID))
t_qry = db.session.query(Task).filter(Task.task_id.contains(TaskID))
e = e_qry.first()
t = t_qry.first()
e.rel_task.append(t)
db.session.add(e)
db.session.commit()
return render_template('my.html', tasks_all=tasks_all, workers_all=workers_all, form=search, title='List of Employee')
I have SQLite database which contains data regarding products such as Product Name, Description and Like which shows if the user likes or doesn't like the product.
A user search for different products which populate a table with the name, description and a checkbox which is clicked in if the value of Like is 1 in the database and unchecked if not.
I have implemented the button through (as seen in index.html)
<form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}>
I now want the user to be able to check and uncheck the boxes in the table, and then press a button that updates the Like column in the database, how do I do this?
(I can't even access the value of the button in app.py. When trying print(requests.form['like']) on the line after name=requests.form['search'] it returns BadRequestKeyError: The browser sent a request that this server could not understand. KeyError: 'like')
app.py
...
product = []
#app.route('/', methods = ['POST'])
def index():
name = None
if request.method == 'POST':
name = request.form['search']
if name is not None or name != "":
query_product = Products.query.filter(Products.productname==name).first()
if (query_product is not None) and (query_product not in product):
company.append(query_company)
print(company, file = sys.stderr)
return render_template('index.html', companies = company)
Products class
class Products(db.Model):
index = db.Column(db.Integer(), primary_key = True)
productname = db.Column(db.String(), primary_key = False)
description = db.Column(db.String(), primary_key = False)
like = db.Column(db.Integer(), primary_key = False)
...more columns
index.html
<!-- /templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<form method="POST">
<input type="text" placeholder="Search for Product.." name="search">
<button type="submit" name="submit">Search</button> <button type="submit" name="update" value = company.index style = margin-left:45%>Update</button>
<div class="product-container" style="overflow: auto; max-height: 80vh">
<div class="table-responsive">
<table class="table" id="products">
<thead>
<tr>
<th scope="col">Product Name</th>
<th scope="col">Description</th>
<th scope="col">Like</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr {{company.index}}>
<th scope="row">{{ product.productname}}</th>
<td> {{ product.description }} </td>
<td><form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}></form></td>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
I guess you need to do this with javascript and add another route to your backend which then updates the database.
maybe something like this, if it should happen automatically:
<input type="checkbox" onchange="updateLike('productId', this.checked)">
<script>
async function updateLike(productId, doesLike) {
let response = await fetch("http://localhost/products/productId/like", {
method:"POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify({
productId: productId,
like: doesLike
})
});
}
</script>
or you could add a button which sends the request to the server.
<input type="checkbox" name="like"/>
<button onclick="updateLike('productId', document.querySelector('input[name=like]').checked)">confirm</button>
So, I'm developing a web application to control the inventory on my company in Django (yes, I know there are plenty open source alternatives out there, we are in fact using fusioninventory right now, but I'm not the boss...).
The thing is, one computer can have multiple users, and so I implemented it on the model as a ManyToMany field. Here is the relevant code for the model, filter and html template. Note that I'm using django_filters lib to implement the search filters and widget_tweaks to give some style to the form.
models.py
class Computer(models.Model):
tag = models.CharField(max_length = 20, unique=True)
users = models.ManyToManyField(User, blank = True, default = 'No_User',)
TYPE = (
('DESK','Desktop'),
('LAP','Laptop'),
)
computertype = models.CharField(
max_length = 4,
choices = TYPE,
)
STATUS = (
('OK','OK'),
('Broken','Broken'),
('Unusable','Unusable'),
)
computerstatus = models.CharField(
max_length = 12,
choices = STATUS,
default = 'OK',
)
model = models.CharField(max_length = 36)
serial = models.CharField(max_length = 36)
buy_date = models.DateField()
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.tag
def get_absolute_url(self):
return reverse('computer_detail', args=[str(self.id)])
filters.py
class UserFilter(django_filters.FilterSet):
class Meta:
model = User
fields = {
'username': ['contains'],
}
class ComputerFilter(django_filters.FilterSet):
tag = django_filters.CharFilter(lookup_expr="icontains",label = 'Etiqueta',)
computertype = django_filters.ChoiceFilter(choices = Computer.TYPE, lookup_expr="icontains",label = 'Tipo',)
computerstatus = django_filters.ChoiceFilter(choices = Computer.STATUS, lookup_expr="icontains",label = 'Estado',)
**users = UserFilter()**
from = django_filters.DateFilter(field_name='buy_date', lookup_expr="gt", label='Adquiridos desde',)
to = django_filters.DateFilter(field_name='buy_date', lookup_expr="lt", label='Hasta',)
class Meta:
model = Computer
fields = ['tag','users','computertype','computerstatus',]
computer_list.html
<form method="get">
<div class="well">
<center><h4><b>Filtro</b></h4></center>
<center><table>
<tr>
<td><div class"form-group">
{{ filter.form.tag.label_tag}}
{% render_field filter.form.tag class="form-control" %}
</div></td>
<td> </td>
<td></td>
</tr>
<tr>
<td><div class"form-group">
{{ filter.form.computertype.label_tag}}
{% render_field filter.form.computertype class="form-control" %}
</div></td>
<td> </td>
<td><div class"form-group">
{{ filter.form.computerstatus.label_tag}}
{% render_field filter.form.computerstatus class="form-control" %}
</div></td>
</tr>
<tr>
<td><div class"form-group">
{{ filter.form.desde.label_tag}}
{% render_field filter.form.desde class="form-control" type="date" %}
</div></td>
<td> </td>
<td><div class"form-group">
{{ filter.form.hasta.label_tag}}
{% render_field filter.form.hasta class="form-control" type="date" %}
</div></td>
</tr>
<tr>
<td><div class"form-control" input type ="text">
{{ filter.form.users.label_tag}}
{% render_field filter.form.users class="form-control" %}
</div></td>
<td></td>
</tr>
</table></center>
<div>
<p> </p><center><button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span> Buscar
</button></center>
</div>
</div>
</form>
You can see the full code on github
Right now the filter form looks like this:
See the Image
Question: Is there an easy way so that the form field would accept input like:
user1, user2, ... , usern instead of selecting the users?
Bonus: Is there a way to implement it so that it also has autocomplete (I write for example, - user1, use - and it shows me all the users begining with "use" so I can click it and it gets added to the field.
Thank you for your time.
you can user ModelMultipleChoiceField
users = ModelMultipleChoiceField()