How to save content with contenteditable attribute back to database(or model instance)?

If I have a <div> with contenteditable attribute in a template like below:

<div contenteditable="true">{{some data from Model}}</div>

I looked on internet and found a way with javascript, or call a API, is there a way come with django itself that when content changed then I am able to save the changed data back to Model instance or save the changed data back to database?

Thanks in advance!

1 Like

I’m not personally familiar with any of this, but I’d say in general, if there’s any way to write JavaScript that takes this data and submits it as a POST request, you’re going to be able to handle it in Django. At the very least, request.body is going to give you access to whatever is submitted.
If the browser can send it, Django will receive it…

1 Like

I am working on this, too. I found a solution. I am also new to django and this solution is not elegant, but maybe other community members can give suggestions. Here is my (generalized) code which allows for the changing of an attribute “name” for a logged in “CustomUser” model object.

Here is the html where the changeable text is.

<!-- detail.html -->
<h2 contenteditable='true' id='name'>{{ object.name }}</h2>
...
<script src="{% static 'js/detail.js' %}" ></script>

Here is the javascript methods that get the new name as the user changes it and send a post request with the changed name. Ideally, this would only happen when the user finishes typing something new and clicks away, but I only figured out how to do it every time the user types a letter. The other thing I wanted to do was send back the old name that was there before (to be certain that the correct field is being changed), but until I fix this previous issue, I don’t think I can do that.

// detail.js
let name = document.getElementById("name")
let csrftoken = getCookie('csrftoken');

name.addEventListener("input", function() {
    let newName = name.textContent  # is this unsafe? I think it might be
    let data = { name: newName }

    fetch('change_name/', { 
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            "X-CSRFToken": csrftoken,
        },
        body: JSON.stringify(data),
        credentials: 'same-origin',
    })
    .then(response => response.json())
    .then(data => {
        console.log('Success: ', data)
    })
    .catch((error) => {
        console.error('My Error: ', error)
    })
}, false);

// The following function are copying from
// https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

Here is the url where the post request goes.

# urls.py
urlpatterns = [
    path(_('<str:pk>/change_name/'), change_name, name='change_name'),
    ...
]

Here is the method that gets the new name and calls a method on the user to change the name.

# views.py
def change_name(request, pk):
    user = get_user_model().objects.get(username=pk)
    data = json.loads(request.body)
    new_name = data['name']
    success = user.change_name(new_name)
    ...
    return HttpResponseRedirect(reverse('detail', kwargs={'pk': pk}))

In my code I did not actually change the name for a user, I did something more specific to the project. But, I think something like this would work.

# models.py
class CustomUser(...):
    ...
    def change_name(self, new_name):
        self.name = new_name
        self.save()
        return self.name == new_name