add related model load tests
This commit is contained in:
@ -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)`
|
||||||
|
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
@ -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)`
|
||||||
|
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user