jduncan
jduncan

Reputation: 677

Django Form Primary Key on save() method - getting NoneType traceback

I was using the q&a at Get Primary Key after Saving a ModelForm in Django.

It's exactly on point with what I need to do.

I have the following model:

class meetingEvent(models.Model):
    '''
    A meeting event
    '''

    name = models.CharField(max_length=64, help_text="a name for this meeting")
    account_number = models.ForeignKey(account)
    meeting_type = models.ForeignKey(meetingType)
    meeting_group = models.ForeignKey(meetingGroup)
    start_time = models.DateTimeField(help_text="start time for this event")
    end_time = models.DateTimeField(help_text="end time for this event")
    created_time = models.DateTimeField(auto_now_add=True)
    listed_products = models.ForeignKey(product)
    additonal_notes = models.TextField(help_text="additional notes for this meeting")

    def __unicode__(self):
        return self.name

I have the following form:

class meetingEventForm(forms.ModelForm):
    """
    to create a new meeting event.
    """
    portal_user = forms.CharField(help_text="username to access portal data")
    portal_pass = forms.CharField(widget=forms.PasswordInput, help_text="password to add access portal data")

    def save(self, commit=True):

        super(meetingEventForm, self).save(commit=commit)

    class Meta:
        model = meetingEvent

I have the following view:

def meeting_event(request):

    if request.method == 'POST':
        form = meetingEventForm(request.POST)
        if form.is_valid():
            new_agenda=form.save()

            return HttpResponseRedirect(reverse('agenda_detail', args=(new_agenda.pk,)))

    else:
        form = meetingEventForm()
        return render_to_response('agendas/event.html',{'form':form,}, context_instance=RequestContext(request))

I've confirmed that this makes it into the database cleanly. However, I get the following error:

Traceback:
File "/usr/lib/python2.6/site-packages/Django-1.5.2-py2.6.egg/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python2.6/site-packages/Django-1.5.2-py2.6.egg/django/contrib/auth/decorators.py" in _wrapped_view
  25.                 return view_func(request, *args, **kwargs)
File "/var/www/html/tamtools/agendas/views.py" in meeting_event
  44.             return HttpResponseRedirect(reverse('agenda_detail', args=(new_agenda.pk,)))

Exception Type: AttributeError at /agendas/add/
Exception Value: 'NoneType' object has no attribute 'pk'

Has something changed in Django 1.5 that I don't know about? new_agenda should be a meetingEventForm type, shouldn't it?

Upvotes: 0

Views: 2622

Answers (2)

Burhan Khalid
Burhan Khalid

Reputation: 174692

You don't need to override the ModeForm save method, since you aren't doing anything special with it. Your ModelForm should look like:

class MeetingEventForm(forms.ModelForm):
    """
    to create a new meeting event.
    """
    class Meta:
        model = meetingEvent

I also changed the class name to conform with the Python style guide.

You also have two extra fields in the form that have nothing to do with your model. There could be two reasons - one, you need to save these fields in another model, or the second option is that you want someone to authorize themselves before they can add a new event.

Since the second one seems more plausible, restrict access to the form from your view:

from django.contrib.auth.decorators import login_required
from django.shorcuts import render, redirect

@login_required()
def meeting_event(request):
    form = MeetingEventForm(request.POST or {})
    context = {'form': form}

    if request.method == 'POST':
        if form.is_valid():
            new_agenda = form.save()
            return redirect('agenda_detail', args=(new_agenda.pk,))
        else:
            return render(request, 'agendas/event.html', context)
    else:
        return render(request, 'agendas/event.html', context)

As this is a common task, and you are using django 1.5, why not use the generic class based views?

Your code will be reduced, and you don't have to worry about the mundane details:

First, in your views.py, create a class that inherits from the generic CreateView which is used to display a model form for a model, let the user fill it in, and save the details:

from django.views.generic.edit import CreateView

class CreateMeetingRequest(CreateView):
    template_name = 'agendas/event.html'
    model = meetingRequest

Now, to map the view to a url, we add it to urls.py. Since we also want the user to be logged in before they can add a meeting request - the login_required decorator takes care of that for us. It will check if the user is logged in - if not, redirect the user to a login form and once they have logged in, redirect them back to the form:

from django.contrib.auth.decorators import login_required

from .views import CreateMeetingRequest

urlpatterns = patterns('',
    # your other views
    url(r'meeting-request/add/$',
        login_required(CreateMeetingRequest.as_view()), name='add-meeting-req'),
)

Finally, we need to tell the view where to go once the form is successful. CreateView will check if the model has a get_absolute_url method, and call that. So in your models.py:

from django.core.urlresolvers import reverse

class meetingRequest(models.Model):

    # your normal fields

    def get_absolute_url(self):
       return reverse('agenda_detail', args=(self.pk,))

Upvotes: 1

dani herrera
dani herrera

Reputation: 51715

You overwrite save methid in modelform, but you forgot to return model.

return super( ....

Upvotes: 2

Related Questions