fmt
fmt

Reputation: 993

Updating certain fields of Django model with ModelForm

I am writing a Django application and need to update a model with an AJAX request, which will only contain a subset of the model's fields as keys. So if I have the model

class TheModel(models.Model):
    a = models.CharField(max_length=16)
    b = models.IntegerField()
    c = models.TextField()
    d = models.ManyToManyField(AnotherModel)

then I could get requests like

id=7&a=Hello
id=7&a=Test&b=123
id=13&b=14&c=Description&d=6&d=10

that is, I will always get the ID field but any subset of the others.

I can't find a "nice" way to do this in Django 1.5: at first I tried

instance = get_instance_or_404(request["id"])
data = django.forms.models.model_to_dict(instance)
data.update(request.POST)
form = TheModelForm(data, instance=instance)
if form.is_valid():
   form.save()
   ...
else:
   ...

but this doesn't seem to work well with the m2m field, and moreover model_to_dict feels incredibly ugly to me. So I also did

instance = get_instance_or_404(request["id"])
for k in TheModel._meta.fields:
    if k in request:
        setattr(instance, k, request[k])
try:
    instance.full_clean()
except ValidationError as e:
    ...
instance.save()

but I don't exactly understand how to handle the m2m fields here either.

Is there an idiomatic way to do this in Django? Thanks in advance for your help.

Upvotes: 1

Views: 820

Answers (1)

Matt
Matt

Reputation: 10312

First of all, GET requests should never update data. They should only ever read and display data. The HTTP method you need to use is POST. This answer is worth a read.

Now that's out of the way, the best way to achieve what you want is by using the generic UpdateView. Here's some sample code:

# urls.py
from views import UpdateTheModelView

urlpatterns = patterns('',
                       url(r'^update-TheModel/(?P<pk>\d+)/?',
                       UpdateTheModelView.as_view(),
                       name='update_the_model'),
)


# forms.py
from django import forms
from models import TheModel

class TheModelForm(forms.ModelForm):
    class Meta:
        model = TheModel
        fields = ('a', 'b', 'c', 'd',)


# views.py
from django.core.urlresolvers import reverse
from django.views.generic import UpdateView
from models import TheModel
from forms import TheModelForm

class UpdateTheModelView(UpdateView):
    model = TheModel
    form_class = TheModelForm
    template_name = 'themodel_form.html'

    def get_success_url(self):
        """
        Just here to redirect back to the update page when the form is posted
        """
        return reverse('update_the_model', args=[self.object.id, ])

And a simple example template to display the form at yourapp/templates/themodel_form.html:

<form action="." method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Update" />
</form>

So now if you have an instance of TheModel saved with an id of 1, you can view the update form loaded with initial data by going to http://your-website.co.uk/update-TheModel/1/ and you can update it by clicking Update or by sending a POST request to this page's URL (along with the CSRF token), which can easily be done in the background with jQuery or vanilla Javascript.

Upvotes: 1

Related Questions