Django framework, crud in grid row - javascript

I’ve been developing an app in djanog 3.0 for my wife. Got a nice modal ajax crud up and running for the grid, but she does not like the modal effect, she just wants to edit inside the row and always have an empty row at the bottom for new entries.
I’ve looked at table2 and jqgrid but have not been able to find something that works like that. I’ve now been playing around with editablegrid.net js grid and I can display data and edit, but not save the edited data. Editablegrid is a good example of what my wife would like to do, without the empty new row, but should be able to hack that in.
Obviously I’ll not be able to make a row a from, so I need to figure out how to make my data serial in something like json. I then also need the CSRF token as part of the json right?
I’m way out of my depth as I develop embedded c for a living and this all is self taught as we go.
Questions are:
What is the best grid for something like this? Is it even possible?
Is django even suited for something like this?
Should I pivot and go with rest api and look at some other tech like react?
EDIT 1:
I tried #Raiyan suggestion like follows:
{% for person in persons %}
<form id="F{{ person.pk}}" method="post" action="{% url 'person-update' person.pk %}" class="js-ajax-update-form" update-table-name="ajax-table">
{% csrf_token %}
<tr>
<td>{{ person.title }}</td>
<td>{{ person.first_name }}</td>
<td>{{ person.last_name }}</td>
<td>{{ person.id_number }}</td>
</tr>
</form>
{% endfor %}
but if I open page and look at html it looks like this:
<form method="post"></form>
<input type="hidden" name="csrfmiddlewaretoken" value="IREdUDr8bplgvDrtmMSSjOua2NL98SXnoeIGdX1mN0nY2hpQTByW0FfJZxvO5kCw">
<tr id="R2">
<td>Mr.</td>
<td>John</td>
<td>Doe</td>
<td>123456789</td>
</tr>
</tbody>
For some reason the form tag gets closed before the table row and input. I've also adapted a simple CRUD books app to try and get the desired behavior with none of the fluf, but there also the form tag closes immediately.
For the CRUD app I changed the fbv view to:
def book_list(request, template_name='books_fbv/book_list.html'):
books = Book.objects.all()
forms = []
for index, book in enumerate(books):
forms.append(BookForm(request.POST or None, instance=book, prefix="form_{}".format(index)))
data = {}
data['object_list'] = forms
if request.method == 'POST':
for form in forms:
if form.is_valid():
action = form.save(commit=False)
action.save()
return render(request, template_name, data)
and html to:
<table>
<thead>
...
</thead>
<tbody>
{% for book in object_list %}
<form method="POST">
{% csrf_token %}
<tr>
<td>{{ book.name }}</td>
<td>{{ book.pages }}</td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
Any idea why form tags close like that?

Django is very well suited for projects like this.
Obviously I’ll not be able to make a row a from
I think you can, unless I misunderstood something. You can render each row as a separate form, using Django templates. The implementation may look like this:
{% for row in rows %}
<form method="post">
<tr>
<td>...</td>
...
</tr>
</form>
{% endfor %}
You are right that you need to handle the CSRF token. Django allows you to insert CSRF token anywhere in the rendered HTML using the {% csrf_token %} in your template. A very simple form would look like this:
<form method="post">{% csrf_token %}... </form>
In your case, each of the row-forms would then look like:
{% for row in rows %}
<form method="post">
{% csrf_token %}
<tr>
<td>...</td>
...
</tr>
</form>
{% endfor %}
Read more about it here:
https://docs.djangoproject.com/en/3.0/ref/csrf/#how-to-use-it
Hope this helps.

I agree with everything that #Raiyan said, but just wanted to give my input on this as well.
she just wants to edit inside the row and always have an empty row at the bottom for new entries.
Sounds like your wife wants something very responsive and dynamic, which for me, it means that the frontend (or client-side) should be more active. Although you can achieve a similar result with Django or other "server render" options, using Rest API and Javascript will give you much more flexibility.
React is an example (as you mentioned), but depending on your project, you don't need to go there. You can still use Django to create your API and simple Javascript (or even jQuery) to call the API. This would allow you to create new rows dynamically, delete data and manipulate the content without reloading the page (which I believe is the smooth effect desired).
Here are some references:
AJAX in jQuery: https://api.jquery.com/jquery.ajax/
Rest API in Django: https://medium.com/#BennettGarner/build-your-first-rest-api-with-django-rest-framework-e394e39a482c

