Repository: tophat/ormar-postgres-extensions (language: python)

Issue:
Array Field Aesthetic Suggestion (Not a Bug)
### Describe the bug

The current Array field examples have:
```python
await ModelWithArray.objects.filter(
  ModelWithArray.data.array_contained_by(["a"])
).all()
```

but as per my test this also works as is:
```python
await ModelWithArray.objects.filter(data__array_contained_by=["a"]).all()
```
The latter seems like a better representation consistent with ormar. Need to change tests and Readme.

### To reproduce

Run tests by replacing current format with this one:
```python
await ModelWithArray.objects.filter(data__array_contained_by=["a"]).all()
```

### Expected behaviour

After replacing with suggested format the tests pass and functionality is maintained.

### Ormar PostgreSQL Extensions Version

v2.1.0

### Ormar Version

v0.10.24

### PostgreSQL Version

13

### Additional Context

_No response_


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 62c025a..9ebbf6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,5 @@ ENV/
 env.bak/
 venv.bak/
 .vscode
+
+.idea
\ No newline at end of file
diff --git a/README.md b/README.md
index af9ec24..363a445 100644
--- a/README.md
+++ b/README.md
@@ -28,25 +28,35 @@ python -m pip install ormar-postgres-extensions
 
 Three native PG fields are provided. The `JSONB` and `UUID` types map to native `JSONB` and `UUID` data types respectively. The `Array` type can be used to create an array column. Using these in an Ormar model is as simple as importing the fields and using them in the model.
 
+#### UUID
+
 ```python
 from uuid import UUID
 
 import ormar
-from ormar_postgres_extensions import PostgresUUID
+import ormar_postgres_extensions as ormar_pg_ext
 
 
 class MyModel(ormar.Model):
-    uid: UUID = PostgresUUID(unique=True, nullable=False)
+    uuid: UUID = ormar_pg_ext.UUID(unique=True, nullable=False)
 ```
+#### JSONB
+```python
+import ormar
+import ormar_postgres_extensions as ormar_pg_ext
 
-#### Array Fields
+class JSONBTestModel(ormar.Model):
+    id: int = ormar.Integer(primary_key=True)
+    data: dict = ormar_pg_ext.JSONB()
+```
+#### Array
 
-Array fields require a bit more setup to pass the type of the array into the field
+Array field requires a bit more setup to pass the type of the array into the field
 
 ```python
 import ormar
 import sqlalchemy
-from ormar_postgres_extensions import Array
+import ormar_postgres_extensions as ormar_pg_ext
 
 class ModelWithArray(ormar.Model):
     class Meta:
@@ -54,7 +64,7 @@ class ModelWithArray(ormar.Model):
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: list = Array(item_type=sqlalchemy.String())
+    data: list = ormar_pg_ext.ARRAY(item_type=sqlalchemy.String())
 ```
 
 Arrays have access to three special methods that map to specific PostgreSQL array functions
