Search code examples
pythondjangopython-3.xdjango-2.1django-1.10

Python TypeError: Object of type 'User' is not JSON serializable after upgrading to Django 2.1


I'm trying to upgrade a Django project from Django(1.10) to Django(2.1), while doing that I'm just stuck at one error, where I was using the request.user.pk to pass a user create an object.

Here's what I have, so far:

From models.py:

class TaggedArticle(models.Model):
    user = models.ForeignKey(User, related_name='tagging', on_delete=models.CASCADE)
    email = models.EmailField(max_length=255)
    category_fit = models.CharField(choices=choices, max_length=255)
    article = models.ForeignKey(Article, related_name='articles', on_delete=models.CASCADE)
    link = models.URLField(max_length=255,)
    relevant_feedback = models.TextField(blank=True)
    category = models.CharField(max_length=255,)
    created_at = models.DateTimeField(default=timezone.now, editable=False)

From forms.py:

class TagForm(forms.ModelForm):
    class Meta:
        model = TaggedArticle
        fields = ('user', 'category_fit', 'article', 'link', 'relevant_feedback', 'category',)
        widgets = {
            'category_fit': forms.RadioSelect()
        }

And From views.py:

class TagView(LoginRequiredMixin, generic.CreateView):
    form_class = forms.TagForm

    def post(self, request, *args, **kwargs):
        try:
            post_data = request.POST.copy()
            post_data.update({'user': request.user.pk})
            print(post_data.values)
            form = forms.TagForm(post_data)
            if form.is_valid():
                tag = form.save(commit=False)
                tag.user = request.user
                tag.email = request.user.email
                tag.save()
                request.session['user'] = tag.user
                request.session['email'] = tag.email
            else:
                print(form.errors)
                return HttpResponse(form.errors, status=400)

            print('going to redirect after successful tagging.')
            return HttpResponseRedirect(reverse('users:dashboard'))

        except Exception as exp:
            logging.error(exp)
            print('error is: {}'.format(exp))
            return HttpResponse(exp, status=400)

Update: Here's the HTML form from template:

<form class="omb_loginForm" action="{% url 'users:tagged' %}" method="POST">
      {% csrf_token %}
      <table class="table">
         <thead><h4> Tag this Article:</h4></thead>
         <tbody>
            <tr>
               <th>Reviewer:</th>
               <td></td>
               <td>{{ user.username }}</td>
             </tr>
             <tr>
                <th>No of Article Reviewed:</th>
                <td></td>
                <td>{{ user.tagging.all |length }}</td>
              </tr>
              <tr>
                 <th>Category:</th>
                 <td></td>
                 <td>{{ art.category }}
                     <input type="hidden" value="{{ art.id }}" name="article"/>
                     <input type="hidden" value="{{ art.link }}" name="link"/>
                     <input type="hidden" value="{{ art.category }}" name="category"/>
                 </td>
              </tr>
              <tr>
                <th>Does the text of this article fit in this category?</th>
                   <td><label class="radio-inline"><input type="radio" name="category_fit"
                                                                            value="yes">Yes</label></td>
                    <td><label class="radio-inline"><input type="radio" name="category_fit"
                                                                               value="no">No</label></td>
                    <td><label class="radio-inline"><input type="radio" name="category_fit"
                                                                               value="not sure">Not Sure</label>
                     </td>
                </tr>
                <tr>
                   <th><label for="comment">Copy and then paste in a relevant sentence for categorization:</label></th>
                      <td colspan="3">
                          <textarea class="form-control" rows="7" id="comment"
                                                      name="relevant_feedback"></textarea>
                      </td>
                  </tr>
              </tbody>
         </table>
         <button class="btn btn-lg btn-primary btn-block" type="submit">Tag</button>
</form>

So, on POST request it returns the error below as:

TypeError: Object of type 'User' is not JSON serializable

When I print the form.errors then it prints:

<ul class="errorlist"><li>user<ul class="errorlist"><li>Select a valid choice. That choice is not one of the available choices.</li></ul></li></ul>

So, what can be wrong here?

Thanks in advance!


Solution

  • Since the user in your TaggedArticle instance is the currently logged in user, I think you don't need to specify the user in the TagForm. You can remove the user from the fields of TagForm:

    class TagForm(forms.ModelForm):
        class Meta:
            model = TaggedArticle
            fields = ('category_fit', 'article', 'link', 'relevant_feedback', 'category',)
            widgets = {
                'category_fit': forms.RadioSelect()
            }
    

    Now since your view automatically saves the currently logged in user as user for TaggedArticle, no more work is needed and the error might also not occur.

    EDIT: Maybe the error is generated by the line:

    request.session['user'] = tag.user
    

    Here the User object is not JSON serializable and Django uses JSON to serialize the session data after version 1.6. So you might need to store the user id instead of the User object. Example:

    request.session['user'] = tag.user.id