django.db.utils.ProgrammingError: can't adapt type 'User' only in Postgres

Viewed 465

I have this really odd error that I can't seem to figure out. I have solved how to fix it but it could be a weird error for others so I am posting anyways.

I have this view function that allows users to invite team members to their team.

@login_required
def invite_team_member(request):
    if request.method == "POST":
        form = forms.InviteTeamMemberForm(request.POST, team=request.user.profile.team)
        if form.is_valid():
            user = form.clean_username_or_email()
            try:
                models.TeamInvite.objects.create(from_user=request.user, to_user=user, team=request.user.profile.team)
                message = _("User successfully invited")
            except IntegrityError:
                message = _("This user has already been invited to your team")
            except ValidationError as e:
                if e.code == "team_full":
                    message = _("Your team is full, no further users can be invited")
                else:
                    raise e

            return render(
                request,
                "tracker/tracker_home/invite_team_member.html",
                {"invite_team_member_form": form, "message": message},
            )
    else:
        form = forms.InviteTeamMemberForm(team=request.user.profile.team)
    return render(request, "tracker/tracker_home/invite_team_member.html", {"invite_team_member_form": form})

Users enter a username or email into a form and the form determines whether it is valid to invite a user with that username or email.

class InviteTeamMemberForm(forms.Form):
    username_or_email = forms.CharField(label=_("Username or Email"))

    def __init__(self, *args, **kwargs):
        self.team: models.Team = kwargs.pop("team")
        super().__init__(*args, **kwargs)

    def clean_username_or_email(self):
        data = self.cleaned_data["username_or_email"]

        try:
            user = User.objects.get(Q(username__iexact=data) | Q(email__iexact=data))
            if user.profile.team == self.team:
                raise ValidationError(_("This user cannot be invited since they are already on this team"))
            return user
        except User.DoesNotExist:
            raise ValidationError(_("A user with that username or email doesn't exist."))

Now this is where it gets weird. This all works great with a Sqlite database but with Postgres, it throws this stacktrace.

Internal Server Error: /tracker/data/invite-team-member/
Traceback (most recent call last):
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: can't adapt type 'User'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/sentry_sdk/integrations/django/views.py", line 67, in sentry_wrapped_callback
    return callback(request, *args, **kwargs)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/home/raymonddevries/PycharmProjects/tourdeskagit/tracker/views.py", line 72, in invite_team_member
    user = form.clean_username_or_email()
  File "/home/raymonddevries/PycharmProjects/tourdeskagit/tracker/forms.py", line 32, in clean_username_or_email
    user = User.objects.get(Q(username__iexact=data) | Q(email__iexact=data))
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/query.py", line 431, in get
    num = len(clone)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/query.py", line 262, in __len__
    self._fetch_all()
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/query.py", line 51, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1175, in execute_sql
    cursor.execute(sql, params)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/sentry_sdk/integrations/django/__init__.py", line 500, in execute
    return real_execute(self, sql, params)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/raymonddevries/PycharmProjects/virtualenvs/tourdeskagit/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: can't adapt type 'User'

I can't figure out if there something going on behind the scenes that I am missing. Stepping the through the code the database retrieves the user fine when executing user = User.objects.get(Q(username__iexact=data) | Q(email__iexact=data)) however when the user is returned to the view using form.clean_username_or_email() an error occurs. Again this only occurs with postgres, not sqlite.

If anyone has insight on what is going on here let me know. I'm guessing form.clean_username_or_email() is not meant to be called directly from a view function. Still odd this throws a database specific error.

1 Answers

The solution for me was to use user = form.cleaned_data["username_or_email"] instead of user = form.clean_username_or_email(). Relevant documentation.

Related