fix too long fk names
This commit is contained in:
@ -10,6 +10,13 @@ Each model instance have a set of methods to `save`, `update` or `load` itself.
|
|||||||
|
|
||||||
Available methods are described below.
|
Available methods are described below.
|
||||||
|
|
||||||
|
## `pydantic` methods
|
||||||
|
|
||||||
|
Note that each `ormar.Model` is also a `pydantic.BaseModel`, so all `pydantic` methods are also available on a model,
|
||||||
|
especially `dict()` and `json()` methods that can also accept `exclude`, `include` and other parameters.
|
||||||
|
|
||||||
|
To read more check [pydantic][pydantic] documentation
|
||||||
|
|
||||||
## load
|
## load
|
||||||
|
|
||||||
By default when you query a table without prefetching related models, the ormar will still construct
|
By default when you query a table without prefetching related models, the ormar will still construct
|
||||||
@ -127,7 +134,7 @@ await track.delete() # will delete the model from database
|
|||||||
|
|
||||||
## save_related
|
## save_related
|
||||||
|
|
||||||
`save_related(follow: bool = False) -> None`
|
`save_related(follow: bool = False, save_all: bool = False, exclude=Optional[Union[Set, Dict]]) -> None`
|
||||||
|
|
||||||
Method goes through all relations of the `Model` on which the method is called,
|
Method goes through all relations of the `Model` on which the method is called,
|
||||||
and calls `upsert()` method on each model that is **not** saved.
|
and calls `upsert()` method on each model that is **not** saved.
|
||||||
@ -138,17 +145,28 @@ By default the `save_related` method saved only models that are directly related
|
|||||||
|
|
||||||
But you can specify the `follow=True` parameter to traverse through nested models and save all of them in the relation tree.
|
But you can specify the `follow=True` parameter to traverse through nested models and save all of them in the relation tree.
|
||||||
|
|
||||||
|
By default save_related saves only model that has not `saved` status, meaning that they were modified in current scope.
|
||||||
|
|
||||||
|
If you want to force saving all of the related methods use `save_all=True` flag, which will upsert all related models, regardless of their save status.
|
||||||
|
|
||||||
|
If you want to skip saving some of the relations you can pass `exclude` parameter.
|
||||||
|
|
||||||
|
`Exclude` can be a set of own model relations,
|
||||||
|
or it can be a dictionary that can also contain nested items.
|
||||||
|
|
||||||
|
!!!note
|
||||||
|
Note that `exclude` parameter in `save_related` accepts only relation fields names, so
|
||||||
|
if you pass any other fields they will be saved anyway
|
||||||
|
|
||||||
|
!!!note
|
||||||
|
To read more about the structure of possible values passed to `exclude` check `Queryset.fields` method documentation.
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
To avoid circular updates with `follow=True` set, `save_related` keeps a set of already visited Models,
|
To avoid circular updates with `follow=True` set, `save_related` keeps a set of already visited Models,
|
||||||
and won't perform nested `save_related` on Models that were already visited.
|
and won't perform nested `save_related` on Models that were already visited.
|
||||||
|
|
||||||
So if you have a diamond or circular relations types you need to perform the updates in a manual way.
|
So if you have a diamond or circular relations types you need to perform the updates in a manual way.
|
||||||
|
|
||||||
```python
|
|
||||||
# in example like this the second Street (coming from City) won't be save_related, so ZipCode won't be updated
|
|
||||||
Street -> District -> City -> Street -> ZipCode
|
|
||||||
```
|
|
||||||
|
|
||||||
[fields]: ../fields.md
|
[fields]: ../fields.md
|
||||||
[relations]: ../relations/index.md
|
[relations]: ../relations/index.md
|
||||||
[queries]: ../queries/index.md
|
[queries]: ../queries/index.md
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 0.10.2
|
# 0.10.2
|
||||||
|
|
||||||
## Features
|
## ✨ Features
|
||||||
|
|
||||||
* `Model.save_related(follow=False)` now accept also two additional arguments: `Model.save_related(follow=False, save_all=False, exclude=None)`.
|
* `Model.save_related(follow=False)` now accept also two additional arguments: `Model.save_related(follow=False, save_all=False, exclude=None)`.
|
||||||
* `save_all:bool` -> By default (so with `save_all=False`) `ormar` only upserts models that are not saved (so new or updated ones),
|
* `save_all:bool` -> By default (so with `save_all=False`) `ormar` only upserts models that are not saved (so new or updated ones),
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* Same thing applies to `QuerysetProxy.update(each=False, **kwargs)` which also previously required that you either pass a `filter` (by `**kwargs` or as a separate `filter()` call) or set `each=True` now also accepts
|
* Same thing applies to `QuerysetProxy.update(each=False, **kwargs)` which also previously required that you either pass a `filter` (by `**kwargs` or as a separate `filter()` call) or set `each=True` now also accepts
|
||||||
`exclude()` calls that generates NOT filter. So either `each=True` needs to be set to update whole table or at least one of `filter/exclude` clauses.
|
`exclude()` calls that generates NOT filter. So either `each=True` needs to be set to update whole table or at least one of `filter/exclude` clauses.
|
||||||
|
|
||||||
## Fixes
|
## 🐛 Fixes
|
||||||
|
|
||||||
* Fix improper relation field resolution in `QuerysetProxy` if fk column has different database alias.
|
* Fix improper relation field resolution in `QuerysetProxy` if fk column has different database alias.
|
||||||
* Fix hitting recursion error with very complicated models structure with loops when calling `dict()`.
|
* Fix hitting recursion error with very complicated models structure with loops when calling `dict()`.
|
||||||
@ -28,7 +28,7 @@
|
|||||||
* Fix bug when bulk_create would try to save also `property_field` decorated methods and `pydantic` fields
|
* Fix bug when bulk_create would try to save also `property_field` decorated methods and `pydantic` fields
|
||||||
* Fix wrong merging of deeply nested chain of reversed relations
|
* Fix wrong merging of deeply nested chain of reversed relations
|
||||||
|
|
||||||
## Other
|
## 💬 Other
|
||||||
|
|
||||||
* Performance optimizations
|
* Performance optimizations
|
||||||
* Split tests into packages based on tested area
|
* Split tests into packages based on tested area
|
||||||
|
|||||||
@ -20,36 +20,30 @@ class DataSource(ormar.Model):
|
|||||||
class Meta(BaseMeta):
|
class Meta(BaseMeta):
|
||||||
tablename = "datasources"
|
tablename = "datasources"
|
||||||
|
|
||||||
source_id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200, unique=True, index=True)
|
name: str = ormar.String(max_length=200, unique=True, index=True)
|
||||||
|
|
||||||
|
|
||||||
class DataSourceTable(ormar.Model):
|
class DataSourceTable(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
class Meta(BaseMeta):
|
||||||
tablename = "datasource_tables"
|
tablename = "tables"
|
||||||
|
|
||||||
datasource_table_id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200, index=True)
|
name: str = ormar.String(max_length=200, index=True)
|
||||||
data_source: Optional[DataSource] = ormar.ForeignKey(
|
source: Optional[DataSource] = ormar.ForeignKey(
|
||||||
DataSource,
|
DataSource, name="source_id", related_name="tables", ondelete="CASCADE",
|
||||||
name="data_source_id",
|
|
||||||
related_name="datasource_tables",
|
|
||||||
ondelete="CASCADE",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DataSourceTableColumn(ormar.Model):
|
class DataSourceTableColumn(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
class Meta(BaseMeta):
|
||||||
tablename = "datasource_table_columns"
|
tablename = "columns"
|
||||||
|
|
||||||
datasource_table_column_id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200, index=True)
|
name: str = ormar.String(max_length=200, index=True)
|
||||||
data_type: str = ormar.String(max_length=200)
|
data_type: str = ormar.String(max_length=200)
|
||||||
datasource_table: Optional[DataSourceTable] = ormar.ForeignKey(
|
table: Optional[DataSourceTable] = ormar.ForeignKey(
|
||||||
DataSourceTable,
|
DataSourceTable, name="table_id", related_name="columns", ondelete="CASCADE",
|
||||||
name="datasource_table_id",
|
|
||||||
related_name="datasource_table_columns",
|
|
||||||
ondelete="CASCADE",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +63,7 @@ async def test_double_nested_reverse_relation():
|
|||||||
test_tables = [
|
test_tables = [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
"datasource_table_columns": [
|
"columns": [
|
||||||
{"name": "col1", "data_type": "test"},
|
{"name": "col1", "data_type": "test"},
|
||||||
{"name": "col2", "data_type": "test2"},
|
{"name": "col2", "data_type": "test2"},
|
||||||
{"name": "col3", "data_type": "test3"},
|
{"name": "col3", "data_type": "test3"},
|
||||||
@ -77,14 +71,14 @@ async def test_double_nested_reverse_relation():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "test2",
|
"name": "test2",
|
||||||
"datasource_table_columns": [
|
"columns": [
|
||||||
{"name": "col4", "data_type": "test"},
|
{"name": "col4", "data_type": "test"},
|
||||||
{"name": "col5", "data_type": "test2"},
|
{"name": "col5", "data_type": "test2"},
|
||||||
{"name": "col6", "data_type": "test3"},
|
{"name": "col6", "data_type": "test3"},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
data_source.datasource_tables = test_tables
|
data_source.tables = test_tables
|
||||||
await data_source.save_related(save_all=True, follow=True)
|
await data_source.save_related(save_all=True, follow=True)
|
||||||
|
|
||||||
tables = await DataSourceTable.objects.all()
|
tables = await DataSourceTable.objects.all()
|
||||||
@ -94,24 +88,14 @@ async def test_double_nested_reverse_relation():
|
|||||||
assert len(columns) == 6
|
assert len(columns) == 6
|
||||||
|
|
||||||
data_source = (
|
data_source = (
|
||||||
await DataSource.objects.select_related(
|
await DataSource.objects.select_related("tables__columns")
|
||||||
"datasource_tables__datasource_table_columns"
|
.filter(tables__name__in=["test1", "test2"], name="local")
|
||||||
)
|
|
||||||
.filter(datasource_tables__name__in=["test1", "test2"], name="local")
|
|
||||||
.get()
|
.get()
|
||||||
)
|
)
|
||||||
assert len(data_source.datasource_tables) == 2
|
assert len(data_source.tables) == 2
|
||||||
assert len(data_source.datasource_tables[0].datasource_table_columns) == 3
|
assert len(data_source.tables[0].columns) == 3
|
||||||
assert (
|
assert data_source.tables[0].columns[0].name == "col1"
|
||||||
data_source.datasource_tables[0].datasource_table_columns[0].name == "col1"
|
assert data_source.tables[0].columns[2].name == "col3"
|
||||||
)
|
assert len(data_source.tables[1].columns) == 3
|
||||||
assert (
|
assert data_source.tables[1].columns[0].name == "col4"
|
||||||
data_source.datasource_tables[0].datasource_table_columns[2].name == "col3"
|
assert data_source.tables[1].columns[2].name == "col6"
|
||||||
)
|
|
||||||
assert len(data_source.datasource_tables[1].datasource_table_columns) == 3
|
|
||||||
assert (
|
|
||||||
data_source.datasource_tables[1].datasource_table_columns[0].name == "col4"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
data_source.datasource_tables[1].datasource_table_columns[2].name == "col6"
|
|
||||||
)
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class Album(ormar.Model):
|
|||||||
metadata = metadata
|
metadata = metadata
|
||||||
database = database
|
database = database
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True, name="album_id")
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class Writer(ormar.Model):
|
|||||||
metadata = metadata
|
metadata = metadata
|
||||||
database = database
|
database = database
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True, name="writer_id")
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
@ -40,11 +40,11 @@ class Track(ormar.Model):
|
|||||||
database = database
|
database = database
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album, name="album_id")
|
||||||
title: str = ormar.String(max_length=100)
|
title: str = ormar.String(max_length=100)
|
||||||
position: int = ormar.Integer()
|
position: int = ormar.Integer()
|
||||||
play_count: int = ormar.Integer(nullable=True)
|
play_count: int = ormar.Integer(nullable=True)
|
||||||
written_by: Optional[Writer] = ormar.ForeignKey(Writer)
|
written_by: Optional[Writer] = ormar.ForeignKey(Writer, name="writer_id")
|
||||||
|
|
||||||
|
|
||||||
async def get_sample_data():
|
async def get_sample_data():
|
||||||
|
|||||||
Reference in New Issue
Block a user