TypeError: post() missing 1 required positional argument

Viewed 1891

I'm trying to edit data from a table in a template:

Table

I click on "Edit" button but I get this error: post() missing 1 required positional argument: 'folio'

Error

Here is my urls.py

 from django.contrib import admin
 from django.contrib.staticfiles.storage import staticfiles_storage
 from django.conf.urls import url
 from django.urls import path, include, re_path
 from . import views
 from django.views.generic.base import RedirectView

 app_name = 'oficios'

 urlpatterns = [
    path('', views.lista_oficios, name="list"),
    path('crear/', views.crear_oficio, name="crear"),
    path('dependencia/', views.agregar_dependencia, name="dependencia"),
    path('editar/', views.EditarOficio.as_view(), name="editar"),
    path('editar/<str:folio>/', views.EditarOficio.as_view(), name="editar"),
]

My views.py:

@method_decorator(login_required, name='dispatch')
class EditarOficio(CreateView):

    model = Oficio
    fields = ['folio', 'usuario', 'asunto',
              'estatus', 'documento', 'dependencia', 'turnado']

    def get(self, request, folio):
        oficio = Oficio.objects.get(id=folio)
        form = forms.CreateOficio(instance=oficio)
        context = {
            "form": form,
            "title": "Editar oficio " + oficio.folio
        }
        return render(request, "editar_oficio.html", context)

    def post(self, request, folio):
        oficio = Oficio.objects.get(id=folio)
        print(oficio)
        form = forms.CreateOficio(request.POST, request.FILES, instance=oficio)
        context = {
            "form": form,
            "title": "Editar oficio " + oficio.folio
        }
        if(form.is_valid()):
            oficio = form.save(commit=False)
            oficio.save()
            return redirect('oficios:list')
        return render(request, "editar_oficio.html", context)

My models.py:

class Oficio(models.Model):

    class Estatus(models.TextChoices):
        NUEVO = 'NU', _('Nuevo')
        NO_REVISADO = 'NR', _('No Revisado')
        LEIDO = 'L', _('Leido')
        SEGUIMIENTO = 'S', _('Seguimiento')
        COMPLETADO = 'C', _('Completado')

    folio = models.CharField(
        primary_key=True, max_length=10, unique=True, null=False)
    usuario = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    fecha = models.DateTimeField(auto_now_add=True)
    asunto = models.TextField(null=False)
    estatus = models.CharField(
        max_length=2, choices=Estatus.choices, default=Estatus.NUEVO)
    documento = models.FileField(upload_to='pdf')
    dependencia = models.ForeignKey(
        'Dependencia', on_delete=models.CASCADE, related_name='dependencia_origen')
    turnado = models.ForeignKey(
        'Dependencia', on_delete=models.CASCADE, related_name='dependencia_destino')

My template:

{% extends 'base.html' %}

{% block content %}
    <h1>EDITAR OFICIO</h1>

     <form class="" action="{% url 'oficios:editar' oficio.folio %}" method='POST' enctype="multipart/form-data">
        {% csrf_token %}
        {{ form }}
        <button class="btn btn-primary btn-lg" type="submit">Editar</button>
    </form>
{% endblock content %}

I tried changing the last post parameter with slug=None but then I have no "id" for the object I am trying to update. I don't know what I am doing wrong, can someone advice me? Please

1 Answers

First of all, by having two URLs of the same name I think you'll get conflict within the system as to what URL is being used and which URL the user is being sent to.

Django checks URLs in order so the first that matches is the URL that's hit.

urlpatterns = [
    path('editar/', views.EditarOficio.as_view(), name="editar"),
    path('editar/<str:folio>/', views.EditarOficio.as_view(), name="editar"),
]

In your class EditarOficio, both get and post requests require a folio argument to be present, which then means that the first URL which doesn't pass that parameter, isn't valid.

I'd start by having two different names here so that you can target each URL as you need to. You've suggested that this still doesn't resolve things so at this point I'd also change the path itself to something besides editar, if only for debugging this problem. Because you've said this issue still exists after defining different names, that then sounds like oficio.folio doesn't have a value or something unexpected is going on.

You should debug in the template by trying to display data like {{ oficio.folio }} etc to confirm the data is available.

Based on your template, it looks as though you aren't passing the necessary data in the context in order to form the correct URL to post your form to.

Your template attempts to do oficio.folio in the formation of the URL, but oficio is not available in the context.

Try to make some changes similar to this;

    @method_decorator(login_required, name='dispatch')
    class EditarOficio(CreateView):
    
        model = Oficio
        fields = ['folio', 'usuario', 'asunto',
                  'estatus', 'documento', 'dependencia', 'turnado']
    
        def get(self, request, folio):
            oficio = Oficio.objects.get(id=folio)
            form = forms.CreateOficio(instance=oficio)
            context = {
                "form": form,
                "oficio": oficio,
                "title": "Editar oficio " + oficio.folio
            }
            return render(request, "editar_oficio.html", context)
    
        def post(self, request, folio):
            oficio = Oficio.objects.get(id=folio)
            print(oficio)
            form = forms.CreateOficio(request.POST, request.FILES, instance=oficio)
            context = {
                "form": form,
                "oficio": oficio,
                "title": "Editar oficio " + oficio.folio
            }
            if(form.is_valid()):
                oficio = form.save(commit=False)
                oficio.save()
                return redirect('oficios:list')
            return render(request, "editar_oficio.html", context)

Alternatively, oficio is the instance of the form, so you could probably do the following while leaving your view as it is:

{% url 'oficios:editar' form.instance.folio %}

Related