add related model load tests

This commit is contained in:
collerek
2021-03-10 14:01:16 +01:00
parent 01904580e5
commit 50ddd1c2bb
5 changed files with 45 additions and 11 deletions

View File

@ -472,6 +472,7 @@ Available Model Fields (with required args - optional ones in docs):
* `Decimal(scale, precision)` * `Decimal(scale, precision)`
* `UUID()` * `UUID()`
* `EnumField` - by passing `choices` to any other Field type * `EnumField` - by passing `choices` to any other Field type
* `EncryptedString` - by passing `encrypt_secret` and `encrypt_backend`
* `ForeignKey(to)` * `ForeignKey(to)`
* `ManyToMany(to, through)` * `ManyToMany(to, through)`

View File

@ -55,7 +55,7 @@ Note that since this backend never decrypt the stored value it's only applicable
!!!note !!!note
Note that provided `encrypt_secret` is first hashed itself and used as salt, so in order to Note that provided `encrypt_secret` is first hashed itself and used as salt, so in order to
compare to stored string you need to recreate this steps. compare to stored string you need to recreate this steps. The `order_by` will not work as encrypted strings are compared so you cannot reliably order by.
```python ```python
class Hash(ormar.Model): class Hash(ormar.Model):
@ -101,7 +101,8 @@ Value is encrypted on way to database end decrypted on way out. Can be used on a
as the returned value is parsed to corresponding python type. as the returned value is parsed to corresponding python type.
!!!warning !!!warning
Note that in `FERNET` backend you loose `filtering` possibility altogether as part of the encrypted value is a timestamp Note that in `FERNET` backend you loose `filter`ing possibility altogether as part of the encrypted value is a timestamp.
The same goes for `order_by` as encrypted strings are compared so you cannot reliably order by.
```python ```python
class Filter(ormar.Model): class Filter(ormar.Model):

View File

@ -472,6 +472,7 @@ Available Model Fields (with required args - optional ones in docs):
* `Decimal(scale, precision)` * `Decimal(scale, precision)`
* `UUID()` * `UUID()`
* `EnumField` - by passing `choices` to any other Field type * `EnumField` - by passing `choices` to any other Field type
* `EncryptedString` - by passing `encrypt_secret` and `encrypt_backend`
* `ForeignKey(to)` * `ForeignKey(to)`
* `ManyToMany(to, through)` * `ManyToMany(to, through)`

View File

@ -144,7 +144,7 @@ class EncryptedString(types.TypeDecorator):
self.type_: Any = type_ self.type_: Any = type_
def __repr__(self) -> str: # pragma: nocover def __repr__(self) -> str: # pragma: nocover
return f"TEXT()" return "TEXT()"
def load_dialect_impl(self, dialect: DefaultDialect) -> Any: def load_dialect_impl(self, dialect: DefaultDialect) -> Any:
return dialect.type_descriptor(types.TEXT()) return dialect.type_descriptor(types.TEXT())

View File

@ -77,14 +77,6 @@ class Author(ormar.Model):
) )
class Filter(ormar.Model):
class Meta(BaseMeta):
tablename = "filters"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, **default_fernet)
class Hash(ormar.Model): class Hash(ormar.Model):
class Meta(BaseMeta): class Meta(BaseMeta):
tablename = "hashes" tablename = "hashes"
@ -97,6 +89,24 @@ class Hash(ormar.Model):
) )
class Filter(ormar.Model):
class Meta(BaseMeta):
tablename = "filters"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, **default_fernet)
hash = ormar.ForeignKey(Hash)
class Report(ormar.Model):
class Meta(BaseMeta):
tablename = "reports"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
filters = ormar.ManyToMany(Filter)
@pytest.fixture(autouse=True, scope="module") @pytest.fixture(autouse=True, scope="module")
def create_test_database(): def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL) engine = sqlalchemy.create_engine(DATABASE_URL)
@ -236,3 +246,24 @@ async def test_hash_filters_works():
with pytest.raises(NoMatch): with pytest.raises(NoMatch):
await Filter.objects.get(name__icontains="test") await Filter.objects.get(name__icontains="test")
@pytest.mark.asyncio
async def test_related_model_fields_properly_decrypted():
async with database:
hash1 = await Hash(name="test1").save()
report = await Report.objects.create(name="Report1")
await report.filters.create(name="test1", hash=hash1)
await report.filters.create(name="test2")
report2 = await Report.objects.select_related("filters").get()
assert report2.filters[0].name == "test1"
assert report2.filters[1].name == "test2"
secret = hashlib.sha256("udxc32".encode()).digest()
secret = base64.urlsafe_b64encode(secret)
hashed_test1 = hashlib.sha512(secret + "test1".encode()).hexdigest()
report2 = await Report.objects.select_related("filters__hash").get()
assert report2.filters[0].name == "test1"
assert report2.filters[0].hash.name == hashed_test1