Related

How to send Jinja variables to Django View

I have a view in HTML whereby it displays a list of table with attendees;
<table>
{% for people in Attendees %}
<tr>
<td>{{ people.id }}</td>
<td>{{ people.name }}</td>
<td>
<a id='deleteAttendeeButton' class="btn btn-primary btn-xs">Delete</a>
</td>
</tr>
{% endfor %}
</table>
The table has a delete button for all entries and when clicked, I am using AJAX for a GET request as per below;
$("#deleteAttendeeButton").on("click", function () {
$.ajax({
url: '/modifyAttendee/?id={{ people.id }}',
type: 'GET',
})
});
I want to use AJAX to send the people.ID variable to view so that the view can determine what object to delete in the database. However, the issue is I cannot seem to pass in a jinja variable in AJAX.
Am I missing something in my AJAX statement? If not, what would be an ideal solution?
Note: I do not want to use 'href' in the button because I do not want to reload the page.
Well, it doesn't work like that. The template tags are evaluated at render time, but you need the ID of the element that was clicked. You can use the data element to hold the ID and get it back in the JS.
There are a few other things wrong here too; in particular, HTML element ids need to be unique, but you are using the same one for every link in the loop. You should use a class instead.
<a class='deleteAttendeeButton btn btn-primary btn-xs' data-people_id="{{ people.id }}">Delete</a>
...
$(".deleteAttendeeButton").on("click", function () {
var people_id = $(this).data('people_id');
$.ajax({
url: '/modifyAttendee/?id=' + people_id,
type: 'GET',
})
(Note, unless you have specifically configured it, Django does not use Jinja; this is almost certainly Django template language which is similar but not the same. It doesn't affect your problem, though.)

How to save multiple forms in on page in Django

I am trying to make a betting app in Django. After a user signs in, there's a page with several forms, each responsible for saving the result of a match. Each form takes two integer inputs (number of goals for each team). I want to have a save button at the end of these forms such that when clicked, the input data are recorded in the database. I have two models, Game and Bet. Game is responsible for storing the actual result of games, while Bet is responsible for recording user predictions.
class Game(models.Model):
team1_name = models.CharField(max_length=100)
team2_name = models.CharField(max_length=100)
team1_score = models.IntegerField()
team2_score = models.IntegerField()
class Bet(models.Model):
user = models.ForeignKey(User)
game = models.ForeignKey(Game)
team1_score = models.IntegerField()
team2_score = models.IntegerField()
And here's the main page
{% for game in games %}
<form action="../place_bet/" method="post">
{% csrf_token %}
<table>
<tr>
<th class="table-col"><label for="team1_score">{{ game.team1_name}}</label></th>
<th class="table-col">{{ form.team1_score }}</th>
</tr>
<tr>
<td class="table-col"><label for="team2_score">{{ game.team2_name}}</label></td>
<td class="table-col">{{ form.team2_score }}</td>
</tr>
</table>
<input type="submit" value="Submit" id="submit-button"/>
</form>
{% endfor %}
My question is how I can capture the input fields for different forms in the place_bet view that is triggered when submit button is clicked.
For a throughly answer about how to process multiple repeated fields with one form on Django, please read this answer history. This will be an answer on good practices to process many forms from one page in Django.
So there's a recipe for this, what we will need is the following:
A View, it can be class-based or function-based, in this example, I'll use class-based because it's tidy.
An URL where this View is served, the only special thing about it is an optional parameter added to the end of it.
A template with the correct setup for calling the right view function.
Optionally, you can use a Form to validate the data, but it's not a requirement.
So, the first things first, let's create the View. This will separate concerns to better readability.
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.http import HttpResponseBadRequest
class PlaceBet(View):
template_name = 'place_bets.html'
context = some_base_context_dict
def get(self, request):
# the user is requesting the game list
self.context['games'] = Game.objects.all()
return render(request, self.template_name, self.context)
def post(self, request, game_id=None):
# the user is submitting one of game's predictions
if not game_id:
return HttpResponseBadRequest('hey, send a game id, you fool!')
game = get_object_or_404(Game, pk=game_id)
# here you can use a Form to validate the data, but hey,
# do your homework
bet = Bet.objects.create(user=request.user, game=game,
team1_score=request.POST.get('team1_score'),
team2_score=request.POST.get('team2_score'))
# bet is saved at this point so now we can now take the user
# elsewhere or i dunno, the same page...
self.context['games'] = Game.objects.all()
self.context['new_bet'] = bet
response = render(request, self.template_name, self.context)
# it's good practice to say you created something
response.status_code = 201
return response
Now, the Urls need some work too, you're passing a parameter, so...
urlpatterns = [
url(r'^place_bet$', PlaceBet.as_view()),
url(r'^place_bet/(?P<game_id>[^/.]+)', PlaceBet.as_view(), name='place-bet') #name parameter, very important for the future...
]
Your template is almost right, but it needs some work:
{% for game in games %}
<!-- the action now points to the URL by name, and passes the game_id -->
<form action="{% url 'place-bet' game.pk %}" method="POST">
{% csrf_token %}
<table>
<tr>
<th class="table-col">
<label for="team1_score">{{ game.team1_name}}</label>
</th>
<th class="table-col">
<!-- you need something to submit -->
<input type="number" name="team1_score" value="{{ game.team1_score }}">
</th>
</tr>
<tr>
<td class="table-col">
<label for="team2_score">{{ game.team2_name}}</label>
</td>
<td class="table-col">
<input type="number" name="team2_score" value="{{ game.team2_score }}">
</td>
</tr>
</table>
<input type="submit" value="Submit" id="submit-button"/>
</form>
{% endfor %}
And there it is, now when you press the Submit button, the browser will send the POST data to the post method of the view, using an action URL that encodes the game's ID, so there's no room for mistakes.
This code can be improved a lot, but it will get you going.

JQuery - autorefresh part of a page

In my Django project, I have a page that displays all the instances of a particular model. I want to auto-refresh the page every few seconds, but only update the relevant part of the page.
I'll describe each part. First - I have a Django view. In its simplest form, it looks like this
class MyListView(ListView):
lookup_field = 'uuid'
model = Foo
template_name = 'mylist.html'
def get_queryset(self):
return list(Foo.objects.all()) # finds all my objects from DB
def get_context_data(self, **kwargs):
objects = self.get_queryset()
context = super(MyListView, self).get_context_data(**kwargs)
context['foos'] = objects # this gets passed to html page
return context
So it finds all Foo objects from the database and returns it to the html.
The html page displays a table, where each entry is a row. Furthermore, there is a checkbox at the beginning of each row. Like this
<table id="content">
<thead>
<tr>
<th><input type="checkbox" id="chckHead"/></th>
<th>UUID</th>
<th>Attribute1</th>
<th>Attribute2</th>
</tr>
</thead>
<tbody>
{% for item in foos %}
<tr id="{{ item.uuid }}">
<td>
<input type="checkbox" name="checkboxlist" class="chcktbl" />
</td>
<td><code>{{ item.uuid }}</code></td>
<td>{{ item.attribute1 }}</td>
<td>{{ item.attribute2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Now, some of the attributes of the Foo model may get updated in the background, so I want to somehow refresh the page periodically with new data.
I did that in javascript, like this
function setup() {
setTimeout("refreshPage();", 5000);
}
function refreshPage() {
window.location = location.href;
}
$(document).ready(setup);
So every 5 seconds, JS would call refreshPage(), and my page gets refreshed. If the database was changed behind the scenes, my page would reflect it.
The problem with the above is that it refreshes the whole page, which includes the checkbox. So if I had already selected a checkbox, the refresh would reset it to its original state ('not checked').
So, what is the correct way to solve this problem?
Just really high level:
The window.location that you're adding is going to reset the whole page. By this time you've already figured that out. What you're looking for is something could be handled by using a page partial or by using a REST endpoint and pushing it back through the template.
Here are the steps you'll want refreshPage to preform instead.
Save off any values that you think are important
Send an ajax request to get either the partial or json
Render that new information to the page
Put back the values from step one over the values rendered in step 3
More likely you're going to want to wait to do the refreshPage if they're currently editing the area (saves you step 1 and 4). That, may be a choice you've already voted against.
I hope that helps, it's a very non-technical answer but I'm not sure I could give you a very specific answer without knowing A LOT more about your project.

Javascript validation for dynamically generated forms in Django template

I am struggling with something that is probably very basic: I need to generate a form with marks for my University database application. Each student in each module has a class got "Performance" that stores all the marks for the module. There are different assessments and the Performance class calculates the average for them.
I need to be able to enter, for example, all the marks for the first assessment, and I did that with a dynamically generated Django Form as a table in the template:
{% for performance in performances %}
<tr>
<td>
{{ performance.student }}
</td>
<td>
{% if to_mark == 1 %}
<input type="text" class="input-mini" name="{{ student.student_id }}" value="{{ performance.assessment_1 }}">
{% else %}
{{ performance.assessment_1 }}
{% endif %}
</td>
And the same for the other assessments (to_mark gets passed on by views.py to indicate which assessments needs to be marked here)
I have failed to use Inlineformfields and therefore decided to generate a form dynamically and validate it with Javascript, also because the validation is simple (has to be a number between 1 and 100), and also because I want to use Javascript to calculate the average on the fly.
My problem is that I have no clue about Javascript. All the tutorials (like this one http://www.w3schools.com/js/js_form_validation.asp) use the name of the form field in the Javascript function, but in my case that name is dynamically generated, so that I can access it easily in the views.py:
if student.student_id in request.POST:
tmp = request.POST[student.student_id]
try:
mark = int(tmp)
if mark in range(0, 100):
performance = Performance.objects.get(student=student, module=module)
if to_change == 1:
performance.assessment_1 = mark
...and so on for the other assessments
except ValueError:
pass (in case someone accidentally enters a letter and not a number)
Is there a way I can use Javascript to address my form fields? Or should I use a different approach than taking the student_id as the name to read it out? How could I do that?
Thanks,
Tobi
There are at least 3 ways to get to the form fields using JavaScript:
By ID, by CSS class or by DOM traversal. If you're not familiar with JavaScript, I would suggest finding a django-friendly client-side form validator like: https://code.google.com/p/django-ajax-forms/

Rerender a HTML template fragment or section after a successful AJAX post

I am trying to implement a feature and I have no idea how to go about it. My project leverages Django to implement backend logic and templating and Google Closure javascript library for front end.
My use case is given below:
I use a "rate"value to calculate the Total Amount for numerous entities. The entities are displayed in tabular format as follows:
<!-- I need to reload this table again. -->
<table class = "striped" ....>
<thead>
<tr>
<th>Entity</th>
<th>Amount (USD)</th>
<th>Rate (%)</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td>{{ entity.name }}</td>
<td>${{ entity.total }}</td>
<td>{{ entity.rate }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The rate is available in the database table to calculate the total amount. But I have to do the following "what-if" scenario; What would the total be if the "rate" was changed?
I have an input box that accepts new rate as follows:
<label for="Updated Rate">
<input type=number id="updatedRate" name="updatedRate" value="0" />
<input type="button" value="Update Rate" />
<input type="button" value="Recalculate Entities"/>
<input type="reset" value="Reset rate and Recalculate"/>
I store the update rates value to windows local storage when the user clicks the Update Rate button, then. If the user clicks the Recalculate Entities button, then I would like to make a AJAX request to the back end to recalculate the updated value and re render the table with the updated values. If the user resets the rate value, then the recalculated values must be In this event, the rate used would be fetched from the database and the table would have to re rendered.
I know how to make a AJAX request to the back end to recalculate the data.but I don't know how to re render the table if the "rate" has been updated or reset.
You need a way to identify the entity you are calculating for. Something like:
{% for entity in entities %}
<tr data-id="{{ entity.id }}">
<td>{{ entity.name }}</td>
<td class="total">${{ entity.total }}</td>
<td>{{ entity.rate }}</td>
</tr>
{% endfor %}
Then in the callback of the ajax replace the total:
$.ajax({
url: 'someurl',
success: function(json) {
$('tr[data-id=' + json.id + '] td.total').text(json.total);
}
});
Of course the actually structure of the returned data will vary. But I think you've got the basic structure.
That help?

Categories

Resources