Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: post() got an unexpected keyword argument

I am trying to create a simple blog where I can communicate with the users directly. Each user will have a blog post each month posted by the Admin, and they can comment on it to communicate. The workflow is as follows:

Admin logs into the site -> Admin is presented with all the available Users. -> Admin clicks on a User -> If that user has a post for current month, display that post -> Else create new post.

Here is what I have:

blog/urls.py:

from django.urls import path
from .views import MessageThread, CreateThread

urlpatterns = [
    path('user_thread/<int:user_id>', MessageThread.as_view(), name='message_thread'),
    path('create_thread/<int:user_id>', CreateThread.as_view(), name='create_thread'),
]

blog/models.py:

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField(max_length=9001)
    posted_for = models.ForeignKey(User, related_name='posted_for', on_delete=models.CASCADE)
    published_date = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return self.title

blog/forms.py:

from django.forms import models
from .models import Post


class CreateThreadForm(models.ModelForm):

    class Meta:
        model = Post
        fields = ['title', 'text']

blog/views.py:

from .forms import CreateThreadForm
from django.shortcuts import render, redirect
from django.views.generic import TemplateView
from datetime import datetime
from django.contrib.auth.models import User

class CreateThread(TemplateView):
    template_name = 'blog/create_thread.html'

    def get(self, request, *args, **kwargs):
        
        return render(request, self.template_name, {'form': CreateThreadForm(), 'user_id': self.kwargs['user_id']})

    def post(self, request):
        
        if 'comfirm_post' in request.POST:
            form = CreateThreadForm(request.POST)
            if form.is_valid():
                post_details = form.save(commit=False)
                post_details.author = request.user
                post_details.posted_for = User.objects.get(id=request.POST['user_id'])
                post_details.published_date = datetime.now()
                form.save()

                messages.success(request, "Successfully created post")
                return redirect('message_thread', user_id=request.POST['user_id'])

        messages.error(request, 'Something went wrong')
        return redirect('create_thread', user_id=request.POST['user_id'])

blog/create_thread.html:

{% extends 'navbar.html' %}
{% block content %}
    <h1>Create New Thread</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" name="comfirm_post">Create Thread</button>
    </form>
{% endblock %}

The problem is that it does not even go in the post() function in CreateThread class. It simply throws an error saying post() got an unexpected keyword argument 'user_id'. Here is a complete trackback of the error:

Internal Server Error: /blog/create_thread/4
Traceback (most recent call last):
  File "C:\Projects\1-2-1\venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Projects\1-2-1\venv\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Projects\1-2-1\venv\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Projects\1-2-1\venv\lib\site-packages\django\views\generic\base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Projects\1-2-1\venv\lib\site-packages\django\views\generic\base.py", line 97, in dispatch
    return handler(request, *args, **kwargs)
TypeError: post() got an unexpected keyword argument 'user_id'
[27/Aug/2019 16:04:02] "POST /blog/create_thread/4 HTTP/1.1" 500 75548

I have no idea where this user_id it says is coming from. I have tried passing user_id as hidden input from form, I tried changing the action to pass a user_id like action={% url 'create_thread' user_id %} but that does not work at all.

like image 710
Naeem Khan Avatar asked Aug 27 '19 12:08

Naeem Khan


2 Answers

You have user_id in your URL pattern,

path('create_thread/<int:user_id>', ...

Therefore your post method should take user_id, and you should use user_id instead of request.POST['user_id']:

def post(self, request, user_id):
    ...
    post_details.posted_for = User.objects.get(id=user_id)

Alternatively, you can accept *args and **kwargs, and fetch user_id from kwargs or self.kwargs.

def post(self, request, *args, **kwargs):
    ...
    post_details.posted_for = User.objects.get(id=self.kwargs['user_id'])
like image 164
Alasdair Avatar answered Nov 07 '22 10:11

Alasdair


Since your url has a user_id, you need to use that in the post method:

class CreateThread(TemplateView):
    template_name = 'blog/create_thread.html'

    def get(self, request, *args, **kwargs):
        return render(request, self.template_name, {'form': CreateThreadForm(), 'user_id': self.kwargs['user_id']})

    def post(self, request, user_id):

        if 'comfirm_post' in request.POST:
            form = CreateThreadForm(request.POST)
            if form.is_valid():
                post_details = form.save(commit=False)
                post_details.author = request.user
                post_details.posted_for = User.objects.get(id=user_id)
                post_details.published_date = datetime.now()
                form.save()

                messages.success(request, "Successfully created post")
                return redirect('message_thread', user_id=user_id)

        messages.error(request, 'Something went wrong')
        return redirect('create_thread', user_id=user_id)

You might however use a CreateView [Django-doc] here, this can reduce the amount of boilerplate code:

from django.views.generic.edit import CreateView
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse

class CreateThread(SuccessMessageMixin, CreateView):
    template_name = 'blog/create_thread.html'
    form_class = CreateThreadForm
    model = Post
    success_message = 'Successfully created post'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['user_id'] = self.kwargs['user_id']
        return context

    def get_success_url(self):
        return reverse('message_thread', kwargs={'user_id': self.kwargs['user_id']})

    def form_valid(self, form):
        form.instance.author = request.user
        form.instance.posted_for_id = self.kwargs['user_id']
        form.instance.published_date = datetime.now()
        return super().form_valid(form)

    def post(self, request, *args, **kwargs):
        if 'comfirm_post' in request.POST:
            return super().post(request, *args, **kwargs)
        messages.error(request, 'Something went wrong')
        return redirect('create_thread', user_id=user_id)
like image 32
Willem Van Onsem Avatar answered Nov 07 '22 09:11

Willem Van Onsem