Flask: delete file from server and database - python

Hat I'm trying to accomplish is to delete file from server ('static' folder, to be specific).
My jinja template:
<table>
<tr>
{% for file in files_.items %}
<td data-title="title" style="text-align: center">{{ file.title }}</td>
<td data-title="download">Download</td>
{% if current_user.username == "admin" %}
<td data-title="delete" style="text-align: center">Delete</td>
{% endif %}
</tr>
{% endfor %}
</table>
and my function:
#app.route('/upload/<path:filename>/', methods=['GET', 'POST'])
#login_required
def delete(filename):
item = db.session.query(File).get(filename)
os.remove(os.path.join(app.static_folder, item.filename))
db.session.query(File).filter_by(file=filename).delete()
db.session.commit()
return render_template('dashboard.html',delete=delete)
What I'm trying to do is to after clicking on delete in html I want to delete record from database and file from the server. Right now I'm not sure if my approach to call this function is correct, since I've tried to use prints as a primitive log system and there was nothing in the terminal, co I would say function was not called. Also my guess is that I would need to pass filename to it, so Ive tried
{{ delete(filename=file.file) }}
but it returned
UndefinedError: 'delete' is undefined

{{ delete(filename=file.file) }} in template tells python "when rendering template, call function delete()". What you want to do is generate link which, when clicked, will call delete endpoint.
So, use {{ url_for('delete', filename=...) }}

Related

Query and display one row based on user input in jinja

I want to print one table row based on the user input. I am getting the user input (id) via HTML and Python, here:
html file:
<tbody class="table-group-divider">
{% for course in user.courses %}
<tr>
<!-- here an ID gets rendered from the table, and user clicks on it -->
<td>{{ course.course_name }}</td>
<td class="align-middle">
{% for student in course.students %}
<span>{{ student.student_name }}</span><br>
{% endfor %}
</td>
<td>{{ course.course_day }} {{ course.course_time }}</td>
<td>{{ course.course_language }}</td>
</tr>
{% endfor %}
</tbody>
app.py:
#app.route('/course_detail/<id>', methods=["GET", "POST"])
#login_required
def course_detail(id):
return render_template("course_detail.html", user=current_user, id=id)
My logic is to loop through the table, and if id provided by user matches id (primary key) in table, I found what I want to display, and then display it. However, my attempt does not print anything:
<!-- setting the ID from url/html, basically what user clicked -->
{% set id = id %}
{% for course in user.courses %}
{% if course.id == id %}
<p>im working</p>
{% endif %}
{% endfor %}
I get the id from user, but I can't seem to compare it with course.id so I could display the whole row. Is there a problem with my if statement?
Edit: If I hardcode the if statement to be for example {% if course.id == 2 %} (or any other valid course.id from the table), the information gets printed with no issues.
While I'm not sure why the original proposition doesn't work, I managed to reach my goal via querying my database in app.py, not my html file.
#app.route('/course_detail/<id>', methods=["GET", "POST"])
#login_required
def course_detail(id):
# query the id in db
db_id = Course.query.filter_by(id=id).first()
# save all info into variables
db_course_name = db_id.course_name
db_course_language = db_id.course_language
db_course_day = db_id.course_day
db_course_time = db_id.course_time
db_hourly_rate = db_id.hourly_rate
# send the variables and print them in html
return render_template("course_detail.html", user=current_user, pageid=db_id, db_course_name=db_course_name, db_course_language=db_course_language, db_course_day=db_course_day, db_course_time=db_course_time, db_hourly_rate=db_hourly_rate)
Then I simply printed the variables using double curly braces. Not sure if this is the best design, but it certainly feels better than my previous attempt.

How to render multiple update forms in Django

