Issue: Custom uint64 field and foreign keys
Author: user5642
State: closed
Created: 2015-12-14 09:05:21

I needed uint64 field as primary key field for my database, expectedly other models used foreign key to point to this model. Resulting code was something like this:

```
from peewee import *

db = SqliteDatabase('test.db')

class UInt64(Field):
    db_field = 'bigint'

    def db_value(self, value):
        return value - (1<<63)

    def python_value(self, value):
        return value + (1<<63)

class Foo(Model):
    id = UInt64(primary_key = True)

    class Meta:
        database = db

class Bar(Model):
    id = UInt64(primary_key = True)
    foo = ForeignKeyField(Foo, related_name='bars')

    class Meta:
        database = db
```

This code works properly for all requests used by me expect one:

```
>>> f = Foo.create(id=10000)
>>> Bar.create(id=123, foo=f)
<ptest.Bar object at 0x7f2fde7f7e10>
>>> b = Bar.get()
>>> b.id
123
>>> b.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 1151, in __get__
    return self.get_object_or_id(instance)
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 1142, in get_object_or_id
    obj = self.rel_model.get(self.field.to_field == rel_id)
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 4403, in get
    return sq.get()
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 2812, in get
    return clone.execute().next()
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 2859, in execute
    self._qr = ResultWrapper(model_class, self._execute(), query_meta)
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 2555, in _execute
    return self.database.execute_sql(sql, params, self.require_commit)
  File "/usr/local/lib/python3.4/dist-packages/peewee.py", line 3359, in execute_sql
    cursor.execute(sql, params or ())
OverflowError: Python int too large to convert to SQLite INTEGER
```

It seems that somewhere conversion between python value and db value was not done properly. Looks like a bug to me.


SQLite does not have a bigint type natively. Check out the sqlite docs on type affinities. Regarding the specific issue it sounds to me like it's coming from the SQLite driver and not peewee.


Sorry it was not the best illustration of problem encountered by me. This code should be more demonstrative:

```
from peewee import *

db = SqliteDatabase('test.db')

class UInt32(Field):
    db_field = 'int'

    def db_value(self, value):
        return value - (1<<31)

    def python_value(self, value):
        return value + (1<<31)

class Foo(Model):
    id = UInt32(primary_key = True)

    class Meta:
        database = db

class Bar(Model):
    id = UInt32(primary_key = True)
    foo = ForeignKeyField(Foo, related_name='bars')

    class Meta:
        database = db

db.create_tables([Foo, Bar])

f = Foo.create(id=(1<<31)+1000)
Bar.create(id=0, foo=f)
b = Bar.get()
b.foo
```

This will fall with following error:

```
__main__.FooDoesNotExist: Instance matching query does not exist:
SQL: SELECT "t1"."id" FROM "foo" AS t1 WHERE ("t1"."id" = ?)
PARAMS: [-2147484648]
```

It's clear that it tries to query for entry with id equal to 1000 - (1<<31) instead of 1000. So it's seems that peewee applies `db_value` where it should not. (or other way around not applying `python_value` where it should have)


It applies db_value when sending values to the database. So technically it shouldn't be necessary to do the conversion yourself when calling create(), right?


"Python id" is equal to (1<<31)+1000 = 2147484648 (I specially chose value outside of signed int32 range for demonstration purposes)
"Database id" is equal to (1<<31)+1000 - (1<<31) = 1000 (record with such id is successfully created in DB)
But `b.foo` for some reason makes query for id -2147484648 = 1000 -  (1<<31) , not for 1000 as it should have been.


Maybe try this:

``` python
class UInt32(Field):
    db_field = 'int'

    def db_value(self, value):
        return long(value - (1<<31))

    def python_value(self, value):
        return long(value + (1<<31))
```


Actually, I think you may be onto something, I'm looking into it.


Alright, I believe the commit in 9e9e0bd should have things fixed.


Now it works as expected.Thank you!