Django CreateView does not save object
Question: I'm trying to use a Create View class in a Django App, but can't save the
object. I tried some suggestions posted in related questions
[here](http://stackoverflow.com/questions/17826778/django-createview-is-not-
saving-object) and [here](http://stackoverflow.com/questions/17833117/djangos-
createview-is-not-saving-an-object) with no results.

The error is

> NoReverseMatch at /metas/add-meta Reverse for 'metas_detalle' with arguments
> '()' and keyword arguments '{'pk': None}' not found. 1 pattern(s) tried:
> ['metas/(?P\d+)/control$']

The form is working and valid, the class call the `save()` method but the
object isn't save, so, the `id` is `None`.

This is my `models.py`:

    
    
    class MetasSPE(models.Model):
        puesto = models.CharField("Cargo", max_length=6, choices=PUESTOS)
        clave = models.CharField("Clave de la Meta", max_length=2)
        nom_corto = models.CharField('Identificación', max_length=25)
        year = models.PositiveIntegerField("Año")
        evaluacion = models.BooleanField('Evaluación', default=True)
        ciclos = models.PositiveSmallIntegerField('Repeticiones')
        descripcion = models.TextField('Descripción de la Meta')
        descripcion_html = models.TextField(
            'Descripción de la Meta', editable=False)
        soporte = models.FileField(
            'Soporte', upload_to=archivo_soporte, blank=True, null=True)
        usuario = models.ForeignKey(User, related_name='meta_user', editable=False)
        creacion = models.DateTimeField(auto_now_add=True)
        actualiza = models.DateTimeField(auto_now=True)
    
        def get_absolute_url(self):
            from django.core.urlresolvers import reverse
            return reverse('metas_detalle', kwargs={'pk': self.id})
    

The `get_absolute_url()` is working, tested in `python manage.py shell`:

    
    
    In [1]: from django.core.urlresolvers import reverse
    
    In [2]: reverse('metas_detalle', kwargs={'pk': 1})
    Out[2]: '/metas/1/control'
    

This is the `forms.py`:

    
    
    class MetasSPEForm(forms.ModelForm):
        class Meta:
            model = MetasSPE
            fields = ("puesto", "clave", "nom_corto", "evaluacion", "ciclos", "descripcion", "soporte")
    

And this is my `views.py`:

    
    
    class CrearMeta(CreateView):
        model = MetasSPE
        form_class = MetasSPEForm
        template_name = 'metas/form_base.html'
    
        def form_valid(self, form):
            form.instance.usuario = self.request.user
            form.instance.year = 2015
            return super(CrearMeta, self).form_valid(form)
    

The `urls.py` looks like this:

    
    
    urlpatterns = patterns(
        'metas.views',
        url(r'^$', 'home', name='metas_index'),
    
        url(r'^(?P<pk>\d+)/control$', MetaDetalle.as_view(), name='metas_detalle'),
        url(r'^add-meta$', 'agregar_meta', name='metas_add'),
    )
    

By the way, I tried this function, and got the exact same error, but I'm
unable to see the cause:

    
    
    # from annoying.decorators import render_to
    # from django.contrib.auth.decorators import login_required
    
    @login_required
    @render_to('metas/form_base.html')
    def agregar_meta(request):
        if request.method == 'POST':
            form = MetasSPEForm(request.POST, request.FILES)
            if form.is_valid():
                meta = form.save(commit=False)
                meta.usuario = request.user
                meta.year = 2015
                meta.save()
                return redirect('metas_detalle', kwargs={'pk': meta.id})
        else:
            form = MetasSPEForm()
        return {'title': 'Agregar nueva meta', 'form': form}
    

Hope you can help me.


Answer: According to the error, you have the following URL pattern defined:

> `metas/(?P\d+)/control$`

...which **_should_** be `metas/(?P<pk>\d+)/control$`

Note that this is different from the given patterns above:

    
    
    urlpatterns = patterns(
        'metas.views',
        url(r'^$', 'home', name='metas_index'),
    
        url(r'^(?P<pk>\d+)/control$', MetaDetalle.as_view(), name='metas_detalle'),
        url(r'^add-meta$', 'agregar_meta', name='metas_add'),
    )
    

If I had to guess, you are doing something like the below in the root
**urls.py** :

    
    
    urlpatterns = patterns(
        '',
        url(r'^metas/', include('metas.urls')),
        # Bad line with bad regex below!
        url(r'metas/(?P\d+)/control$', MetaDetalle.as_view(), name='metas_detalle'),
    )
    

Actually, I found the problem:

<https://github.com/SGC-Tlaxcala/sgc-
metas/blob/e5a6c8e7a54f833795c46d5ece438b219460bf47/src/metas/models.py#L167-L177>

    
    
    class MetasSPE(models.Model):
        ...
        def save(self, **kwargs):
            """
            Se sobre-escribe el método `save()` para guardar la descripción con html.
            :param kwargs: Parámetros en clave
            :return: nada
            """
            from markdown import markdown
            self.descripcion_html = markdown(
                self.descripcion, outpu_format='html5', lazy_ol=True
            )
    
        def __str__(self):
            ...
    

You forgot to call super() on your overidden save() method, which means your
model will never save:

    
    
    class MetasSPE(models.Model):
        ...
        def save(self, **kwargs):
            """
            Se sobre-escribe el método `save()` para guardar la descripción con html.
            :param kwargs: Parámetros en clave
            :return: nada
            """
            from markdown import markdown
            self.descripcion_html = markdown(
                self.descripcion, outpu_format='html5', lazy_ol=True
            )
            super(MetasSPE, self).save(**kwargs)
    

