akumoth/peewee-issues huggingface dataset:
Issue: Forward class creation keyword arguments
Author: smac89
State: closed
Created: 2022-10-10 21:37:44

https://docs.python.org/3/library/functions.html#type

I wanted to be able to create a template model which other models inherit from and modify, by passing keyword arguments in the class definition. However, the current implementation of `ModelBase` does not pass along those arguments which results in errors like

```
TypeError: ModelBase.__new__() got an unexpected keyword argument 'custom_arg'
```

---

Example code:

Base class
```py
class Foo(pw.Model):
    foo_type: pw.CharField

    @classmethod
    def __init_subclass__(
        cls, foo_type_default: str
    ) -> None:
        cls.foo_type = pw.CharField(
            choices=(
                ("bar", "Bar"),
                ("foobar", "Foobar"),
            ),
            default=foo_type_default,
        )
```

Subclass1
```py
class Bar(Foo, foo_type_default="bar"):
    pass
```

Subclass2
```py
class FooBar(Foo, foo_type_default="foobar"):
    pass
```

With the current implementation, the above doesn't work. This PR fixes that.

Sure, I've merged an equivalent patch w/just the 79 char line limit.

That was easy. Thanks!

When do you think a new release will be available?

There's hardly any fixes, I think it may need a bit of time.
Issue: Any way to change table naming logic?
Author: Glucksistemi
State: closed
Created: 2022-10-06 10:15:57

By default, if I call my Model class using python conventional CamelCase with multiple words in its name (like `class ManyWordsNamedTable(peewee.Model)`) Peewee will just lowercase it, so I'll get table "manywordsnamedtable" created in my DB which is hardly readable. I know there's an option to use Meta.table_name parameter, but it applies individually so I should define names for every table by my hands. Is there any option to name all the tables in snake_case just changing base model Meta class and then inherit every table from this BaseModel?

I want it to be something like this:

```python
import peewee

class BaseModel(peewee.Model):
    class Meta:
        database = peewee.PostgresqlDatabase(...)
        table_nameing_type = peewee.SNAKE_CASE_NAMING

class RealModelWithLongName(BaseModel)
    some_field = peewee.IntegerField()

RealModelWithLongName.create_table()
```
And it would create table named real_model_with_long_name

Yes, specify `legacy_table_names = False` in your base model Meta:

```python
from peewee import *

def table_name(cls):
    return cls.__name__

class Base(Model):
    class Meta:
        legacy_table_names = False

class RealModelWithLongName(Base):
    pass

print(RealModelWithLongName._meta.table_name)
```

Prints "real_model_with_long_name".

peewee also supports a `Meta.table_function` option to provide your own implementation. For your example:

```python
from peewee import *

def table_name(cls):
    return cls.__name__

class Base(Model):
    class Meta:
        table_function = table_name

class RealModelWithLongName(Base):
    pass

print(RealModelWithLongName._meta.table_name)
```

Prints "RealModelWithLongName". Documented here: http://docs.peewee-orm.com/en/latest/peewee/models.html#table-names
Issue: Postgresql: Updating an existing field with adding/incrementing some value didn't work as expected
Author: Sonotoki-da
State: closed
Created: 2022-10-01 17:58:26

My table is:
```python
class Work(BaseModel):
    id = AutoField()
    user_id = BigIntegerField()
    name = TextField(unique=True)
    filename = CharField(null=True, unique=True)
    authors = TextField(null=True)
    content = TextField(null=True)
    start_date = DateField()
    end_date = DateField(null=True)
```

I'm trying to add (that is, +=) some string to an existing data record:
```python
def add_author(user_id: int, name: str, author: str):
    # author = 'Illyustrator:An illustrator%'
    # the delimiter is '%'
    return Work.update(authors=Work.authors + author).where(
            (Work.user_id == user_id) & (Work.name == name)
    ).execute()
```

The debug logs are:
```
2022-10-01 22:41:17,493 - DEBUG - peewee - ('SELECT "t1"."authors" FROM "work" AS "t1" WHERE (("t1"."user_id" = %s) AND ("t1"."name" = %s))', [5581756786, 'Title name here'])
2022-10-01 22:41:17,495 - DEBUG - peewee - ('UPDATE "work" SET "authors" = ("work"."authors" || %s) WHERE (("work"."user_id" = %s) AND ("work"."name" = %s))', ['Illyustrator:An illustrator%', 5581756786, 'Title name here'])
```

