Seth
Seth

Reputation: 6832

What is the correct way to construct a Django ModelForm in a create/edit view?

Consider a model:

class MyModel(models.Model):
    token = models.CharField(unique=True, db_index=True, max_length...)
    name = models.CharField(...)
    ...

(Aside: The purpose of the token is to be an alternative to displaying and using the ID in URLs; it is not the primary key.)

And its form:

class MyForm(forms.ModelForm):
    ...    
    class Meta:
        model = models.MyModel
        fields = '__all__' # Django 1.6

And its template:

...
<form action={% url 'create_or_edit_mymodel' %} ...>{% csrf_token %}

    {{ form.token.as_hidden }}

    <label for="id_name">Name:</label>
    {{ form.name }}
    ...

And, finally, its view:

def create_or_edit_mymodel(request, token=None):

    # [A] Entering via a hyperlink with the token, editing existing model
    if token: 
        m = models.MyModel.objects.filter(token=token).first()
        form = forms.MyForm(instance=m)

    # [B] Postback from form
    elif request.POST: 
        form = forms.MyForm(request.POST)

    # [C] Entering via a hyperlink without the token, creating new model
    else:
        m = create_new_mymodel(...) # somewhere else
        form = forms.MyForm(instance=m)

    if request.method == 'POST' and form.is_valid():
        saved = form.save()
        # Determine if 'Save' or 'Save & Close' was clicked... assume 'Save'...
        form = forms.MyForm(instance=saved)

    return shortcuts.render(request, '...', { 'form': form }, context_instance=RequestContext(request))

This doesn't work. The problem is that the model's ID doesn't seem to be available to Django, so entering the view at [A] populates the form with everything as expected, but clicking 'Save' and entering the view at [B] attempts to save a model with no ID, and the unique constraint on the 'token' field fires.

I tried adding the id field to the form:

{{ form.id.as_hidden }} # or...
{{ form.pk.as_hidden }}

But nothing gets rendered.

That view looks pretty uncomfortable to me, so I'm hoping I'm making this harder than it needs to be.

Upvotes: 1

Views: 121

Answers (1)

ndpu
ndpu

Reputation: 22561

Here you should pass both request.POST and instance to form init:

# [B] Postback from form
elif request.POST: 
    form = forms.MyForm(request.POST, instance=instance)

Upvotes: 2

Related Questions