Blending the rendering of DataTableView and a standard view in Django - python

I'm pretty new to django and have been experimenting with some of the code
I want to build a form that starts with a parent record, lists the children of that record, and then when I click on a child (or a button in the row of that child) shows the children under that in a datatableview object. phew It should look a little like this:
So the dataset is the primary object into the view, and the tables are a datatables filtered by the dataset id, which all works fine... but how do I get the {{ datatable }} to render in context?
The current view code is pretty basic - this is initially all just for display.
def datasetview(request, datasetid):
dataset = get_object_or_404(DataSet, pk=datasetid)
context = {
'dataset': dataset,
}
return render(request, 'data/dataset_view.html', context)
within the html template, I render the table list with:
{% for datatable in dataset.datatables.all %}
{% if not datatable.deleted %}
<tr>
<td class="p-1 align-middle">{{ datatable.tablename }}</td>
<td class="p-1 align-middle"><button type="button" class="btn btn-outline-primary" onclick="fill_attribute_table({{ datatable.datatableid }})">Edit</button></td>
</tr>
{% endif %}
{% endfor %}
I've been able to render the dataviewtable as a generic page using the demo code provided at pypi.org/project/django-datatable-view (that's how I produced the hacky screen image above) but have no idea how to blend the results here together or pass the datatableid that I can easily attach to the row of tables (the edit button currently throws up an alert with the relevant id...

Related

Generate data when button/link is clicked Django

I'm very new to Django and web apps. I have an HTML page that allows users to search database for player names. An HTML table is returned. Each entry is a player's performance in relation to a game they played. Each game has 10 players associated with it
search_db.html
<h1>Search Results for: {{searched}}</h1>
<table>
{% for player in match %}
<tr>
<td>
<a href=search_player_db/{{player.match_id}}>{{player.match_id}}</a>
</td>
<td>{{ player.name}}</td>
<td>{{ player.role}}</td>
<td>{{ player.win_or_loss}}</td>
</tr>
{% endfor %}
</table>
{{IM TRYING TO GENERATE DATA HERE}}
The match id is a link that brings the user to a page with additional details related to the match
match_details.html
{%block stats%}
<body>
<h1>Winning team</h1>
{%for player in winners %}
<li>
{{player.name}}
{{player.role}}
</li>
{%endfor%}
<h1>Losing team</h1>
{%for player in losers %}
<li>
{{player.name}}
{{player.role}}
</li>
{%endfor%}
</body>
{%endblock%}
Instead of being redirected to a new page when clicking the link, I'd like to load the content from match_details.html into the page below the table on search_db.html through the button/link in search_db.html. That way the user can click through search results
views.py
def search_playerdb(request):
if request.method == "POST":
searched = request.POST['searched']
players = PlayerInfo.objects.filter(player_name__contains=searched)
context={
'searched': searched,
'players': players}
return render(request, 'searchdb.html', context)
else:
return render(request, 'searchdb.html', {})
def display_match(request, matchid):
match = PlayerInfo.objects.filter(match_id=matchid)
winners = match.filter(win_or_loss=True)
losers = match.filter(win_or_loss=False)
context = {
'match': match,
'winners': winners,
'losers': losers,}
return render(request, 'match_details.html', context)
In order to do this you'll need to use Javascript to make an AJAX call. In case you're unaware, an AJAX call allows a web page to send or receive data without having to refresh the page.
This can be done using pure javascript - Example https://www.w3schools.com/xml/ajax_intro.asp
Or you could use a library to abstract some of the complexity away. One example of this would be JQuery https://www.w3schools.com/jquery/ajax_ajax.asp
In either case, you will be calling a new URL on your site.

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.

Delete individual SQLAlchemy row from an HTML table with flask

I've created a HTML table that lists many rows from a SQLAlchemy table. The table is created with a loop using jinja2 templates:
{% for single_merchant in merchants %}
<tr>
<td>{{single_merchant.id}}</td>
<td><button type="button" class="btn btn-sm btn-outline-danger">Delete</button>
</tr>
Each of the rows has a "delete" button. Im am trying to figure out how can I assign the delete button to delete that particular SQLalchemy row. I tried creating the button as a separate flask form called delete_form and adding an id="{{single_merchant.id}} attribute to the button like so:
{% for single_merchant in merchants %}
<tr>
<td>{{single_merchant.id}}</td>
<form method="post">
{{ delete_form.hidden_tag() }}
<td>{{ delete_form.delete(id=single_merchant.id) }}</td>
</form>
</tr>
In the app.py I then created an if statement:
if delete_form.validate_on_submit():
print(f"merchant to delete ID - {delete_form.delete.id}")
I was hoping to get the single_merchant.id in the output and use it in the if statement to delete the particular merchant from my SQLAlchemy table, but instead I got in the output merchant to delete ID - delete even though from the HTML file the id attribute had a value of 1 since {{single_merchant.id}} is 1
Is there a way how to execute SQLAlchemy row deletion from HTML tables? Here's a rough example how the HTML table is expected to look like:
Implement a route to handle the deletion:
def merch_delete(mid):
merch = Merchant.query.filter_by(id=mid).first()
if merch:
msg_text = 'Merchant %s successfully removed' % str(merch)
cs.delete(merch)
cs.commit()
flash(msg_text)
return redirect(url_for('merchants_view'))
Then, add the following to a column in your jinja table:
<a href="{{ url_for('merch_delete', mid=single_merchant.id) }}"
onclick="return confirm('Do you want to permanently delete merchant {{ merchant }}?');" title="Delete Merchant">
<i class="material-icons" style="font-size:16px">delete</i></a>
The icon and Js verification step are optional.
You can use your original approach as well just add a hidden field to the form that has your delete button in which the value could be your id. You just have to then explicitly define the csrf_token rather than using the hidden_tag() method.
In your WtfForm class object define a hidden field like:
class DeleteForm(FlaskForm):
delete_id = HiddenField("Hidden table row ID")
delete = SubmitField("Delete")
Render it in your HTML Jinja2 template like this. Notice I flipped the <td> and the <form> so that your whole table data cell is the form
{% for single_merchant in merchants %}
<tr>
<td>{{single_merchant.id}}</td>
<td>
<form method="post">
{{ delete_form.csrf_token }}
{{ delete_form.delete_id(value=single_merchant.id) }}
{{ delete_form.delete(class="btn btn-danger") }}
</form>
</td>
</tr>
Then in your code you can easily check it using the wtfform validate_on_submit() and then use the SQLAlchemy get() method to query for that id pulling the value from the hidden fields data attribute.
def post(self):
if self.delete_form.validate_on_submit():
entry_to_delete = YourSQLAlchemyTableClassObject.query.get(self.delete_form.delete_id.data)
db.session.delete(entry_to_delete)
db.session.commit()
You could if you wanted to combine the entry_to_delete line with the db.session.delete() by just putting the query to your table using the hidden field data property into the call to the delete() method. I like to separate those pieces in case I need to do anything additional to the found entry before I delete it but it doesn't matter.
Here is a screenshot of one of my apps I built for my family that allows my kids to add suggestions for our Disney Vacations. I didn't use a cool trash can icon like you did but you get the point.

Django: Pagination without changing url?

I learned the pagination basics for Django from this link:
https://simpleisbetterthancomplex.com/tutorial/2016/08/03/how-to-paginate-with-django.html
However! the only problem I have is that I want to paginate without changing url.
By that, I mean I need to paginate a table while there are multiple tables in a html page.
How do i paginate only one table???
My code:
This is the basic structure of the html template:
...
<table class="uk-table uk-table-striped">
<tbody>
{% for post_status in post.statusPage %}
<tr>
<td>{{ post_status.status_time }}</td>
<td>{{ post_status.repost_count }}</td>
<td>{{ post_status.comment_count }}</td>
<td>{{ post_status.like_count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if post.statusPage.has_other_pages %}
<ul class="uk-pagination uk-flex-center" uk-margin>
{% if post.statusPage.has_previous %}
<li><span uk-pagination-previous></span></li>
{% else %}
<li><a><span uk-pagination-previous></span></a></li>
{% endif %}
{% for i in post.statusPage.paginator.page_range %}
{% if post.statusPage.number == i %}
<li class="uk-active"><span>{{ i }}</span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if post.statusPage.has_next %}
<li><span uk-pagination-next></span></li>
{% else %}
<li><a><span uk-pagination-next></span></a></li>
{% endif %}
</ul>
{% endif %}
In side the template, there are multiple post objects
post
Each post has its own table and paginator:
post.statusPage
is the page object of the post.
Rather than building the pagination yourself and dealing with the complexity you're running into, consider using DataTables.
You can place multiple tables on a single view, and the pagination for each table is handled via javascript with no actual change in the URL. You can either insert the values into the table using standard django template tags, or you can build a simple API (using DRF or django's JSON libraries) to feed the tables via Ajax. This is a bit more advanced, but when you have a lot of data, it ensures you aren't sending it all to the user at once and hogging system memory.
I am not sure I know what you're trying to do, but it seems like your view returns a full set of tables, and you'd like the user to be able to view them one at a time, with a click in between, instead of scrolling down forever. You don't want to hit a different URL, so this needs to be handled in the client browser, and that means this is not a Django problem and hence not a Django solution. (Calling it pagination but also wanting it all to appear at the same URL and hence be the same web page is slightly confusing.)
The simplest way from where you are now might be toggling 'show-hide' html divs, that with a button created for each page. So each page is rendered but selectively presented. You'd have to modify the little script there to set all other pages to hidden.
I'm sorry I haven't got a better example for your particular situation. There are probably a bunch of more elegant javascript-heavy solutions out there, but I can't advise you on those.
If you are willing to send querystrings with a Get request for the next page, you could solve this entirely with Django, by creating links that include '?param=' type page numbers. The parameters can then be obtained from 'request.GET.getlist('param')' to return the right page. But if your view returns everything now, you might not want to get into generating and handling querystrings.

django input element error css class

I'd like to know how can I add .error class to input elements (to registration app) when the form validation fails.
If you want to place your error CSS class to form input widgets (not their containers), you can derive your form class from the following one:
class StyledErrorForm(forms.Form):
def is_valid(self):
result = super().is_valid()
# loop on *all* fields if key '__all__' found else only on errors:
for x in (self.fields if '__all__' in self.errors else self.errors):
attrs = self.fields[x].widget.attrs
attrs.update({'class': attrs.get('class', '') + ' is-invalid'})
return result
It's now easy -- new feature in Django 1.2
Just add an attribute on the form class & you're good to go. This feature is mentioned in the docs under a "new in 1.2" note, but you can find the magic at django.forms.forms.BoundField.css_classes Here's the API reference, and an example:
class MyForm(forms.Form):
required_css_class = "required"
error_css_class = "error"
This can be done completely through your template.
You build the form template for each form field that you want to test you can use the following example construct
<input type="text" class="reg-txt{% if form.fieldname.errors %} errors{% endif %}"/>
This lets you provide the interface you want without modifying the view & django form code.
Using a Custom Template...
Personally never had much luck using the built in Django error classing solutions, and besides, I like to use the built in 'striptags' template filter on the errors, to get rid of all the html list stuff which I cant figure out how to render nicely anyway.
I use the following custom template to class them as 'error_id'.
#register.filter(is_safe=True)
#stringfilter
def error_id(value):
if value=='':
return ''
else:
return r'<span class="error_id">'+value+'</span>'
Render the individual errors in your template using:
{{ form.my_field.errors|striptags|error_id}}
Or render the whole form using something like:
<table border="1" cellpadding="5px" align="center">
{% for field in form.visible_fields %}
<tr>
<td> {{ field.label_tag }}: </td>
<td> {{ field }} </td>
<td> {{ field.errors|striptags|error_id }} </td>
</tr>
{% endfor %}
</table>
(Better late than never)
You should be able to do this with Django Uni Form

Categories