a Postgresql query in the psql cli after that query:
```
ebookmaker=> select * from work;
 id |  user_id   |      name       | filename | authors | content | start_date | end_date 
----+------------+-----------------+----------+---------+---------+------------+----------
  4 | 5581756786 | Title name here |          |         |         | 2022-10-01 | 
(1 row)
```
But the query doesn't adding the string value to the authors field...
Versions of python and pewee are:
```
Python 3.10.6 (main, Aug  3 2022, 17:39:45) [GCC 12.1.1 20220730] on linux
peewee: 3.15.2
```

By the way, this feature was working in sqlite database

I found that I'm concatenating a string with a None type object...
```python
# Work.authors = None
# author = 'string'
Work.update(authors=Work.authors + author)
```
Maybe this is the problem I'm encountering? It's strange that I'm not getting any errors though

All is working as far as I can tell:

```python
db = PostgresqlDatabase('peewee_test')

class Reg(Model):
    data = TextField()
    class Meta:
        database = db

db.drop_tables([Reg])
db.create_tables([Reg])

r = Reg.create(data='start')
uq = (Reg
      .update(data=Reg.data + '-end')
      .where(Reg.id == r.id)
      .execute())

r_db = Reg.get(Reg.id == r.id)
print(r_db.data)
```

Prints:

```
start-end
```

Oh, sorry for troubling
Maybe something went wrong, and it seems I haven't tested my code thoroughly yet
Thank you for your confirmation on this case, I really love this library!
Issue: Is it possible to make non-peewee models play nicely with Peewee?
Author: ilyakamens
State: closed
Created: 2022-09-23 13:11:29

Good morning (?), @coleifer.

I'm wondering if it's possible to make non-peewee models play nicely with Peewee. Consider the following, which is contrived, but also a real pattern we use at my company:

```python
class DBUser(Model):
    pass

class DBAccount(Model):
    user = ForeignKeyField(DBUser)

class BankUser:
    """A user in our banking partner's system (not our DB)."""

class User:
    """A higher level user representing a user that lives both in our DB and in our banking partner's system."""

    def __init__(dbuser, bankuser):
        self._db = dbuser
        self._bank = bankuser
```

Is it possible, or are there officially endorsed ways, to duck-type a non-Peewee model (e.g.,`User`) so that it will play nicely with queries like the following:

```python
DBAccount.select().where(DBAccount.user == user)
```

In this particular case, adding the following methods does the trick:

```python
# We already have this method defined for other purposes.
@property
def id(self):
    return self._db.id

def __int__(self):
    return self.id
```

That's obviously brittle, though, and doesn't work when the primary key isn't the ID. I'm looking for something that doesn't rely on Peewee's internals, and wouldn't be subject to breaking from a non-major version change. I briefly looked at `Metadata`, but figured I'd ask before going further down the rabbit hole. No problem if there's nothing official or unofficial for this. Thank you.

I would not advise doing this, even if it works in the simple case. It will be too confusing, I think, especially if you have more than one person working on this codebase or intend to support it for a while (imagine trying to explain all this to a new hire).

Better, in my opinion, to just explicitly reference the IDs or add convenience methods. Also in your example, I don't think it is correct to `ForeignKey(User)` since `User` is not a real table.

>Also in your example, I don't think it is correct to ForeignKey(User) since User is not a real table.

I don't think it'll change your response, but that was a typo, which I've since fixed.

> It will be too confusing, I think, especially if you have more than one person working on this codebase or intend to support it for a while (imagine trying to explain all this to a new hire).

This is subjective / beyond the scope of the discussion, but we optimize our codebases for folks who have been working in them for some time (which is the majority / common case). Thanks again for your consideration.
Issue: test_murmur_hash fails on s390x architecture
Author: carlwgeorge
State: closed
Created: 2022-09-21 19:32:25

While running the tests on the s390x architecture for a Fedora build, I'm seeing this failure.

```
======================================================================
FAIL: test_murmur_hash (tests.sqlite.TestMurmurHash.test_murmur_hash)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/peewee-3.15.2/tests/sqlite.py", line 1576, in test_murmur_hash
    self.assertHash('testkey', 2871421366)
  File "/builddir/build/BUILD/peewee-3.15.2/tests/sqlite.py", line 1573, in assertHash
    self.assertEqual(cursor.fetchone()[0], e)
AssertionError: 3876111420 != 2871421366
----------------------------------------------------------------------
```

