views:

56

answers:

1

Hi all,

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 %}
+1  A: 

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.

Tiago Brandes
Hi Tiago, thanks for replying. I hadn't heard of the generic_update / create views before. i will read up on them. Just trying to get the existing method working, I can't seem to extract the ID from charity.id so as to append it to the edit URL. I tried form.id as well but it's just not in context. Also, what is charities_app? is this you just putting in a placeholder for me to substitute? In my member view I tried using {% url race.members.views.edit member.id %} but got error............... Caught ViewDoesNotExist while rendering: Tried home in module race.charities.views.
Rich
I stupidly forgot I could pass the member/ charity Id in via the context. So i now have the ID in the edit URL and it works of course. Thanks. Now i am just trying to understand how to replace the action URL with similar to your example {% url charities_app.views.edit charity.id %} . What is charities_app. Should it be the path to the view? do I need to have get_absolute_url defined?
Rich
Hi, in this case charities_app would be the name of the app that contains the Charities model. charities_app.view.edit is just the name of the view that's going to process the request. This url tag is useful because you don't have to hardwire the urls in your templates.. That way, if you choose to change the url mappings in urls.py, you don't need to update the templates :)
Tiago Brandes