pythonflasksqlalchemyflask-sqlalchemy

"TypeError: expected string or bytes-like object, got 'type'" error with SQLAlchemy query


(I'd tried to post this here, had someone post a version of it for me on r/flask, and am trying again here, so please excuse the duplication if the original one shows up.)

I'm trying to modify a Flask/SQLAlchemy app with what seems like a simple query, and it's throwing an error I can't figure out. I'm trying to retrieve some "author" records, to pass through to a template. Given an ID value, I'm joining two tables, in classes Author and Pseudonyms, that (I think) are properly declared. The function looks like:

def get_authors(author_id):
    authors = Author.query.\
        filter(Pseudonyms.author_id == Author.author_id).\
        filter(Pseudonyms.pseudonym == author_id)
    return authors

In the table declarations, the Author class has (among other things, of course, but I'm just identifying what's shown here):

pseudonyms = db.relationship("Pseudonyms")

and the Pseudonyms class has (likewise):

author_id = db.Column(db.Integer, db.ForeignKey('authors.author_id'))

I have a number of similar queries, with similarly-declared tables, that all work fine. This one, however, throws TypeError: expected string or bytes-like object, got 'type', with the stacktrace showing the second "filter" line in the above query as the most-immediate error line. But when I go to the console debugger, it shows that "author_id" is an int (substituting a literal integer value doesn't affect the error, BTW), and that "Pseudonyms.pseudonym" is also an integer type. I just don't know what's wrong with this line; and I don't see any other example of this "got 'type'" error anywhere, so don't know how to interpret it.

I've also tried rewriting this in the newer execute/select/where style, instead of query/filter, but it's the same. Everything I've tried always throws the "got 'type'" error in the second "filter" line (or its equivalent). I'm actually porting the query itself from raw SQL from a related app, but the problem is with this TypeError, not incorrect SQL or anything.

What am I missing?

Edit: As mentioned, I'm porting the query from raw SQL in a related app, where this query has been working fine for decades. The original query is:

 query = """SELECT a.author_id, a.author_name
                   FROM authors a, pseudonyms p
                   WHERE p.pseudonym = %d
                   AND p.author_id = a.author_id""" % int(author_id)

Edit: added full traceback (with some changes to paths etc.):

Traceback (most recent call last):
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 1536, in __call__
    return self.wsgi_app(environ, start_response)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 1514, in wsgi_app
    response = self.handle_exception(e)
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask/app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask_caching/__init__.py", line 426, in decorated_function
    rv = self._call_fn(f, *args, **kwargs)
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/flask_caching/__init__.py", line 185, in _call_fn
    return ensure_sync(fn)(*args, **kwargs)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/path/to/app/python-port/app/views.py", line 228, in view_author
    real_names=app.models.get_real_names_of_pseud(author),
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/path/to/app/python-port/app/models.py", line 520, in get_real_names_of_pseud
    return get_authors(author.author_id)
  File "/path/to/app/python-port/app/models.py", line 477, in get_authors
    filter(Pseudonyms.pseudonym == author_id).\
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/operators.py", line 584, in __eq__
    return self.operate(eq, other)
           ~~~~~~~~~~~~^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 1515, in operate
    return op(self.comparator, *other, **kwargs)  # type: ignore[no-any-return]  # noqa: E501
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/operators.py", line 584, in __eq__
    return self.operate(eq, other)
           ~~~~~~~~~~~~^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/type_api.py", line 197, in operate
    return op_fn(self.expr, op, *other, **addtl_kw)
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/default_comparator.py", line 121, in _boolean_compare
    obj = coercions.expect(
    
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/coercions.py", line 395, in expect
    resolved = impl._literal_coercion(
    
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/coercions.py", line 803, in _literal_coercion
    return expr._bind_param(operator, element, type_=bindparam_type)
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 4603, in _bind_param
    return BindParameter(
    
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 1978, in __init__
    self.key = _anonymous_label.safe_construct(
    
  File "/path/to/app/python-port/venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 5471, in safe_construct
    body = re.sub(r"[%\(\) \$]+", "_", body)
  File "/usr/lib/python3.13/re/__init__.py", line 208, in sub
    return _compile(pattern, flags).sub(repl, string, count)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'type'

Solution

  • OP here. I have finally solved this, after a thorough debugging session where I started writing an MRE (at @snakecharmerb's suggestion).

    In my class declaration of the Pseudonyms class, I originally had

        pseudonym = db.column(db.Integer)
    

    instead of the correct

        pseudonym = db.Column(db.Integer)
    

    And that was it.

    @Detlef's code would have worked, if I had pasted it in instead of eyeballing it and missing the c/C distinction.

    I suppose I would have expected some more clear error when I did this in the first place, or when I tried to use the pseudonym column, but so it goes. Thank you everyone for sticking with this.