# How to do multi-line f strings?

**URL:** <https://discuss.python.org/t/how-to-do-multi-line-f-strings/46634>\
**Category:** Python Help\
**Created:** [February 22, 2024, 12:21pm UTC](https://discuss.python.org/t/how-to-do-multi-line-f-strings/46634 "2024-02-22T12:21:59Z")\
**Posts on this page:** 1\
**Showing post:** 24

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [March 1, 2024, 2:14am UTC](https://discuss.python.org/t/how-to-do-multi-line-f-strings/46634/24 "2024-03-01T02:14:57Z")

</div>

> [@c-rob](#):
>
> The teacher has suggested this method for parameterizing stuff for long SQL statements.
> 
> ```python
> # Use ? as placeholder for parameters 
> sql = 'UPDATE products SET name=?, price=?, description=? WHERE id=?' 
> # Pass the parameters as a tuple 
> params = (name, price, description, product_id) 
> cursor.execute(sql, params)
> 
> ```
> 
> I think I mentioned a single SQL statement that I use in production can be 1000+ characters, or even 80-100 lines long with 2-3 fields per line. So I needed a way to make the SQL readable in the code for debugging purposes.

It sounds like you have a database backend that does not support named parameters, in which case you can create a helper function that converts an `execute` method that accepts only positional parameters into a wrapper function that accepts named parameters. Use a regex pattern that grabs the name after a colon but ignores quoted strings and comments:

```python
import re
import sys

def to_positional(func, _pattern=re.compile(r"'(?:''|[^'])*'|--.*|:(\w+)")):
    def execute(operation, namespace=sys._getframe(1).f_locals):
        def replacer(match):
            if name := match[1]:
                params.append(namespace[name])
                return '?'
            return match[0]
        params = []
        return func(_pattern.sub(replacer, operation), params)
    return execute

```

so that you can call the wrapped `execute` method with a query with named parameters. The wrapper uses names and values from the current local namespace by default so it behaves more like an f-string:

```python
sql = """\
UPDATE products
SET
    name=:name,
    price=:price,
    description=':description ''' || :description || ''''
WHERE id=:id; -- :comment
"""
id, name, price, description = 123, 'foo', 99.99, 'foo description'
to_positional(cursor.execute)(sql)

```

But you can also call the wrapper with your own namespace:

```python
to_positional(cursor.execute)(sql,
    {'id': 123, 'name': 'foo', 'price': 99.99, 'description': 'foo description'})

```

To more easily test the transfromed query without involving the database, you can make it wrap `print` instead of `cursor.execute`:

```python
to_positional(print)(sql)

```

so it would output:

```python
UPDATE products
SET
    name=?,
    price=?,
    description=':description ''' || ? || ''''
WHERE id=?; -- :comment
 ['foo', 99.99, 'foo description', 123]

```

---

_[View the full topic](https://discuss.python.org/t/how-to-do-multi-line-f-strings/46634)._
