Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fix render() got an unexpected keyword argument 'renderer' in Django 2.1

I'm resurrecting some old code that use to work in Django 1.9. I'm trying to convert this code over to Django 2.1, but this one package that is part of my project has some compatibility issues. I'm looking to correct the render() type error.

I've looked for possible deprecated code like other similar posts, but I'm not able to find anything, such as widgets. I did try adding a renderer to the body in the forms.py widget, but that didn't work either.

Views.py

@login_required
def compose(request, recipient=None, form_class=ComposeForm,
        template_name='django_messages/compose.html', 
success_url=None, recipient_filter=None):
    """
    Displays and handles the ``form_class`` form to compose new 
messages.
    Required Arguments: None
    Optional Arguments:
    ``recipient``: username of a `django.contrib.auth` User, who should
                   receive the message, optionally multiple usernames
                   could be separated by a '+'
    ``form_class``: the form-class to use
    ``template_name``: the template to use
    ``success_url``: where to redirect after successfull submission
"""
if request.method == "POST":
    sender = request.user
    form = form_class(request.POST, recipient_filter=recipient_filter)
    if form.is_valid():
        form.save(sender=request.user)
        messages.info(request, _(u"Message successfully sent."))
        if success_url is None:
            success_url = reverse('messages_inbox')
        if 'next' in request.GET:
            success_url = request.GET['next']
        return HttpResponseRedirect(success_url)
else:
    form = form_class()
    if recipient is not None:
        recipients = [u for u in User.objects.filter(
            **{'%s__in' % get_username_field(): [r.strip() for r in recipient.split('+')]})]
        form.fields['recipient'].initial = recipients
return render(request, template_name, {
    'form': form,
})

Forms.py

class ComposeForm(forms.Form):
    """
    A simple default form for private messages.
    """
    recipient = CommaSeparatedUserField(label=_(u"Recipient"))
    subject = forms.CharField(label=_(u"Subject"), max_length=140)
    body = forms.CharField(label=_(u"Body"),
                           widget=forms.Textarea(attrs={'rows': '12', 'cols': '55'}))

    def __init__(self, *args, **kwargs):
        recipient_filter = kwargs.pop('recipient_filter', None)
        super(ComposeForm, self).__init__(*args, **kwargs)
        if recipient_filter is not None:
            self.fields['recipient']._recipient_filter = recipient_filter

    def save(self, sender, parent_msg=None):
        recipients = self.cleaned_data['recipient']
        subject = self.cleaned_data['subject']
        body = self.cleaned_data['body']
        message_list = []
        for r in recipients:
            msg = Message(
                sender=sender,
                recipient=r,
                subject=subject,
                body=body,
            )
            if parent_msg is not None:
                msg.parent_msg = parent_msg
                parent_msg.replied_at = timezone.now()
                parent_msg.save()
            msg.save()
            message_list.append(msg)
            if notification:
                if parent_msg is not None:
                    notification.send([sender], "messages_replied", {'message': msg, })
                    notification.send([r], "messages_reply_received", {'message': msg, })
                else:
                    notification.send([sender], "messages_sent", {'message': msg, })
                    notification.send([r], "messages_received", {'message': msg, })
        return message_list

And here is the traceback:

  File "/Users/justinboucher/PycharmProjects/awaylm/django_messages/views.py", line 96, in compose
    'form': form,

  File "/anaconda3/envs/awaylm/lib/python3.6/site-packages/django/forms/boundfield.py", line 33, in __str__
    return self.as_widget()
  File "/anaconda3/envs/awaylm/lib/python3.6/site-packages/django/forms/boundfield.py", line 93, in as_widget
    renderer=self.form.renderer,
TypeError: render() got an unexpected keyword argument 'renderer'
[24/Jan/2019 03:57:26] "GET /messages/compose/ HTTP/1.1" 500 188278
like image 386
Justin Boucher Avatar asked Jan 24 '19 04:01

Justin Boucher


1 Answers

I had the same problem and it turned out to be one of my dependencies.

First I added some debugging information to Django so I could see what was causing the problem, so I edited the below Django file (in my virtualenv so as not to break anything in the global Django package) :

vi site-packages/django/forms/boundfield.py

Your server log traceback will identify the specific file for you.

I added the "help" call at line 50:

        if self.auto_id and 'id' not in widget.attrs:
            attrs.setdefault('id', self.html_initial_id if only_initial else self.auto_id)
        help(widget.render)
        return widget.render(
            name=self.html_initial_name if only_initial else self.html_name,
            value=self.value(),
            attrs=attrs,
            renderer=self.form.renderer,
        )

Then I restarted my Django server so it would execute the help function, and in the server log I saw the following:

Help on method render in module codemirror.widgets:

render(name, value, attrs=None) method of codemirror.widgets.CodeMirror instance
    Render the widget as an HTML string.

This indicates a compatibility issue between Django and django-codemirror which is a dependency I was using to render some content on the form.

I decided to further modify the Django file so as to allow for widgets without a renderer argument. This is not an ideal solution but prevents me having to mess around with the django-codemirror source files.

        if self.auto_id and 'id' not in widget.attrs:
            attrs.setdefault('id', self.html_initial_id if only_initial else self.auto_id)
        # help(widget.render)
        try:
            return widget.render(
            name=self.html_initial_name if only_initial else self.html_name,
            value=self.value(),
            attrs=attrs,
            renderer=self.form.renderer,
        )
        except:
            return widget.render(
            name=self.html_initial_name if only_initial else self.html_name,
            value=self.value(),
            attrs=attrs
        )

Hopefully this process helps to identify the specific object which is incompatible in your environment.

Edit: I have since decided to change Django back to default code, and made the changes needed in my copy of django-codemirror. I'll leave my answer above in case anyone else wants to modify Django to work with their other dependencies.

Have also logged the following issue: https://github.com/onrik/django-codemirror/issues/3

like image 121
BjornO Avatar answered Sep 29 '22 07:09

BjornO