This passes on all the other architectures that Fedora builds for (i686, x86_64, aarch64, ppc64le).  I was able to reproduce this error in the following environments:

- [Fedora 38](https://koji.fedoraproject.org/koji/taskinfo?taskID=92255140), Python 3.11.0~rc2, sqlite 3.39.3
- [Fedora 37](https://koji.fedoraproject.org/koji/taskinfo?taskID=92254905), Python 3.11.0~rc2, sqlite 3.39.2
- [Fedora 36](https://koji.fedoraproject.org/koji/taskinfo?taskID=92254910), Python 3.10.6, sqlite 3.36.0
- [Fedora 35](https://koji.fedoraproject.org/koji/taskinfo?taskID=92254914), Python 3.10.6, sqlite 3.36.0
- [RHEL 9](https://koji.fedoraproject.org/koji/taskinfo?taskID=92254798), Python 3.9.10, sqlite 3.34.1

What does `platform.machine()` return on that hardware, out of curiosity? I'm sure this is just an endianness thing, so we can just add a conditional to skip the test.

```
>>> platform.machine()
's390x'
```

Just FYI I tagged a new release, 3.15.3.
Issue: Pwiz exporting of postgres materialized view
Author: surfer190
State: closed
Created: 2022-09-21 07:29:25

Pwiz exports an existing db into models that can be written to a file.

The [documentation mentions that the `-v` flag](https://docs.peewee-orm.com/en/latest/peewee/playhouse.html?highlight=generating%20models#pwiz-a-model-generator) can be used to export views.

I tried using `pwiz` with the `-v` flag and a materialized view I have was not exported.

In a [previous issue](https://github.com/coleifer/peewee/issues/1052) it was mentioned that peewee does not generate the DDL (data definition language) for the creation of views.

So the correct course of action is to manually define the model for the view?

Do you see your materialized view when running:

```sql
select viewname, definition from pg_catalog.pg_views 
where schemaname = 'public' order by tablename; -- change schema name if differs
```

Oh wait, nevermind, I see that they're listed separately in `pg_catalog.pg_matviews`.

For now I'm going to hold off on implementing this, as our standard way of introspecting the columns associated with a table (or non-materialized view) does not seem to work with materialized views. With regular tables or views, we can run something like:

```sql
select column_name, is_nullable, data_type, column_default
from information_schema.columns
where table_name = 'the table or view name'
```

Unfortunately, it looks like the process for introspecting a materialized view is different, see: https://stackoverflow.com/questions/39179253/postgresql-get-materialized-view-column-metadata

For now you'll just want to create these manually.

If the view you are manually defining does not have a primary key - read about [models without a primary key](https://docs.peewee-orm.com/en/latest/peewee/models.html#models-without-a-primary-key)

eg.

    class MyMaterializedView(BaseModel):
        ...
    
        class Meta:
            primary_key = False
Issue: Add operator overloading for joins 
Author: valq7711
State: closed
Created: 2022-09-18 00:10:17

@coleifer  Hi! Thank you for great project!
So I have an idea:
```python
query = (Foo
         .select(...)
         .join(
            Foo.t 
            & Bar.t.on(...)  #  INNER JOIN
            + Baz.t.on(),    #  LEFT JOIN
            # switch to Foo-context (notice comma above)
            Foo.t  
            & Whatever.t.on(...)   
            ...
         ) 
         .where(...))
```

Sorry, that's not going to work, as we overload those already for the creation of expressions. This would require treating expression objects differently in the context of the `.join()` method. The way you've got `.on()` and the absence of parentheses presents even more difficulties.

Sorry, but I don't see the problem. 
`User.t` - `t` - is not the field,  but magic model self-reference, so it doesn't overlap with field-expressions

Here is dirty but working example to demonstrate the concept:
```python
import os
from peewee import *
import datetime

fp = os.path.join(os.path.dirname(__file__), 'my_database.db')


db = SqliteDatabase(fp)


class ModelRef:
    def __init__(self, cls):
        self._cls = cls
        self._right = None
        self._join_type = None
        self._on = None

    def __and__(self, model_to_join):
        self._join_type = JOIN.INNER
        self._right = model_to_join
        return self

    def on(self, expression):
        self._on = expression
        return self

    def __call__(self):
        return (
            self._right._cls,
            self._join_type,
            self._right._on,
        )


class BaseModel(Model):

    # should be @classproperty
    @classmethod
    def t(cls):
        return ModelRef(cls)

    class Meta:
        database = db


class User(BaseModel):
    username = CharField(unique=True)


class Tweet(BaseModel):
    user = ForeignKeyField(User, backref='tweets')
    message = TextField()
    created_date = DateTimeField(default=datetime.datetime.now)
    is_published = BooleanField(default=True)


query = (
    User
    .select(User, Tweet)
    .join(*(
        User.t()
        & Tweet.t().on(Tweet.user == User.id)
    )())
)

print(query)

```

@coleifer  so it only requires proper `ModelRef` implementation and `isinstance(dest, ModelRef)`-logic in `ModelSelect.join`-method
Issue: Expression syntax should properly handle chained expressions like `a <= expr <= b`
Author: jonashaag
State: closed
Created: 2022-09-03 13:29:53

IMO Peewee should support chained expressions like `a <= expr <= b` or raise an error.

```py
# M = my model.
# As expected, there are 3 objects with id <= 3.
In [33]: list(M.select(M.id).where(M.id <= 3).tuples())
Out[33]: [(1,), (2,), (3,)]

# Unexpected: Ignores the first constraint.
In [34]: list(M.select(M.id).where(2 <= M.id <= 3).tuples())
Out[34]: [(1,), (2,), (3,)]
```

I don't know that we're able to do this, actually:

```python
In [1]: class Expr(object):
   ...:     def __init__(self, lhs, op, rhs):
   ...:         self.lhs, self.op, self.rhs = lhs, op, rhs
   ...:     def __str__(self):
   ...:         return '%s %s %s' % (self.lhs, self.op, self.rhs)
   ...:     def __le__(self, other):
   ...:         return Expr(self, '<=', other)
   ...:     def __ge__(self, other):
   ...:         return Expr(self, '>=', other)
   ...: 

In [2]: e = Expr(None, None, None)

In [3]: print(2 <= e <= 5)
None None None <= 5
```

See how the first comparison (2 <= e) is lost?

The best you can get is to use parentheses, but the semantics change and will not be correct:

```python
In [5]: print((2 <= e) <= 5)
None None None >= 2 <= 5
```

Anyways, you should probably be using `.between()` anyways, as some databases are able to optimize this:

```python
M.select(M.id).where(M.id.between(2, 3))
```


Indeed, this is impossible to implement in Python. I'm surprised.

```
>>> dis.disassemble((lambda: x <= y < z).__code__)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 DUP_TOP
              6 ROT_THREE
              8 COMPARE_OP               1 (<=)
             10 JUMP_IF_FALSE_OR_POP     9 (to 18)
             12 LOAD_GLOBAL              2 (z)
             14 COMPARE_OP               0 (<)
             16 RETURN_VALUE
        >>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE
```

The result of one of the comparisons will always be ignored (8 -> 10 -> 18 => second comparison never executed; or 8 -> 10 -> 12 => result of first comparison is dropped).

Thank you for your comment Jonas, very nice debugging and a better explanation than the one I provided.
Issue: Add Example for Idiomatic Message Queue Pattern
Author: zackees
State: closed
Created: 2022-09-02 19:17:55

Hi there. Great piece of software. I'm a novice at databases which has brought me to this solution.

I'm looking for examples on the internet for how to use Peewee to implement a Message Queue. The challenges here is that the message queue is highly sensitive to locking multiple entries in the queue so that they can be processed. As the items are processed their entries to have status updates so that in the case of a power failure, the system has enough information to pick up where it left off.

It looks like the `atomic` functionality will do what I need it to. But as a novice I lack the knowledge on whether my solution will be proper enough. Or whether there is some hidden, large grain implicit table lock that won't show a problem until the point of launch when the database starts getting hot.

I believe it would really help PeeWee's adoption if a simple, performant Message Queue implementation was added to the examples. This would allow dumb people like me to build off an idiomatic implementation and incrementally add features until it's satisfies the requirements.

Thanks again!

I'm not going to do your homework for you, but there is a ton of discussion on this on the web. Some of it depends on what database you use, postgres probably being the most commonly discussed.

* https://github.com/que-rb/que - https://github.com/que-rb/que/blob/master/lib/que/poller.rb#L60-L113
* https://github.com/QueueClassic/queue_classic - https://github.com/QueueClassic/queue_classic/blob/master/lib/queue_classic/queue.rb#L92-L111

If you're looking for something simpler, you can always refer to this, which is Peewee-specific:

https://github.com/coleifer/huey/blob/master/huey/contrib/sql_huey.py

> I'm not going to do your homework for you, but there is a ton of discussion on this on the web.

Dude, I've been a software developer for nearly 20 years in embedded land. It's possible one can be a novice to databases and also not a student, at the same time.
Issue: AttributeError when calling len of model
Author: ghost
State: closed
Created: 2022-08-26 15:52:45

I'm getting an AttributeError when I call len on my Model class. Weirdly this occurs when I run a test in the pycharm debugger but not when I run the same test outside the debugger. 

Python Version 3.10.4
Peewee Version 3.15.1

```
../../src/stores/fs.py:79: in rows
    return len(self.model)
../../venv/lib/python3.10/site-packages/peewee.py:6366: in __len__
    return self.select().count()
../../venv/lib/python3.10/site-packages/peewee.py:1931: in inner
    return method(self, database, *args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <peewee.ModelSelect object at 0x1112fdf30>
database = <playhouse.apsw_ext.APSWDatabase object at 0x10f42bb50>
clear_limit = False

    @database_required
    def count(self, database, clear_limit=False):
        clone = self.order_by().alias('_wrapped')
        if clear_limit:
            clone._limit = clone._offset = None
        try:
            if clone._having is None and clone._group_by is None and \
               clone._windows is None and clone._distinct is None and \
               clone._simple_distinct is not True:
                clone = clone.select(SQL('1'))
        except AttributeError:
            print("AttributeError")
            pass
        print(f"2211 peewee - {fn.__name__}")
        print(dir(fn))
>       return Select([clone], [fn.COUNT(SQL('1'))]).scalar(database)
E       AttributeError: 'builtin_function_or_method' object has no attribute 'COUNT'

../../venv/lib/python3.10/site-packages/peewee.py:2214: AttributeError
```

Instead of calling `len(model)` I am successfully able to run `len(model.select())` but `model.select().count()` raises the AttributeError. 

Not sure if this is helpful, but I tried putting a print message in the ModelSelect.count() function

```
print(f"2211 peewee - {fn.__name__}")
return Select([clone], [fn.COUNT(SQL('1'))]).scalar(database)
```

Which in failing debug mode outputs
```
2211 peewee - peewee_rank
```

Otherwise this outputs
```
2211 peewee - <function Function.__getattr__.<locals>.decorator at 0x1120352d0>
```

I can provide more info if needed.

This looks like some issue with your debugger / pycharm. The `__len__()` / `count` behavior is covered by unit tests. `peewee.fn` is an instance of `Function` and implements some magic -- possibly your debugger is shadowing the `fn` name. Not sure.

I think that must be something to do with it. I tried renaming global 'fn' to 'fn_p' in peewee.py and it succeeded, so it does seem to being overwritten. I've tried tracing this through a bit and it seems that during the initialisation of the `SqliteExtDatabase` the registering of the cython rank functions results in the global fn being overwritten. I cannot understand how this would be happening.
Issue: Pylint complains that subclasses of `ColumnBase` leave `__iter__()` unimplemented
Author: jborman-exos
State: closed
Created: 2022-08-26 15:29:49

Related to the fix released for #2606, running pylint over a local subclass of `ColumnBase` raises the following:

```
Method '__iter__' is abstract in class 'ColumnBase' but is not overridden (abstract-method)
```

Raising `NotimplementedError` implies that a concrete class is meant to implement this abstract method. According to the python [docs](https://docs.python.org/3/library/exceptions.html#NotImplementedError), if `__iter__` is never to be implemented on a subclass of `ColumnBase`, then it should be set to `None`

No problem, fixed.
Issue: Add `SelectBase.scalars()`
Author: ilyakamens
State: closed
Created: 2022-08-26 14:17:06

Mr. Leifer, thanks for creating and maintaining such a great library. We've used peewee at Propel since 2015.

This PR adds `SelectBase.scalars()`, which is something we've wanted for a while. `scalar()` is very convenient, and we thought something similar for multiple values would be too. I've proposed `scalars()` as the name, but would be happy to settle on something else if you prefer.

Thanks for considering.

There are a few issues with your patch - it consumes the entire cursor in one go and returns a list, when it would probably be better suited to use a generator. Similarly, it is not carrying through the `database` parameter. I will implement a similar patch and merge it.

Thank you!