refactor merging of instances from queryset to fakepydantic
This commit is contained in:
18
README.md
18
README.md
@ -10,6 +10,9 @@
|
|||||||
<a href="https://www.codefactor.io/repository/github/collerek/async-orm">
|
<a href="https://www.codefactor.io/repository/github/collerek/async-orm">
|
||||||
<img src="https://www.codefactor.io/repository/github/collerek/async-orm/badge" alt="CodeFactor" />
|
<img src="https://www.codefactor.io/repository/github/collerek/async-orm/badge" alt="CodeFactor" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://app.codacy.com/manual/collerek/async-orm?utm_source=github.com&utm_medium=referral&utm_content=collerek/async-orm&utm_campaign=Badge_Grade_Dashboard">
|
||||||
|
<img src="https://api.codacy.com/project/badge/Grade/62568734f70f49cd8ea7a1a0b2d0c107" alt="Codacy" />
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
The `async-orm` package is an async ORM for Python, with support for Postgres,
|
The `async-orm` package is an async ORM for Python, with support for Postgres,
|
||||||
@ -26,7 +29,7 @@ The goal was to create a simple orm that can be used directly with [`fastapi`][f
|
|||||||
Initial work was inspired by [`encode/orm`][encode/orm].
|
Initial work was inspired by [`encode/orm`][encode/orm].
|
||||||
The encode package was too simple (i.e. no ability to join two times to the same table) and used typesystem for data checks.
|
The encode package was too simple (i.e. no ability to join two times to the same table) and used typesystem for data checks.
|
||||||
|
|
||||||
**aysn-orm is still under development:** We recommend pinning any dependencies with `aorm~=0.0.1`
|
**async-orm is still under development:** We recommend pinning any dependencies with `aorm~=0.0.1`
|
||||||
|
|
||||||
**Note**: Use `ipython` to try this from the console, since it supports `await`.
|
**Note**: Use `ipython` to try this from the console, since it supports `await`.
|
||||||
|
|
||||||
@ -44,8 +47,9 @@ class Note(orm.Model):
|
|||||||
__database__ = database
|
__database__ = database
|
||||||
__metadata__ = metadata
|
__metadata__ = metadata
|
||||||
|
|
||||||
|
# primary keys of type int by dafault are set to autoincrement
|
||||||
id = orm.Integer(primary_key=True)
|
id = orm.Integer(primary_key=True)
|
||||||
text = orm.String(max_length=100)
|
text = orm.String(length=100)
|
||||||
completed = orm.Boolean(default=False)
|
completed = orm.Boolean(default=False)
|
||||||
|
|
||||||
# Create the database
|
# Create the database
|
||||||
@ -97,7 +101,7 @@ class Album(orm.Model):
|
|||||||
__database__ = database
|
__database__ = database
|
||||||
|
|
||||||
id = orm.Integer(primary_key=True)
|
id = orm.Integer(primary_key=True)
|
||||||
name = orm.String(max_length=100)
|
name = orm.String(length=100)
|
||||||
|
|
||||||
|
|
||||||
class Track(orm.Model):
|
class Track(orm.Model):
|
||||||
@ -107,7 +111,7 @@ class Track(orm.Model):
|
|||||||
|
|
||||||
id = orm.Integer(primary_key=True)
|
id = orm.Integer(primary_key=True)
|
||||||
album = orm.ForeignKey(Album)
|
album = orm.ForeignKey(Album)
|
||||||
title = orm.String(max_length=100)
|
title = orm.String(length=100)
|
||||||
position = orm.Integer()
|
position = orm.Integer()
|
||||||
|
|
||||||
|
|
||||||
@ -138,6 +142,12 @@ assert track.album.name == "Malibu"
|
|||||||
track = await Track.objects.select_related("album").get(title="The Bird")
|
track = await Track.objects.select_related("album").get(title="The Bird")
|
||||||
assert track.album.name == "Malibu"
|
assert track.album.name == "Malibu"
|
||||||
|
|
||||||
|
# By default you also get a second side of the relation
|
||||||
|
# constructed as lowercase source model name +'s' (tracks in this case)
|
||||||
|
# you can also provide custom name with parameter related_name
|
||||||
|
album = await Album.objects.select_related("tracks").all()
|
||||||
|
assert len(album.tracks) == 3
|
||||||
|
|
||||||
# Fetch instances, with a filter across an FK relationship.
|
# Fetch instances, with a filter across an FK relationship.
|
||||||
tracks = Track.objects.filter(album__name="Fantasies")
|
tracks = Track.objects.filter(album__name="Fantasies")
|
||||||
assert len(tracks) == 2
|
assert len(tracks) == 2
|
||||||
|
|||||||
@ -249,8 +249,10 @@ class ForeignKey(BaseField):
|
|||||||
else:
|
else:
|
||||||
if not isinstance(value, self.to.pk_type()):
|
if not isinstance(value, self.to.pk_type()):
|
||||||
raise RelationshipInstanceError(
|
raise RelationshipInstanceError(
|
||||||
f"Relationship error - ForeignKey {self.to.__name__} is of type {self.to.pk_type()} "
|
f"Relationship error - ForeignKey {self.to.__name__} "
|
||||||
f"of type {self.__type__} while {type(value)} passed as a parameter."
|
f"is of type {self.to.pk_type()} "
|
||||||
|
f"of type {self.__type__} "
|
||||||
|
f"while {type(value)} passed as a parameter."
|
||||||
)
|
)
|
||||||
model = create_dummy_instance(fk=self.to, pk=value)
|
model = create_dummy_instance(fk=self.to, pk=value)
|
||||||
|
|
||||||
|
|||||||
@ -210,7 +210,7 @@ class FakePydantic(list, metaclass=ModelMetaclass):
|
|||||||
return self.__table__.primary_key.columns.values()[0]
|
return self.__table__.primary_key.columns.values()[0]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pk_type(cls):
|
def pk_type(cls) -> Any:
|
||||||
return cls.__model_fields__[cls.__pkname__].__type__
|
return cls.__model_fields__[cls.__pkname__].__type__
|
||||||
|
|
||||||
def dict(self) -> Dict: # noqa: A003
|
def dict(self) -> Dict: # noqa: A003
|
||||||
@ -259,6 +259,35 @@ class FakePydantic(list, metaclass=ModelMetaclass):
|
|||||||
)
|
)
|
||||||
return self_fields
|
return self_fields
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def merge_instances_list(cls, result_rows: List["Model"]) -> List["Model"]:
|
||||||
|
merged_rows = []
|
||||||
|
for index, model in enumerate(result_rows):
|
||||||
|
if index > 0 and model.pk == result_rows[index - 1].pk:
|
||||||
|
result_rows[-1] = cls.merge_two_instances(model, merged_rows[-1])
|
||||||
|
else:
|
||||||
|
merged_rows.append(model)
|
||||||
|
return merged_rows
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def merge_two_instances(cls, one: "Model", other: "Model") -> "Model":
|
||||||
|
for field in one.__model_fields__.keys():
|
||||||
|
# print(field, one.dict(), other.dict())
|
||||||
|
if isinstance(getattr(one, field), list) and not isinstance(
|
||||||
|
getattr(one, field), Model
|
||||||
|
):
|
||||||
|
setattr(other, field, getattr(one, field) + getattr(other, field))
|
||||||
|
elif isinstance(getattr(one, field), Model):
|
||||||
|
if getattr(one, field).pk == getattr(other, field).pk:
|
||||||
|
setattr(
|
||||||
|
other,
|
||||||
|
field,
|
||||||
|
cls.merge_two_instances(
|
||||||
|
getattr(one, field), getattr(other, field)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return other
|
||||||
|
|
||||||
|
|
||||||
class Model(FakePydantic):
|
class Model(FakePydantic):
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|||||||
@ -479,39 +479,10 @@ class QuerySet:
|
|||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
|
|
||||||
result_rows = self.merge_result_rows(result_rows)
|
result_rows = self.model_cls.merge_instances_list(result_rows)
|
||||||
|
|
||||||
return result_rows
|
return result_rows
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def merge_result_rows(cls, result_rows: List["Model"]) -> List["Model"]:
|
|
||||||
merged_rows = []
|
|
||||||
for index, model in enumerate(result_rows):
|
|
||||||
if index > 0 and model.pk == result_rows[index - 1].pk:
|
|
||||||
result_rows[-1] = cls.merge_two_instances(model, merged_rows[-1])
|
|
||||||
else:
|
|
||||||
merged_rows.append(model)
|
|
||||||
return merged_rows
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def merge_two_instances(cls, one: "Model", other: "Model") -> "Model":
|
|
||||||
for field in one.__model_fields__.keys():
|
|
||||||
# print(field, one.dict(), other.dict())
|
|
||||||
if isinstance(getattr(one, field), list) and not isinstance(
|
|
||||||
getattr(one, field), orm.models.Model
|
|
||||||
):
|
|
||||||
setattr(other, field, getattr(one, field) + getattr(other, field))
|
|
||||||
elif isinstance(getattr(one, field), orm.models.Model):
|
|
||||||
if getattr(one, field).pk == getattr(other, field).pk:
|
|
||||||
setattr(
|
|
||||||
other,
|
|
||||||
field,
|
|
||||||
cls.merge_two_instances(
|
|
||||||
getattr(one, field), getattr(other, field)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return other
|
|
||||||
|
|
||||||
async def create(self, **kwargs: Any) -> "Model":
|
async def create(self, **kwargs: Any) -> "Model":
|
||||||
|
|
||||||
new_kwargs = dict(**kwargs)
|
new_kwargs = dict(**kwargs)
|
||||||
|
|||||||
Reference in New Issue
Block a user