Save Changes to Database using Django and x-editable
Asked Answered
M

2

4

I am new to JS, HTML, Django and all that related stuff, and I was not able to solve my problem on my own by reading documentation or using Google.

I want to save inline-changes using x-editable to the database in an django environment. The template below works perfectly, but now I would like to overwrite the database entries with the new names.

I tried to reduce the code to the relevant parts of my problem.

models.py:

class MyClass(models.Model):
      name = models.CharField(max_length=120)

views.py:

def list(request):
    return render(request, 'list.html', {'list':MyClass.objects.all()}

urls.py:

url(r'^list/$', 'myapp.views.list', name='list'),

list.html:

{% extends "base.html" %}
{% block content %}
<script>
    $(document).ready(function() {
        $.fn.editable.defaults.mode = 'inline';   
        $('.name').editable({

        });
    });
</script>

<h1>Names</h1>
   <div>
        <table border='1'>
            {% for l in list %}
            <tr><td><a class="name">{{ l.name }}</a></td></tr>
            {% endfor %}
        </table>
    </div>

{% endblock %}

--------------------------------------------------------

My most promising approach was to create an update view.

def list_update(request, pk):
    l = get_object_or_404(MyClass, pk=pk)
    form = ListForm(request.POST or None, instance=l)
    if form.is_valid():
        form.save()
        return redirect('list')
    return render(request, '', {'form':form})

and add the following lines to the code above:

urls.py
    url(r'^update/(?P<pk>\d+)$', 'myapp.views.list_update', name='list_update'),


list.html
    $('.name').editable({
        pk: l.pk,
        url: '{% url 'list_update' l.pk%}',
    });

But this attempt results in an NoReverseMatch error and l.pk seems to be empty. I appreciate any help on how to do this in the right way.

Massacre answered 6/8, 2015 at 10:4 Comment(1)
I face similar issue, is there any chance you can update us on it to let us know if you managed to solve the problem ?Randeerandel
N
2

urls.py:

    url(r'^xed_post$', views.xed_post, name='xed_post'),

Django view code (function based) in views.py:

from django.http import JsonResponse

def xed_post(request):
    """
    X-Editable: handle post request to change the value of an attribute of an object

    request.POST['model']: name of Django model of which an object will be changed
    request.POST['pk']: pk of object to be changed
    request.POST['name']: name of the field to be set
    request.POST['value']: new value to be set
    """
    try:
        if not 'name' in request.POST or not 'pk' in request.POST or not 'value' in request.POST:
           _data = {'success': False, 'error_msg': 'Error, missing POST parameter'}
          return JsonResponse(_data)

        _model = apps.get_model('swingit', request.POST['model'])  # Grab the Django model
        _obj = _model.objects.filter(pk=request.POST['pk']).first()  # Get the object to be changed
        setattr(_obj, request.POST['name'], request.POST['value'])  # Actually change the attribute to the new value
        _obj.save()  # And save to DB

        _data = {'success': True}
        return JsonResponse(_data)

    # Catch issues like object does not exist (wrong pk) or other
    except Exception as e:
        _data = {'success': False,
                'error_msg': f'Exception: {e}'}
        return JsonResponse(_data)

then, in list.html:

{% extends "base.html" %}
{% block content %}
<script>
    $(document).ready(function() {
        $.fn.editable.defaults.mode = 'inline';   
        $('.name').editable({
            params: {
                csrfmiddlewaretoken:'{{csrf_token}}',
                model:'MyClass'
            },
            url: '/xed_post',
            error: function(response, newValue) {
                return response.responseText;
            },
            success: function(response, newValue) {
                if(!response.success) return response.error_msg;
            }
        });
    });
</script>

<h1>Names</h1>
   <div>
        <table border='1'>
            {% for l in list %}
            <tr><td><a class="name" data-name="name" data-type="text" data-pk="{{ l.pk }}">{{ l.name }}</a></td></tr>
            {% endfor %}
        </table>
    </div>

{% endblock %}

Note that using "name" for both the css class and the Django field name is confusing. data-name="name" refers to the Django field name, while class="name" refers to the css class.

Nonsuch answered 11/12, 2018 at 6:7 Comment(2)
Thanks. This is genius. Upvoted! Coding with it right now...Ardithardme
Has CSRF issues. Doesn't work on my setup, tried a million things, read the Django article. Any guidance?Ardithardme
A
0

Davy's code doesn't work as is (for me).

But if you add:

$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});     

Before everything then it does, as long as you have {% csrf_token %} somewhere in the html template. I simply put it at the top since I'm using X-editable's and there's no form tags in the template.

So add that {% csrf_token %} line somewhere and change Davy's JS code to:

{% extends "base.html" %}
{% block content %}
<script>
    $.ajaxSetup({
        data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
    });             

    $(document).ready(function() {
        $.fn.editable.defaults.mode = 'inline';   
        $('.name').editable({
            params: {
                model:'MyClass'
            },
            url: '/xed_post',
            error: function(response, newValue) {
                return response.responseText;
            },
            success: function(response, newValue) {
                if(!response.success) return response.error_msg;
            }
        });
    });
</script>

And it should work. Note that I removed the {{ csrf_token }} line from the params dictionary since the AJAX setup call takes care of it so it's not necessary (at least for my setup).

Ardithardme answered 17/1, 2021 at 5:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.