Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating editable HTML tables with Django

I'm trying to create a Django app where the user can make a list of movies. Each time the user logs in, their list of movies will be presented to them in a table. The table will have three columns: one for the movie's name, one for the genre, and another that will contain delete buttons that will allow the user to delete the row corresponding to the button. The user can add rows to the table by filling in a textbox with the name of the movie, and selecting a genre from a drop down menu, and then pressing an "Add" button. The "Add" and "Delete" buttons are the only way by which the user can edit the table.

Is there any Django shortcuts to creating such an editable table? I thought this might be something that is appropriate for formsets, but I can't figure out how to do it. Also, it has been difficult to google "Django tables" as the results seem to be about database tables.

This is the model that I am currently trying to use:

class MovieList(models.Model):
    user = models.ForeignKey(User)
    movie = models.ForeignKey(Movie)

class Movie(models.Model):
    genre = models.ForeignKey(Genre)
    name = models.CharField(max_length=300)

class Genre(models.Model):
    name = models.CharField(max_length=200)

Any help would be very much appreciated.

like image 441
tkon J. Avatar asked May 17 '11 21:05

tkon J.


People also ask

What is Django tables2?

django-tables2 - An app for creating HTML tables django-tables2 simplifies the task of turning sets of data into HTML tables. It has native support for pagination and sorting. It does for HTML tables what django. forms does for HTML forms. e.g.


2 Answers

There are several ways you can solve this. Though it doesn't really use a shortcut i can't see what's wrong about rendering the table by a standard view and a attach a form for entering new entries to it.

If you are looking for a shortcut and some additional convenience django-tables might be for you.

Of course you could use jQuery to make the process even more dynamic so that people can add and delete items without reloading the whole page: Something similar to Django's dynamic inlines in the admin. If you decide to take this route you should take a closer look a django-dynamic-formsets or at one of the several grid-plugins for jQuery. jqGrid to name one.

like image 94
arie Avatar answered Oct 16 '22 06:10

arie


You can use ajax requests and reload only the tbody after the user add or delete a movie, here is a example that i used before

In html template you will have your table and inside the script tag you will have a function to load the table, to delete a movie, and to add a movie

template 1

<table>
    <thead>
        <th>Name</th>
        <th>Genre</th>
        <th>Action</th>
    </thead>
    <tbody id="table_movie_id">
        {% if movies %}
        {% for movie in movies %}
        <tr>
            <td>{{movie.name}}</td>
            <td>{{movie.genre}}</td>
            <td>
                <button value="{{movie.id}}" onclick=deleteMovie(this.value)>
                    delete
                </button>
            </td>
        </tr>
        {% endfor %}
        {% endif %}
    </tbody>
    <tfoot>
        <form method="POST" onsubmit=addMovie(event)>
            {% csrf_token %}
            <td>
                <label>Name</label>
                {{ form.name }}
            </td>
            <td>
                <label>Genre</label>
                {{ form.genre }}
            </td>
            <td>
                <button type="submit">
                    add
                </button>
            </td>
        </form>
    </tfoot>
</table>
<script>
    function loadTable() {
        let xhttpTableMoves = new XMLHttpRequest()
        xhttpTableMovies.onreadystatechange = function (data) {
            if (this.readyState === 4 && this.status === 200) {
                document.getElementById("table_movie_id").innerHTML = this.response
            }
        }
        xhttpTableMoves.open("GET", "./url_to_api/load_table/", true)
        xhttpTableMoves.send()
    }

    function deleteMovie(idMovie) {
        const request = new Request(`./url_to_django/delete-movie/${idMovie}`,
            {
                method: "DELETE",
                headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value }
            }
        )
        fetch(request, {
            method: "DELETE",
            mode: 'same-origin'
        }).then(
            function (response) {
                if (response.status === 200) {
                    loadTable()
                } else {
                    alert("error")
                }
            }
        )
    }

    function addMovie(e) {
        e.preventDefault()
        const dataToBackEnd = new FormData()
        dataToBackEnd.append("name", document.getElementById("id_name").value)
        dataToBackEnd.append("genre", document.getElementById("id_genre").value)
        const request = new Request('./url_to_django/add-movie/',
        {
            method: 'POST',
            headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value },
            body: dataToBackEnd
        })
        fetch(request, {
            method: 'POST',
            mode: 'same-origin'
        }).then(
            function(response) {
                if (response.status === 201) {
                    loadTable()
                } else {
                    alert("error")
                }
            }
        )

    }
</script>

And in another template you will have only the tbody part

template2.html

<tbody id="table_movie_id">
    {% if movies %}
    {% for movie in movies %}
    <tr>
        <td>{{movie.name}}</td>
        <td>{{movie.genre}}</td>
        <td>
            <button value="{{movie.id}}" onclick=deleteMovie(this.value)>
                delete
            </button>
        </td>
    </tr>
    {% endfor %}
    {% endif %}
</tbody>

In your urls you have to set routes for this ajax calls

urls.py

urlpatterns = [path(
        'url_to_django/delete-movie/<int:idmovie>',
        views.delete_movie, 
        name='delete_movie'),
    path(
        'url_to_django/add-movie/',
        views.add_movie, 
        name='add_movie'),
    path(
        'url_to_django/load_table/',
        views.load_table, 
        name='load_table')]

And in your views.py you will have something like

    def load_table(request, codorcam):
        if request.method == "GET":
            if request.user:
                movies = [] # here your movie query to database
                return render(request, 'orcs/template2.html', {"movies": movies})
            else:
                return HttpResponse(status=403)
        else:
            return HttpResponse(status=405)

    def delete_movie(request, idmovie):
        if request.method == "DELETE":
            if request.user:
                movie_to_delete = Movie.objects.get(id=idmovie)
                movie_to_delete.delete()
                return HttpResponse(status=200)
            else:
                return HttpResponse(status=403)
        else:
            return HttpResponse(status=405)

def add_movie(request, codorcam):
    if request.method == "POST":
        if request.user:
            form = formMovie(request.POST) #the form created in forms.py to add the movie
            if form.is_valid():
                name = form.cleaned_data['name']
                genre = form.cleaned_data['genre']
                new_movie = Movie(
                    name=name,
                    genre=genre,
                )
                new_movie.save()
                return HttpResponse(status=201)
            else:
                return HttpResponse(status=400)
        else:
            return HttpResponse(status=403)
    else:
        return HttpResponse(status=405)

You can also reload only the table row of reload the tbody if you want

like image 26
Luiz Avatar answered Oct 16 '22 06:10

Luiz