here is my problem.
I have a list of objects that I display in a table and all works just fine except that I would like to edit them inside a modal and submit them using AJAX.
I though that it was a simple idea to render, for each row, a form with the inputs pre-filled and then submit the selected form with AJAX.
I wonder if there is a relatively simplified way to render the UpdateForm without writing manually all the input fields.
Something like this:
<table>
{% for transaction in transactions %}
<tr>
<td>{{ transaction.date|date:"d.m.Y" }}</td>
<td>{{ transaction.amount }}</td>
<td>
Edit
<div class="modal" id="edit{{ transaction.id }}">
{{ transaction_form }}
</div>
</td>
<tr>
{% endfor %}
</table>
But how can I pass the form from the view?
The way I'm currently doing it is that when the user click on edit the page refresh and the modal is displayed with the form prefilled but it is (a) slow to open and (b) I don't think it is a nice way to do it.
This is my current code
views.py
class ProjectDetailView(DetailView):
model = Project
template_name = "template.html"
context_object_name = "project"
def get_transactions(self):
transactions = Transaction.objects.filter(project=self.get_object())
return transactions
def get_transaction_form(self):
form = TransactionForm()
if self.request.POST:
form = TransactionForm(self.request.POST)
elif 'edit_entry' in self.request.GET:
form = TransactionForm(instance=self.get_entry())
return form
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['transactions'] = self.get_transactions()
context['transaction_form'] = self.get_transaction_form()
return context
template.html
<table>
{% for transaction in transactions %}
<tr>
<td>{{ transaction.date|date:"d.m.Y" }}</td>
<td>{{ transaction.amount }}</td>
<td>
Edit
</td>
<tr>
{% endfor %}
</table>
<div class="modal" id="edit-modal">
{{ transaction_form }}
</div>
<script>
{% if 'edit_entry' in request.GET %}
$('#edit-modal').modal('show')
{% endif %}
</script>
Thank you for any feedback
This solution needs you work with Javascript to do that,
when the user clicks 'Edit' for an object on your page,
you send AJAX request (using Fetch API or Jquery) to your view,
The view will return HTML of the form and you put this HTML in the modal's body
Show the modal with an action button to submit the form.
As the user clicks submit, your code submits the form through Ajax, you can use Formdata or AjaxForm for that, the view which return an JSON or HTML which indicates if the data is saved successfully or not.
The problem I'm not a Class-Based View guy so I can't give you specifics from Django side.

how to get value of download button into my django view?

I have a download button in my HTML that has a href to a path on my system for the corresponding file. How can I load that path into my view when at user clicks download? Also this value is unique for each download button.
If there's any other way that I can do this without exposing my system path in the href I would much prefer to know that. Thanks in advance.
Right now I have some HTML that looks like this. How do I grab the info from item.OutputPath into my view when clicked?
<div class="dashboard-2">
<div class="tasks-finished">
<h1>Finished tasks</h1>
</div>
<div class="tasks-list">
<table>
<tr>
<th>Name</th>
<th>Task ID</th>
<th>Status</th>
</tr>
{% for item in query_finished %}
<tr>
<td>{{ item.TaskNavn }}</td>
<td>{{ item.TaskID }}</td>
<td>Download </tr>
{% endfor %}
</table>
</div>
</div>
Additonal info:
I need this value because i'm trying to save it as a variable to serve protected files using Nginx.
Exposing the system path is a bad idea in itself, but using it as an input parameter would be a huge security risk.
It is better to pass the id of your item to your download view. Something like this:
# template
<td>Download</tr>
# urls.py
path('download/<int:pk>/', views.download_item, name='item-download'),
# views.py
def download_item(request, pk):
# Make sure to perform any required checks, e.g. item.owner=request.user
item = get_object_or_404(Item, pk=pk)
output_path = item.OutputPath
...

Django Search And View more