@@ -64,9 +74,7 @@ Arrays have access to three special methods that map to specific PostgreSQL arra
 The maps to the [`contained_by`](https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#sqlalchemy.dialects.postgresql.ARRAY.Comparator.contained_by) operator in Postgres.
 
 ```python
-await ModelWithArray.objects.filter(
-  ModelWithArray.data.array_contained_by(["a"])
-).all()
+await ModelWithArray.objects.filter(data__array_contained_by=["a"]).all()
 ```
 
 ##### array_contains
@@ -74,9 +82,7 @@ await ModelWithArray.objects.filter(
 The maps to the [`contains`](https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#sqlalchemy.dialects.postgresql.ARRAY.Comparator.contains) operator in Postgres.
 
 ```python
-await ModelWithArray.objects.filter(
-  ModelWithArray.data.array_contains(["a"])
-).all()
+await ModelWithArray.objects.filter(data__array_contains=["a"]).all()
 ```
 
 ##### array_overlap
@@ -84,9 +90,7 @@ await ModelWithArray.objects.filter(
 The maps to the [`overlap`](https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#sqlalchemy.dialects.postgresql.ARRAY.Comparator.overlap) operator in Postgres.
 
 ```python
-await ModelWithArray.objects.filter(
-  ModelWithArray.data.array_overlap(["a"])
-).all()
+await ModelWithArray.objects.filter(data__array_overlap=["a"]).all()
 ```
 
 ## Uninstalling
diff --git a/dev_requirements.txt b/dev_requirements.txt
index 22e490a..13b1c41 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -11,7 +11,7 @@ asyncpg==0.25.0
     # via databases
 attrs==21.4.0
     # via pytest
-black==21.12b0
+black==22.1.0
     # via ormar-postgres-extensions
 bleach==4.1.0
     # via readme-renderer


Test patch:
diff --git a/tests/fields/test_array.py b/tests/fields/test_array.py
index aafa2b0..7927934 100644
--- a/tests/fields/test_array.py
+++ b/tests/fields/test_array.py
@@ -2,12 +2,9 @@
 
 import ormar
 import pytest
-from sqlalchemy import (
-    Integer,
-    String,
-)
+import sqlalchemy
 
-from ormar_postgres_extensions.fields import ARRAY
+import ormar_postgres_extensions as ormar_pg_ext
 from tests.database import (
     database,
     metadata,
@@ -20,7 +17,8 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: list = ARRAY(item_type=String())
+    data: list = ormar_pg_ext.ARRAY(item_type=sqlalchemy.String())
+    thing: str = ormar.String(max_length=20, nullable=True)
 
 
 class MultiDimensionArrayTestModel(ormar.Model):
@@ -29,7 +27,7 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: list = ARRAY(item_type=Integer(), dimensions=2)
+    data: list = ormar_pg_ext.ARRAY(item_type=sqlalchemy.Integer(), dimensions=2)
 
 
 class NullableArrayTestModel(ormar.Model):
@@ -38,17 +36,20 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: Optional[list] = ARRAY(item_type=String(), nullable=True)
+    data: Optional[list] = ormar_pg_ext.ARRAY(
+        item_type=sqlalchemy.String(), nullable=True
+    )
 
 
 @pytest.mark.asyncio
 async def test_create_model_with_array(db):
-    created = await ArrayTestModel(data=["a", "b"]).save()
+    created = await ArrayTestModel(data=["a", "b"], thing="test thing").save()
     assert created.data == ["a", "b"]
 
     # Confirm the model got saved to the DB by querying it back
     found = await ArrayTestModel.objects.get()
     assert found.data == ["a", "b"]
+    assert found.thing == "test thing"
 
 
 @pytest.mark.asyncio
@@ -62,24 +63,31 @@ async def test_create_model_with_nullable_array(db):
 
 @pytest.mark.asyncio
 async def test_filter_array_column_contains(db):
-    await ArrayTestModel(data=["a", "b"]).save()
+    await ArrayTestModel(data=["a", "b"], thing="test thing 2").save()
     await ArrayTestModel(data=["c"]).save()
 
-    found = await ArrayTestModel.objects.filter(
-        (ArrayTestModel.data.array_contains(["c"]))
-    ).all()
+    found = await ArrayTestModel.objects.filter(data__array_contains=["c"]).all()
 
     assert len(found) == 1
     assert found[0].data == ["c"]
 
+    found = await ArrayTestModel.objects.filter(thing="test thing 2").all()
+    assert len(found) == 1
+    assert found[0].data == ["a", "b"]
+
 
 @pytest.mark.asyncio
 async def test_filter_array_column_contains_multiple(db):
     await ArrayTestModel(data=["a", "b"]).save()
-    await ArrayTestModel(data=["c", "d"]).save()
+    await ArrayTestModel(data=["c", "d"], thing="test thing 2").save()
+
+    found = await ArrayTestModel.objects.filter(data__array_contains=["a"]).all()
+
+    assert len(found) == 1
+    assert found[0].data == ["a", "b"]
 
     found = await ArrayTestModel.objects.filter(
-        (ArrayTestModel.data.array_contains(["c"]))
+        data__array_contains=["c"], thing="test thing 2"
     ).all()
 
     assert len(found) == 1
@@ -88,52 +96,79 @@ async def test_filter_array_column_contains_multiple(db):
 
 @pytest.mark.asyncio
 async def test_filter_array_column_contained_by(db):
-    await ArrayTestModel(data=["a", "b"]).save()
+    await ArrayTestModel(data=["a", "b"], thing="aa").save()
     await ArrayTestModel(data=["c"]).save()
-    await ArrayTestModel(data=["c", "d"]).save()
+    await ArrayTestModel(data=["c", "d"], thing="aa").save()
     await ArrayTestModel(data=["c", "d", "e"]).save()
 
     found = await ArrayTestModel.objects.filter(
-        (ArrayTestModel.data.array_contained_by(["c", "d"]))
+        data__array_contained_by=["c", "d"]
     ).all()
 
     assert len(found) == 2
     assert found[0].data == ["c"]
     assert found[1].data == ["c", "d"]
 
+    found = await ArrayTestModel.objects.filter(
+        data__array_contained_by=["c", "d"], thing="aa"
+    ).all()
+
+    assert len(found) == 1
+    assert found[0].data == ["c", "d"]
+
 
 @pytest.mark.asyncio
 async def test_filter_array_column_overlap(db):
-    await ArrayTestModel(data=["a", "b"]).save()
-    await ArrayTestModel(data=["c"]).save()
-    await ArrayTestModel(data=["c", "d"]).save()
-    await ArrayTestModel(data=["c", "d", "e"]).save()
+    await ArrayTestModel(data=["a", "b"], thing="aa").save()
+    await ArrayTestModel(data=["c"], thing="aa").save()
+    await ArrayTestModel(data=["c", "d"], thing="cc").save()
+    await ArrayTestModel(data=["c", "d", "e"], thing="aa").save()
 
-    found = await ArrayTestModel.objects.filter(
-        (ArrayTestModel.data.array_overlap(["a", "d"]))
-    ).all()
+    found = await ArrayTestModel.objects.filter(data__array_overlap=["a", "d"]).all()
 
     assert len(found) == 3
     assert found[0].data == ["a", "b"]
     assert found[1].data == ["c", "d"]
     assert found[2].data == ["c", "d", "e"]
 
+    found = await ArrayTestModel.objects.filter(
+        data__array_overlap=["a", "d"], thing="aa"
+    ).all()
+
+    assert len(found) == 2
+    assert found[0].data == ["a", "b"]
+    assert found[1].data == ["c", "d", "e"]
+
 
 @pytest.mark.asyncio
 async def test_filter_array_column_equals(db):
-    await ArrayTestModel(data=["a", "b"]).save()
+    await ArrayTestModel(data=["a", "b"], thing="aa").save()
     await ArrayTestModel(data=["c", "d"]).save()
 
-    found = await ArrayTestModel.objects.filter(
-        (ArrayTestModel.data == ["c", "d"])
-    ).all()
+    found = await ArrayTestModel.objects.filter(data=["c", "d"]).all()
 
     assert len(found) == 1
     assert found[0].data == ["c", "d"]
 
-    found = await ArrayTestModel.objects.filter((ArrayTestModel.data == ["c"])).all()
+    found = await ArrayTestModel.objects.filter(data=["c"]).all()
     assert len(found) == 0
 
+    found = await ArrayTestModel.objects.filter(data=["c", "d"], thing="aa").all()
+
+    assert len(found) == 0
+
+    found = await ArrayTestModel.objects.filter(data=["a", "b"], thing="aa").all()
+
+    assert len(found) == 1
+    assert found[0].data == ["a", "b"]
+    assert found[0].thing == "aa"
+
+    found = await ArrayTestModel.objects.filter(thing="aa").all()
+
+    assert len(found) == 1
+    assert found[0].data == ["a", "b"]
+    assert found[0].thing == "aa"
+
 
 @pytest.mark.asyncio
 async def test_multi_dimensional_array(db):
diff --git a/tests/fields/test_jsonb.py b/tests/fields/test_jsonb.py
index 2aaed8a..201a36f 100644
--- a/tests/fields/test_jsonb.py
+++ b/tests/fields/test_jsonb.py
@@ -4,7 +4,7 @@
 import ormar
 import pytest
 
-from ormar_postgres_extensions.fields import JSONB
+import ormar_postgres_extensions as ormar_pg_ext
 from tests.database import (
     database,
     metadata,
@@ -17,7 +17,7 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: dict = JSONB()
+    data: dict = ormar_pg_ext.JSONB()
 
 
 class NullableJSONBTestModel(ormar.Model):
@@ -26,7 +26,7 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    data: Optional[dict] = JSONB(nullable=True)
+    data: Optional[dict] = ormar_pg_ext.JSONB(nullable=True)
 
 
 @pytest.mark.asyncio
diff --git a/tests/fields/test_uuid.py b/tests/fields/test_uuid.py
index 3f614e7..1e087a3 100644
--- a/tests/fields/test_uuid.py
+++ b/tests/fields/test_uuid.py
@@ -7,7 +7,7 @@
 import ormar
 import pytest
 
-from ormar_postgres_extensions.fields import UUID as UUIDField
+import ormar_postgres_extensions as ormar_pg_ext
 from tests.database import (
     database,
     metadata,
@@ -20,7 +20,7 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    uid: UUID = UUIDField(default=uuid4)
+    uid: UUID = ormar_pg_ext.UUID(default=uuid4)
 
 
 class NullableUUIDTestModel(ormar.Model):
@@ -29,7 +29,7 @@ class Meta:
         metadata = metadata
 
     id: int = ormar.Integer(primary_key=True)
-    uid: Optional[UUID] = UUIDField(nullable=True)
+    uid: Optional[UUID] = ormar_pg_ext.UUID(nullable=True)
 
 
 @pytest.mark.asyncio