Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ModelForm

I am trying to create a simple CRUD with ModelForm. It works fine except that every time I edit, saving creates a new instance of the data. So i edit and get an extra row in DB instead of an updated one. I am at a loss as to how it knows to save an existing charity as it does not store the PK (id) as a hidden field in the form. That is how I always did it before trying to use the 'fabulous' ModelForm!

It's driving me nuts, I have read everything and as far as I can tell I am doing everything right.

Here is my code..

Model:

from django.db import models
from django.conf import settings

COUNTRY_CHOICES = settings.COUNTRIES

class Charities(models.Model):
    charity_name            = models.CharField(max_length=100)
    country                 = models.CharField(max_length=4, choices=COUNTRY_CHOICES)
    registration_number     = models.CharField(max_length=100)
    address1                = models.CharField(max_length=100)
    address2                = models.CharField(max_length=100)
    city                    = models.CharField(max_length=30)
    zip                     = models.CharField(max_length=10)
    phone                   = models.CharField(max_length=20)
    email                   = models.EmailField()
    charity_logo_image      = models.CharField(max_length=100)
    charity_banner_image    = models.CharField(max_length=100)
    charity_accepted        = models.IntegerField()

    def __str__(self):
       return self.charity_name

    def __unicode__(self):
        self.charity_name

View:

def list(request):
    charities = Charities.objects.all()
    return render_to_response('charities_charity_list.html', {'charities': charities})

def add(request):
    return add_or_edit(request)

def edit(request, charity_id):
    return add_or_edit(request, charity_id)

def add_or_edit(request, charity_id=None):
    print "ID = " + str(charity_id)  
    form = CharityForm(request.POST or None,
                   instance=charity_id and Charities.objects.get(pk=charity_id))

    # Save new/edited student
    if request.method == 'POST' and form.is_valid():
        print form
        form.save()
        return HttpResponseRedirect('/charities/list/')

    return render_to_response('charities_charity_edit.html', {'form': form})

Form:

class CharityForm(ModelForm):
    class Meta:
        model = Charities

Template:

{% extends "base.html" %}

{% block title %}Charities Add{% endblock %}
{% block content %}

<form method="post" action="/charities/add/" id="save"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}
like image 381
Rich Avatar asked Sep 22 '10 00:09

Rich


1 Answers

It doesn`t work because your template is always POSTing to the view that adds a new Charity. When you manually type a URL like /charities/edit/5, it creates the ModelForm with the right initial data, but then POSTs to /charities/add, thus creating a new instance. You need to POST to /charities/edit/5, for example. Take a look at the url template tag.

I suggest you use 2 templates, one for adding, another for editing. I know it may not be very DRY, but I believe it's clearer this way.

Add template:

{% extends "base.html" %}

{% block title %}Charities Add{% endblock %}
{% block content %}

<form method="post" action="{% url charities_app.views.add %}"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}

Edit template:

{% extends "base.html" %}

{% block title %}Edit Charity{% endblock %}
{% block content %}

<form method="post" action="{% url charities_app.views.edit charity.id %}"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}

You may also want to check the create_object and update_object generic views, they are very useful in simple cases like yours.

like image 141
Tiago Brandes Avatar answered Sep 23 '22 15:09

Tiago Brandes