I have search results in form of table. I want to add a detail page button which can send the id of the result to another function in view.py
so i can query it from database.
{% if sr %}
{% for k,j in sr %}
<tbody>
<tr>
<td>{{ k.id }}</td>
<td>{{ k.chromosome }}</td>
<td>{{ k.gene_id }} </td>
<td> view</td>
</tr>
</tbody>
{% endfor %}
{% endif %}
I want to send this k.id to another function
def detailed(request):
return render(request,"search/Detailed.html")
so I can again perform a query from database by this id
Since you didn't specify the version of django you're using, I'm going to assume it's 2.x. The only huge difference between that and more recent versions of 1.x is the urlpatterns. If you're using 1.11, just use the required regex's you need, as described in the docs. Either way, the principle is the same.
urls.py
urlpatterns = [
....
path('<int:some_id>/', views.detail_view, name='detail_view'),
# if django 1.11... you would use ([0-9]{4}) in place of the 'int', or for whatever max amount of numbers you'd want to capture... But I will continue for django 2.x.
]
views.py
def detail_view(request, some_id):
some_object = YourModel.objects.get(id=some_id)
return render(request, 'detail_template.html', {'some_object ': some_object})
detail_template.html
<p>{{ some_object.chromosome }}</p>
<p>{{ some_object.gene_id }}</p>
<p>View details</p>
Note that the url block above has some_object.id added to it as an argument AFTER the view it goes to in quotations. This is the easiest way.
You can also acheive the same thing with a model method by using the reverse('app_name:view_name', args=[arg_1, arg_2, etc) function, and then call the method with {{ some_object.your_method }}. But those arguments would all depend on what your url patterns and functions took.

flask-wtf same form on same page using for loop

I'm brand new to programming. I came up with a project to help me learn and I'm stuck already. I'm using Flask, Flask-SQLAlchemy and Flask-wtf.
I'm trying to create a club attendance system that lists members and checks them off if they are present and logs the amount they paid (either $15 for 1 lesson, or $25 for the week). I have a table that I populate from my database that looks like this:
I want to click on submit to mark the person as present but this ticks the checkbox for everyone in the list and sets the amount paid to the same for everyone.
I have tried lots of things. I have seen similar issues here and people suggesting using FieldList and FormField - I tried this with no luck. Here is my Form code:
class MemberForm(Form):
form_id = HiddenField()
member_id = DecimalField('id')
member_name = StringField('name')
attend_date = StringField('date', default=todays_date())
is_here = BooleanField('here')
has_paid = SelectField('Amount', choices=[(15, '15'), (25, '25')])
submit = SubmitField("Submit")
def __init__(self, *args, **kwargs):
super(MemberForm, self).__init__(*args, **kwargs)
read_only(self.member_name)
My controller code:
#app.route('/', methods=['GET', 'POST'])
def home():
members = Member.query.order_by(Member.name).all()
form = MemberForm()
if request.method == 'POST': # TODO form validation and database stuff
print('got this far')
print(form.data)
return render_template('index.html', title='Tong Long',
today=todays_date(), members=members,
form=form)
and the jinja2 template part:
<table width="483" border="1">
<tbody>
<tr>
<th width="271"><strong>Member</strong></th>
<th width="152"><strong>Grade</strong></th>
<th><strong>Last Seen</strong></th>
<th width="38"><strong>Paid?</strong></th>
<th><strong>Is Here?</strong></th>
<th>Submit</th>
</tr>
{% for member in members %}
<form action="" method="post" name="{{ member.id }}">
<tr>
<td>{{form.member_name(value=member.name)}}</td>
{% for g in member.grade %}
<td>{{ g.grade }}</td>
{% endfor %}
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{form.is_here}}</td>
<td>
{{ form.submit }}
</td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
Viewing the rendered HTML I can see that all the fields have the same id.
I'm starting to think this can't be done with WTForms. Will I need to use javascript perhaps (something I know nothing about). Or manually create the forms rather than using WTF? Any help appreciated!
This is very late, but perhaps it is helpful to somebody.
What calabash is doing, is create one single form and then display it multiple times in the template.
However, to achieve the desired outcome (independend forms with independend submit buttons), multiple forms need to be created within the route function. They can be passed as a list to the template and then looped over. (A simpler solution would be one form with one submit button and dynamically created "lines" for each member. See FieldList...)
Logic:
def home():
members = Member.query.order_by(Member.name).all()
forms = []
for member in members:
form = MemberForm(prefix=member.name)
form.member_name.data = member.name
forms.append(form)
# validation:
for form in forms:
if form.submit.data and form.validate_on_submit():
# do_something here for each form, e.g. write to database
return render_template('index.html', title='Tong Long',
today=todays_date(),
forms=forms,
members=members)
The different forms need to have individual prefixes. They need to be validated individually and it needs to be checked which submit-button was used.
Note: It is perhaps not a good idea to use a form field for the name, as that information is already known from the members database entry and it might not be intended to change it here. A simple text label would make more sense in that case.
The table rows in the template could look like this:
{% for form in forms %}
<form action="" method="post">
{{ form.hidden_tag() }}
<tr>
<td>{{ form.member_name }}</td>
<td>{{ members[loop.index0].grade }}</td>
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{ form.is_here }}</td>
<td>{{ form.submit }}</td>
</tr>
</form>
{% endfor %}

Categories