issuing authority note
0.20.1
Major changes
- Please note that this is the first non-beta version of ormar and supports Pydantic v2. Check the release notes for 0.20.0 and https://collerek.github.io/ormar/0.20.0b1/migration/
🐛Repair
- Fixed merge of same target models using select_lated and prefetch_lated #906
- Fixed save issue #812 related to pk-only models
- Fix adding the same relationship multiple times corrupts relationship cache #1335
Features
- Allow adding foreign key index #1276 via @cmflynn
💬Other
- @Chaoyingz repaired some documents, thank you!
0.20.0
Major changes
-
Omar Model Configuration
Each ormar model now requires an ormar_config parameter that is an instance of the OrmarConfig class, not a Meta class definition. Note that the attribute must be named ormar_config and an instance of the config class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
import databases import ormar import sqlalchemy database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData() # ormar < 0.20 class Album(ormar.Model): class Meta: database = database metadata = metadata tablename = "albums" id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) favorite: bool = ormar.Boolean(default=False) # ormar >= 0.20 class AlbumV20(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="albums_v20" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) favorite: bool = ormar.Boolean(default=False) -
OrmarConfig api/Parameters
ormar_config exposes the same set of settings as the Meta class provides. This means that you can initialize the configuration with any of the following parameters:
1 2 3 4 5 6 7 8 9 10
metadata: Optional[sqlalchemy.MetaData] database: Optional[databases.Database] engine: Optional[sqlalchemy.engine.Engine] tablename: Optional[str] order_by: Optional[List[str]] abstract: bool exclude_parent_fields: Optional[List[str]] queryset_class: Type[QuerySet] extra: Extra constraints: Optional[List[ColumnCollectionConstraint]] -
BaseMeta Equivalents-Best Practices
Note that to reduce code duplication and simplify development, it is still recommended to create a base configuration and provide a copy for each model. OrmarConfig provides a convenient way to copy for this.
The copy method accepts the same parameters as OrmarConfig init, so you can override them as needed, but by default it returns properties that already exist, except for tablename, order_by, and constraints, which are cleared by default.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import databases import ormar import sqlalchemy base_ormar_config = ormar.OrmarConfig( database=databases.Database("sqlite:///db.sqlite"), metadata=sqlalchemy.MetaData() ) class AlbumV20(ormar.Model): ormar_config = base_ormar_config.copy( tablename="albums_v20" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class TrackV20(ormar.Model): ormar_config = base_ormar_config.copy( tablename="tracks_v20" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) -
The options field parameter is no longer supported.
Prior to version 0.20, you could provide option parameters to any existing ormar field to limit the acceptable values. This feature has been removed and you should use the ormar.Enum field designed for this purpose. If you want to preserve database field types (i.e. integer fields), you can always write custom validators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import databases import ormar import sqlalchemy database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData() # ormar < 0.20 class Artist(ormar.Model): class Meta: database = database metadata = metadata id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) country: str = ormar.String(default=False, max_length=50, choices=["UK", "US", "Vietnam", "Colombia"]) # ormar >= 0.20 from enum import Enum class Country(str, Enum): UK = "UK" US = "US" VIETNAM = "Vietnam" COLOMBIA = "Colombia" class ArtistV20(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="artists_v20" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) country: Country = ormar.Enum(enum_class=Country) -
pydantic_only Field parameters are no longer supported
The pydantic_only field has been deprecated and removed in v 0.20. Ormar allows pydantic fields to be defined like normal pydantic models.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
import databases import ormar import sqlalchemy database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData() # ormar < 0.20 class Dish(ormar.Model): class Meta: database = database metadata = metadata tablename = "dishes" id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) cook: str = ormar.String(max_length=40, pydantic_only=True, default="sam") # ormar >= 0.20 class DishV20(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="dishes_v20" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) cook: str = "sam" # this is normal pydantic field -
The property_field decorator is no longer supported
The property_field decorator is used to provide a way to pass along the computed fields contained in the dictionary/serialized json representation of the model. Pydantic version 2.X introduces this possibility, so you should now switch to the native version of pydantic.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import databases import ormar import sqlalchemy import pydantic database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData() # ormar < 0.20 class Employee(ormar.Model): class Meta: database = database metadata = metadata id: int = ormar.Integer(primary_key=True) first_name: str = ormar.String(max_length=100) last_name: str = ormar.String(max_length=100) @ormar.property_field() def full_name(self) -> str: return f"{self.first_name} {self.last_name}" # ormar >= 0.20 class EmployeeV20(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, ) id: int = ormar.Integer(primary_key=True) first_name: str = ormar.String(max_length=100) last_name: str = ormar.String(max_length=100) @pydantic.computed_field() def full_name(self) -> str: return f"{self.first_name} {self.last_name}" -
deprecated method
All methods listed below are deprecated and will be removed in ormar version 0.30.
-
dict() becomes model_dump()
import databases import ormar import sqlalchemy
database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData()
class Album(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="albums" )
1 2 3
id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) favorite: bool = ormar.Boolean(default=False)album = Album(name="Dark Side of the Moon")
ormar < 0.20
album_dict = album.dict()
ormar >= 0.20
new_album_dict = album.model_dump()
Note that the parameters remain unchanged, i.e. include, exclude, etc.
-
json() becomes model_dump_json()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
import databases import ormar import sqlalchemy database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData() class Album(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="albums" ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) favorite: bool = ormar.Boolean(default=False) album = Album(name="Dark Side of the Moon") # ormar < 0.20 album_json= album.json() # ormar >= 0.20 new_album_dict = album.model_dump_json()Note that the parameters remain unchanged, i.e. include, exclude, etc.
-
construct() becomes model_construct()
import databases import ormar import sqlalchemy
database = databases.Database("sqlite:///db.sqlite") metadata = sqlalchemy.MetaData()
class Album(ormar.Model): ormar_config = ormar.OrmarConfig( database=database, metadata=metadata, tablename="albums" )
1 2 3
id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) favorite: bool = ormar.Boolean(default=False)params = { "name": "Dark Side of the Moon", "favorite": True, }
ormar < 0.20
album = Album.construct(**params)
ormar >= 0.20
album = Album.model_construct(**params)
To learn more about construction, see the pydantic documentation.
-
0.12.2
Features
- Upgraded FastAPI support to latest version (0.97.0) #1110
- Added support for Python 3.11 and test #1110
0.12.1
Features
- Performance in loading models is greatly improved due to recursive loading and caching of models and related models.(@erichaydel -thank you!)#853
Internal structure
- Benchmarks comparing the performance effects of implemented trend changes (again, by @erichaydel -thank you!)#853
0.12.0
Major changes
- Queryset.bulk_create will now raise ModelListEmptyError on empty model lists (by @ponytailer -thank you!)#853
Features
- Model.upsert() now handles a flag__force_save__: bool that allows updates to be inserted into models regardless of whether they have a primary key set. Note that setting this flag causes two queries to be made for each model that updates insert-> get to check whether the model exists and update/insert accordingly later. Chapter 889
Repair
- Fixed empty relations breaking construction methods (by @Abdeldjalil-H -Thanks!)#870
- Fix save related issue not saving pks set models (including uuid)#885
- Fixed incorrect relationship exclusion according to exclusion order #779
- Fix\property_fields not inherited correctly #774
0.11.3
Features
- logs onupdate and ondelete reference operations in ForeignKey and provides ReferentialAction enumeration to specify the behavior of the relationship (by @SepehrBazyar -thank you!)#724
- Add CheckColumn to constraints supported in model Meta (by @SepehrBazyar -thank you!)#729
Repair
- Fix limiting query results to 0 should return empty list (by @SepehrBazyar -thank you!)#766
Others
- Add Dark Mode to Document (by @SepehrBazyar -Thank you!)#717
- Update aiomysql dependency #778
0.11.2
Repair
- Fix database drivers are required, while they should be optional #713
- Fixed boolean field in limit queries without limit_raw_sql flag in postgres #704
- Fix enum_class overflow into schema causing bug #699 in OpenAPI
0.11.1
Repair
- Fixed deep copy issue introduced in pydantic 1.9 #685
0.11.0
Major changes
- Dropped support for Python 3.6
- Queryset.get_or_create now returns a tuple containing the model and a Boolean value indicating whether the model has been created (by @MojixCoder -thank you!)#554
- Queryset.count() now counts the number of rows of different parent models by default, you can count all rows by setting unique=False (by @erichaydel -thank you)#588
Features
- Added support for Python 3.10
Repair
- Fix inconsistent JSON field behavior in save and bulk_create #584
- Fixed Max Recursion Error #580
0.10.25
Features
- adds queryset_class option to Model.Meta, making it easy to replace QuerySet with your model (by @ponytailer -thank you!)#538
- allows extra kwargs to be passed to IndexColumns, which will be passed to sqlalchemy Index (by @zevisert -thank you)#575
Repair
- Fix nullable setting for JSON field #529
- fixes bytes/str mismatch in batch operations when using orjson instead of json (by @ponytailer -thank you!)#538
0.10.24
Features
- Add post_bulk_update signal (by @ ponytailer-thank you!)#524
Repair
- Fixed support for pydantic==1.9.0#502
- Fix Date Time Zone Issue #504
- Remove literal bindings from query generation to unlock postgres array #/tophat/ormar-postgres-extensions/9
- Fix batch update of JSON field #519
Others
- improves bulk_create performance by bypassing databaseexecute_many suboptimal implementations.(@Mng-dev-ai thanks!)#520
- Impact minutes. Database version required is>=5.4.
0.10.23
Features
- Added ability to pass comments to sqlalchemy when creating columns #485
Repair
- Fixed LargeBinary field #409 that could be empty
- Make ormar.Model available #413
- Make first() and get() take no arguments respecting the order of the master model set by the user, falling back to the primary key (asc and desc, respectively)#453
- Fix incorrect references to non-alias join clauses in postgress #455
0.10.22
Repair
- Hot fix where validator is not inherited when setting parent ormar model #365
0.10.21
Repair
- adds an ormar implementation of the constructor class method that allows model instances to be built without validating inputs to speed up the entire process (if your data has already been validated)#318
- fixes an issue with "inheriting" field validators from ormar models when generating newly created pydanic models using get_pydantic#365
0.10.20
Features
- adds an extra parameter to Model.Meta that accepts Extra.ignore and Extra. bid (default) and ignores extra fields passed to ormar model, or throws exception #358 on exception encountered
Repair
- None is not allowed if field is nullable and option is set #354
- Regardless of autoincrement and explicit nullability settings, always set Primary_key to non-null to avoid migration issue #348
0.10.19
Features
- Added support for multi-column non-unique IndexColumns in Meta.constraints#307
- adds sql_nullable field attribute to allow different nullable settings for pydantic model and underlying sql columns #308
Repair
- Enable Graph Cache for Performance #337
- Clarified and fixed doc #339 on nullable fields
Others
- Upgrade supported database versions to <=5.2.
0.10.18
Repair
- Fixed field order #328 in pydantic model
- Fix Database 0.5.0 Support #142
0.10.17
Features
- Default pydantic type allowed to override model fields #312
- Add support for sqlalchemy >=1.4 (requires database>= 0.5.0)#142
0.10.16
Features
- allows you to pass your own pydantic configuration to ormar.Model, which will be merged with default configuration by@naturalethic#285
- Add SmallInteger field type via@ProgrammerPlus1998 (thanks!)#297
Repair
- Fix generate openapi pattern by removing obsolete pydantic field parameters exposed directly in pattern #291
- Fix unnecessary warnings automatically generated by models #295
0.10.15
Repair
- Fixed issue generating pydantic model trees using nested models (by @pawamoy -thank you!)#278
- Fix missing f string #274 in warning about missing primary key field
- Fix passing foreign key values as relationships (extra protection, fixed in latest version)#270
0.10.14
Features
- allows the timezone:bool = False parameter to be passed to the DateTime and Time fields #264 of a time-zone aware database column
- allows filtering by passing datetime, date, and time on DateTime, Time, and Date fields to allow filtering by datetime instead of converting values to string #79
Repair
- Fix dependency from psycopg2 to psycopg2-binary #255
0.10.13
Features
-
allows field accessors to be passed in select_lated and prefetch_lated. python style select_related#225.
-
Previous:
await Post.objects.select_related(["author", "categories"]).get() await Author.objects.prefetch_related("posts__categories").get()
-
Now also:
await Post.objects.select_related([Post.author, Post.categories]).get() await Author.objects.prefetch_related(Author.posts.categories).get()
-
Repair
- Fix overwriting inherited primary key defaults #253
0.10.12
Repair
- Fix QuerySet.create method does not use init (if customization is provided)#245
- Fix pydantic type #250 with ForwardRefManyToMany relationship set incorrectly
0.10.11
Features
- adds values and values_list to QuerySet and QuerysetProxy, allowing raw data to be returned from query #223.
- Allow tuple lists or dictionary lists to be returned from queries
- Skipping parsing data to ormar model, therefore skipping validation as well
- Allows models between relationship chains to be excluded, so you can extract only the columns you want
- If you extract only one column, values_list allows you to flatten the results.
Repair
- Fixed issue with ForwardRef #226 creating autos via m2m relational model
0.10.10
Features
- adds the get_pydantic flag, allowing you to automatically generate an equivalent pydantic model tree from ormar.Model. This newly generated model tree can be used in requests and responses to exclude fields that you do not want to include in your data.
- adds the except_parent_fields parameter to the model Meta, allowing you to exclude fields from the parent model during inheritance. Note that best practice is to combine models and mixins, but if you have many similar models and only one different model, it can be a useful tool for achieving this goal.
Repair
- Fixed empty filters with pagination and relationships (provided by @erichaydel)#214
- Fixed child objects not saved on the opposite side of relationship if not saved before #216
Others
- Expand the fastapi section of the document to show examples of using ormar in fastapi requests and responses.
- Improved documentation on defaults, ForeignKey.add, etc.
0.10.9
Important security fixes
- Update pydantic pin to fix security vulnerability CVE-2021-29510
It is recommended that you update to the patched pydantic version. In 0.10.9, ormar excluded versions with vulnerabilities in fixed dependencies.
Repair
- Fix OpenAPI Architecture for LargeBinary #204
0.10.8
Repair
- Fix fill default #202 in pk_only submodel
- Use base64 str to represent mypy #199 for fixing LargeBinary fields
- Fixed OpenAPI schema format #199 for LargeBinary fields with Base64 str representation
- Fixed OpenAPI selection encoding for LargeBinary fields with Base64 str representation
0.10.7
Features
- Add
exclude_primary_keys: bool = Falsemarked as dict() method, allowing exclusion of all primary key columns in the results dictionary. Chapter 164 - adds
exclude_through_models: bool = Falsedict() flag to allow exclusion of all models #164 from the ManyToMany relationship - adds an
represent_as_base64_str: bool = Falseparameter that allows converting byte LargeBinary fields to base64 encoded strings. Strings are returned in dict() when accessing properties, and strings are converted to bytes when set. Data in the database is stored in bytes. Chapter 187 - Add pk alias to allow Model.pk to access fields in filters and order by clauses (python style)
Repair
- Remove the default None option for max_length in the LargeBinary field #186
- Remove default None option for string field max_length
Others
- Guidelines and examples for providing dict() parameters in documentation
- Refactor the main get/set attributes in magic methods to descriptors-> significant performance improvements
0.10.6
Features
-
Add LargeBinary(max_length) field type #166
-
adds support for plain pydantic fields (including models) instead of the pydantic_only attribute, which is now deprecated #160. Pydantic fields should be declared as normal as in the pydantic model next to the ormar field, noting that (obviously) ormar does not save and load the value of this field in the database, which means that one of the following must be true:
- Pydantic fields declared on ormar models must be optional (default is None)
- Pydantic field must be set to default
- pydantic field has default_factory function set
- ormar.Model must override__init__() and provide values there
If none of the above ormar (or rather pydantic) fails during the loading of data from the database, and the required value for the declared pydantic field is missing.
-
Ormar now provides meaningful examples in openapi patterns, including nested models. Use the same algorithms as dict() and select/load_all to iterate related models without looking. Examples also appear in fastapi. Chapter 157
Repair
- By default pydantic does not validate fields during allocation, which is not ideal for ORMs, now all ormar.Models have validation turned on during allocation (e.g. model.column ='value ')
Others
- Add connect to database in QuickStart for readme #180 The* OpenAPI architecture no longer contains the ormar.Model docstring as a description, and if you don't provide your own docstring, just the model name.
- Some performance improvements.
0.10.5
Repair
- Fix bug #73 in fastapi-pagination
- removes unnecessary Optional #174 from List[Optional[T]] in QuerySet.all() and Querysetproxy.all() return values
- Run test coverage is published only on internal PRs, not in github operations.
0.10.4
Features
- Add Python Style
filtersumorder_byUse field access instead of dunder delimited strings. Chapter 51- Access through attributes (dot symbol chain) Access fields can be used to construct FilterGroups (ormar.and_and ormar.or_)
The* field access overloaded a set of Python operators and provided a set of functions to allow the same functionality as the dunder separated parameter names in**kwargs, which means you now have more options for querying from the sample model Track associated with the model Album:
- Exact-exactly matches the value, sql column =
- Old: album__name__exact='Malibu'
- New: can also be written as Track.album.name == 'Malibu
- iexact -exact match sql column =
(case insensitive) - Old: album__name__iexact='malibu'
- New: can also be written
Track.album.name.iexact('malibu')
- contains- sql column LIKE '%
%' - old: album__name__contains='Mal'
- New: can also be written as www.example.com %'Mal ')
- New: can also be written
Track.album.name.contains('Mal')
- icontains-sql column LIKE ' %
%'(case insensitive) - old: album__name__icontains ='mal '
- New: can also be written
Track.album.name.icontains('mal')
- In-sql
column IN (<VALUE1>, <VALUE2>, ...)- Old:
album__name__in=['Malibu', 'Barclay'] - New: can also be written
Track.album.name << ['Malibu', 'Barclay'] - New: can also be written
Track.album.name.in_(['Malibu', 'Barclay'])
- Old:
- isnull-sql column IS NULL (and sql column IS NOT NULL)
- old: album__name__isnull = True (isnotnullalbum__name__isnull = False)
- New: can also be written as www.example.com>> None
- New: can also be written as Track. album. name. isnull (True)
- New: not null can also be written
Track.album.name.isnull(False) - New: not null can also be written~(Track.album.name>> None)
- New: not null can also be written
~(Track.album.name.isnull(True))
- gt-sql column>
(greater than) - old: position__gt = 3
- New: can also be written as www.example.com> 3
- gte-sql column>=
(greater than or equal to) - old: position__gte = 3
- New: can also be written as www.example.com>= 3
- lt-sql column <
(below) - old: position__lt = 3
- New: can also be written as www.example.com <3
- lte - sql column <=
(less than or equal to) - old: position__lte=3
- New: can also be written as Track.album.name <= 3
- startswith - sql column LIKE '
%' - old: album__name__startswith='Mal'
- New: can also be written
Track.album.name.startswith('Mal')
- istartswith - sql COLUMN LIKE '
%'(case insensitive) - Old:
album__name__istartswith='mal' - New: can also be written
Track.album.name.istartswith('mal')
- Old:
- endswith - sql COLUMN LIKE '%
'(Complete End Match) - Old: album__name__endswith='ibu'
- New: can also be written
Track.album.name.endswith('ibu')
- iendswith - sql COLUMN LIKE '%
'(case-insensitive) - old: album__name__iendswith='IBU'
- New: can also be written
Track.album.name.iendswith('IBU')
- Exact-exactly matches the value, sql column =
- Access through attributes (dot symbol chain) Access fields can be used to construct FilterGroups (ormar.and_and ormar.or_)
The* field access overloaded a set of Python operators and provided a set of functions to allow the same functionality as the dunder separated parameter names in**kwargs, which means you now have more options for querying from the sample model Track associated with the model Album:
- You can provide FilterGroups not only in filter() and except(), but also in:
- Get ()
- Get or None ()
- Get or Create ()
- The first ()
- All ()
- Delete ()
- With FilterGroups (ormar.and_and ormar.or_), you can now use:
- & -as an and_instead of the next level of nesting
- | - as "or_" rather than the next level of nesting
- ~ -Negation as a filter group
-
To combine multiple sets of filters into one set of conditions, use & (sql AND) and| (sql or)
1 2 3 4 5 6 7 8 9 10 11
# Following queries are equivalent: # sql: ( product.name = 'Test' AND product.rating >= 3.0 ) # ormar OPTION 1 - OLD one Product.objects.filter(name='Test', rating__gte=3.0).get() # ormar OPTION 2 - OLD one Product.objects.filter(ormar.and_(name='Test', rating__gte=3.0)).get() # ormar OPTION 3 - NEW one (field access) Product.objects.filter((Product.name == 'Test') & (Product.rating >=3.0)).get() -
The same applies to nested complex filters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# Following queries are equivalent: # sql: ( product.name = 'Test' AND product.rating >= 3.0 ) # OR (categories.name IN ('Toys', 'Books')) # ormar OPTION 1 - OLD one Product.objects.filter(ormar.or_( ormar.and_(name='Test', rating__gte=3.0), categories__name__in=['Toys', 'Books']) ).get() # ormar OPTION 2 - NEW one (instead of nested or use `|`) Product.objects.filter( ormar.and_(name='Test', rating__gte=3.0) | ormar.and_(categories__name__in=['Toys', 'Books']) ).get() # ormar OPTION 3 - NEW one (field access) Product.objects.filter( ((Product.name='Test') & (Product.rating >= 3.0)) | (Product.categories.name << ['Toys', 'Books']) ).get() -
You can now also use field access to provide OrderActions to order_by()
- In ascending order:
- Old:
Product.objects.order_by("name").all() - New:
Product.objects.order_by(Product.name.asc()).all()
- Old:
- Descending order:
- Old:
Product.objects.order_by("-name").all() - New:
Product.objects.order_by(Product.name.desc()).all()
- Old:
- Of course, you can also combine different models and many orders_by:
Product.objects.order_by([Product.category.name.asc(), Product.name.desc()]).all()
- In ascending order:
🐛Repair
- is not really an error, but rather an inconsistency. Provide filters with nested models, i.e. album__category__name ='AA 'Checking if album and category models are included in select_lated', if not, they are automatically added there. The same functionality does not apply to FilterGroups (and_and_), it works now (also applies to python style filters that return FilterGroups).
0.10.3
Features
-
foreignKey and ManyToMany now support skip_reverse: bool = False flag #118. If you set the skip_reverse flag internally, the field is still registered at the other end of the relationship, so you can:
- Filter by relevant model fields in reverse model
- order_by Related model fields in reverse model
But you cannot:
- Use related_name to access related fields from the reverse model
- Even if you select correlation from the opposite side of the model, the returned model does not populate the reverse instance (does not block connections, so you can still filter and order_by)
- This relationship is not populated in dict() and json()
- cannot pass nested related objects when populated from dict() or json()(also via fastapi). Depending on the extra settings in pydantic configuration, it will be ignored or raise an error.
-
Model.save_lated() can now save the entire data tree #148 at once.
-
It knows whether the master model or the dependent model should be saved first to preserve relationships
-
It saves the master model if
- It's not preserved,
- There is no PK value.
- or save_all=True flag is set
In these cases, you don't have to split the save into two calls (save() and save_lated())
-
It also supports many to many relationships
-
It also supports optional Through model values for m2m relationships
-
Add the possibility of customization by model relationship field name.
-
By default, the pass-through model relationship name defaults to the lower-case correlation model name. So in such an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
... ## course declaration omitted class Student(ormar.Model): class Meta: database = database metadata = metadata id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) courses = ormar.ManyToMany(Course) ## will produce default Through model like follows (example simplified) class StudentCourse(ormar.Model): class Meta: database = database metadata = metadata tablename = "students_courses" id: int = ormar.Integer(primary_key=True) student = ormar.ForeignKey(Student) ## default name course = ormar.ForeignKey(Course) # default name -
To customize the field/relationship names in the Through model, you can now use the new parameters of ManyToMany:
- through_relation_name -The field name leading to the model declaring ManyToMany
- through_reverse_relation_name -Name of the field leading to the model to which ManyToMany leads
Examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
... # course declaration omitted class Student(ormar.Model): class Meta: database = database metadata = metadata id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) courses = ormar.ManyToMany(Course, through_relation_name="student_id", through_reverse_relation_name="course_id") # will produce default Through model like follows (example simplified) class StudentCourse(ormar.Model): class Meta: database = database metadata = metadata tablename = "students_courses" id: int = ormar.Integer(primary_key=True) student_id = ormar.ForeignKey(Student) # set by through_relation_name course_id = ormar.ForeignKey(Course) # set by through_reverse_relation_name
🐛Repair
- Fix weakref ReferenceError #118
- Fix error caused by Through fields when setting pydantic Config.extra="forbid"
- fix pydantic.PrivateAttr not initialized at__init__error #149
- fixes pydantic-type exclusion error in dict() and__all__key does not work
💬Other
- Link to sqlalgchemy-to-ormar automatic translator for model introduction
- Provides links to fastapi ecosystem libraries that support ormar
- Add transactions to documents (database support)
0.10.2
Features
Model.save_related(follow=False)Two additional parameters are now accepted:Model.save_related(follow=False, save_all=False, exclude=None)。- save_all:bool -> By default (so save_all=False) ormar updates only inserted unsaved models (so new or updated models), use save_all=True All relevant models are saved regardless of save status, which may be useful in the following cases Updated models come from api calls and therefore do not change on the backend.
exclude: Union[Set, Dict, None]-> sets/dictionaries relationships to exclude from saving, even if you use follow=True and save_all=True. To exclude nested relationships, pass a nested dictionary, for example:exclude={"child":{"sub_child": {"exclude_sub_child_realtion"}}}. The allowed values follow the fields/exclude_fields (from QuerySet) method schema, so when in doubt, you can refer to the documentation in the query-> Select a subset of fields-> fields. The* Model.update() method now accepts the_columns: List[str] = None parameter, which accepts a list of column names to update. If passed, only those columns will be updated in the database. Note that update() does not refresh instances of the model, so if you change more columns than are passed in the_columns list, your model instance will have different values than the database!
- The Model.model_dump() method previously contained only direct correlation models or nested models (if they were non-nullable and not virtual), now all correlation models that were not previously accessed without loops are contained in dict(). This should not break because more data will be dumped into the dictionary, but it should not be lost.
QuerySet.delete(each=False, **kwargs)previously required you to pass a filter (via**kwargs or as a separate filter() call) or set each=True now also accepts exclusive() calls that generate NOT filters. Therefore, you need to set each=True to delete the entire table or at least one filter/exclusion clause.- The same thing applies to
QuerySet.update(each=False, **kwargs)before also requiring you to pass a filter (via**kwargs or as a separate filter() call) or setting each=True now also accepts except() calls that generate NOT filters. Therefore, you need to set each=True to update the entire table or at least one filter/exclusion clause. - The same thing applies to
QuerysetProxy.update(each=False, **kwargs)You were also asked to pass a filter (via**kwargs or called as a separate filter()) or set each=True Now also accepts except() calls that generate NOT filters. Therefore, each=True needs to be set to update the entire table or at least one filter/exclusion clause.
🐛Repair
- Fix incorrect relational field resolution in QuerysetProxy if fk column has different database aliases.
- Fixed recursion bug with loops when invoking dict() for very complex model structures.
- Fixed error when merging (appending) in query results when two non-relational fields are not relational fields (i.e. JSON)
- Fixed bug using the same relationship name in chain but resulting in different models during translation from list to dictionary
- Fix bulk_create error when trying to save property_field modifier methods and pydantic fields
- Fixed incorrect merging of deeply nested chains of inverse relationships
💬Other
- performance optimization
- Split tests into packages based on test zones
0.10.1
characteristic
- get_or_none(**kwargs) method is added to QuerySet and QuerysetProxy. It is compatible with get(**kwargs), but if there are no database records matching the condition, get_or_none does not throw an ormar.NoMatch exception, but simply returns None.
repair
- Fixed dialect-related column names and table name references in the order_by clause not working properly in postgres.
0.10.0
break
- Removed support for the long-deprecated field definition notation, where you use the ormar field as a type hint, namely
test_field: ormar.Integger() = None - Improved type hint-> mypy correctly resolves the relevant model fields (ForeignKey and ManyToMany) and the return type of the QuerySet method. Those mentioned now return the correct model (i.e. Book) instead of ormar.Model type. There are still problems with the opposite of the relationship and QuerysetProxy methods, to alleviate the type hint that now returns Any. Partial fix #112.
characteristic
- adds
select_all(follow: bool = False)QuerySet and QuerysetProxy methods. It is similar to the load_all() method for models, but can be used directly in queries. By default select_all() adds only directly related models, and if follow=True also adds related models for related models without loops in relationships. Note that it's not a and-end asynchronous model, so you still have to issue get(), all(), etc., because select_all() returns QuerySets (or proxies) like fields() or order_by().
interior structure
- The ormar field is no longer stored as a class in the Meta.model_fields dictionary, but as an instance.
0.9.9
characteristic
- Add the possibility to change the default order of relationships and models.
- To change the model ordering, pass orders_by = [columns] where columns: List[str] to the model metaclass
- To change the relationship order_by, pass order_by = [columns] where columns: List[str]
- wants to change the reverse relationship order_by passing related_orders_by = [columns] where columns: List[str] The* parameter can be a column name or-{col_name}sorted in descending order
- In relationships, you can sort only by directly related model columns or, for many-to-many columns, by model columns
"{through_field_name}__{column_name}" - The order in which the order_by clause is applied is as follows:
- Explicitly passing the order_by() call in the query
- If present, the relationship passes through orders_by
- model metaclass orders_by
- Model primary key column asc (backup, used if none of the above is provided)
- Add four new aggregate functions-> min, max, sum, and avg are their sql equivalents.
- You can pass one or more column names, including related columns.
- So far, each column passed is aggregated separately (so sum(col1+col2) is impossible, you can use sum(col1, col2) and add the sum of the 2 returns in python)
- You cannot sum and average non-numeric columns
- If a column is aggregated, a single value is returned directly as the result
- If you aggregate multiple columns, a dictionary with column: result pairs is returned
- Add 4 new signals-> pre_relation_add, post_relation_add, pre_relation_remove and post_relation_remove
The newly added signal of* is emitted for both the ManyToMany relationship (both sides) and the opposite of the foreignkey relationship (same as the public QuerysetProxy).
- signal receives the following parameters: sender: Type[Model] -sender class, instance: Model -add instance of related model, child: Model -model being added, relation_name: str -name of relation to which child is added, e.g. add signal also passed_kwargs: dictionary-kwargs dictionary passed to add()
change
- Now, by instantiating a model of many-to-many relationships at creation, deletion, and update time, you can provide not only auto-increment int as a primary key, but any column type with default functionality.
- Since the Through model is now instantiated, you can also subscribe to Save/Update/Delete signals before/after the Through model
- The pre_update signal receiver now also gets the Passed_args parameter, which is a dictionary (if any) of values passed to the update function (otherwise an empty dictionary).
repair
- The pre_update signal is now sent before extracting values so that you can modify the passed instance in-place and the modified field values will be reflected in the database
- bulk_update UUID primary key column types can now also be used correctly
0.9.8
characteristic
- Add the possibility of encrypting selected fields in the database
- At a minimum you need to provide encrypt_secret and encrypt_backend
- encrypt_backend can be one of the ormar.EncryptBackends enumeration (NONE, FERNET, HASH, CUSTOM)-default: NONE
- When choosing a custom backend, you need to provide a backend class of subclass ormar.fields.EncryptBackend
- You cannot encrypt Primary_key columns and Close series (FK and M2M).
- Two backends are provided: HASH and FERNET
- HASH is a one-way hash (such as a password) that is never decrypted when retrieved
- FERNET is a bidirectional encryption/decryption backend
- Note that in the FERNET backend, you completely lose the possibility of filtering because part of the encrypted value is the timestamp.
- Note that in the HASH backend you can filter by full values, but filters like contains will not work because encrypted values are compared.
- Note that adding encrypt_backend changes the database column type to TEXT, which needs to be reflected in db through migration or manual changes
repair
- (advanced/internal) restores the functionality of custom sqlalchemy types (via the types.TypeDecorator subclass) that stopped working, so process_result_value is never called
0.9.7
characteristic
-
Add the isnull operator to filter and exclude methods.
1 2
album__name__isnull=True #(sql: album.name is null) album__name__isnull=False #(sql: album.name is not null)) -
adds ormar.or_and ormar.and_functions that can be used in combination Complex queries with nested conditions. Example queries:
1 2 3 4 5 6 7 8 9 10
books = ( await Book.objects.select_related("author") .filter( ormar.and_( ormar.or_(year__gt=1960, year__lt=1940), author__name="J.R.R. Tolkien", ) ) .all() )Check for updated documents in queries-> Filter and sort-> Complex filters
else
- Setting defaults on ForeignKey or ManyToMany throws a ModelDefinition exception because it is (and was) unsupported
0.9.6
Important
-
is now optional with the many-to-many relational model. This is not a major change, because if you provide it, everything will work as before. So if you don't want or need to "pass" any other fields on the model, you can skip it. Note that it will be created automatically for you and must still be included in the example of alembic migration. If you want to delete an existing database, check the default naming convention to adjust the existing database structure.
Note that if you want to customize the Through model name or database table name, you still need to provide it.
characteristic
- adds update methods to QuerysetProxy, so it is now possible to update dependent models directly from parent models in the Many To Many relationship as well as from reverse foreign key relationships. Note that updates like QuerySetupdate return the number of updated models and do not update dependent models on the parent model. To get refreshed data on the parent model, you need to refresh the dependent model (i.e.
await model_instance.related.all()) - adds the
load_all(follow=False, exclude=None)model method, which allows loading the current instance of a model and all related models in a single call. By default, it loads only directly related models, but setting follow=True causes the tree to be traversed (avoiding loops). You can also pass the same exclusion parameters as the QuerySet.exclude_fields() method. - Added the possibility of adding more fields to the Through model for many-to-many relationships:
- The name of the through model field is the lowercase name of the Through class
- You can pass other fields (on QuerysetProxy) when calling add(child, **kwargs) on a relationship
- When one of the keyword arguments should be a model name with a dictionary of values, you can pass other fields when calling create(**kwargs) on a relationship (on QuerysetProxy)
- You can order through the Model field
- You can filter by model field
- You can include and exclude fields from your model
- through model attaches only to related models (i.e., if you query from A to B -> only on B)
- Note that the pass-through model is explicitly loaded without relationships-> relationships are populated in the ManyToMany field.
- Note that just like before you couldn't declare relational fields through the model, they will be populated for you by ormar, but now if you try to do so, ModelDefinitionError will be thrown.
- Check the updated ManyToMany relationship documentation for more information
else
- Updated documentation and API documentation
- Refactoring and optimization mainly involve filters, exclusions and sorting criteria
0.9.5
repair
- Fix pydantic FieldInfo created after pydantic update to version>=1.8
- Fix required dependency versions to avoid such situations in the future
0.9.4
repair
- Fixed automatic fastapi OpenAPI schema generation of documents when multiple models refer to the same related model
0.9.3
repair
- Fixed JSON field being double escaped when setting value after initialization
- Fixed JSON fields not complying with settable for null fields due to pydantic internal structure
- Fix selection validation for JSON fields
- Fixed unverified options when setting properties after initialization
- Fix unvalidated option during QuerySet update call
0.9.2
else
- Updated Quick Start in Documentation/Readme
- Updated documentation to include links to query subpages
- Badges with code climate and downloads added
0.9.1
characteristic
- Add option values to the OpenAPI specification to make them look like native Enum fields in the resulting schema.
repair
- Use fastapi to use fix selection behavior when special fields are not initialized but passed as strings, etc.
0.9.0
important
- brake fix: Version 0.8.0 introduced a bug that prevented foreign_keys constraints from being generated in the database, either in alembic or during creation via sqlalchemy.engine, which has now been fixed.
- Therefore, if you use version>=0.8.0, it is strongly recommended that you update, because although most ormar functions are running, databases created using ormar (or ormar + alembic) have no relationships and suffer from integrity from a performance and data point of view.
- If you use ormar to connect to an existing database, your performance and integrity should be good, but you should update to reflect all future schema updates in the model.
break
- Breakthrough: All foreign keys and unique constraints now have a name, so alembic can recognize them in the database without relying on the database
- corruption: During model construction, ModelDefinitionError will now be raised instead of generic AttributeError if the metaclass of the model does not contain metadata or database.
- destruction: The encoding/database used to run the query has no join pool for sqlite backend, which means that every query runs with new connections, and enforced foreign key constraints cannot be enabled because these constraints are turned off on every connection by default. This has changed in ormar since>=0.9.0, and by default every sqlite3 query has "PRAGMAforeign_keys=1; Run so that each sqlite3 connection now enforces foreign key constraints by default, including cascading.
else
- Update API documentation.
- Add tests that create fk in database and cascade in database
0.8.1
characteristic
- describes the handling of ForwardRef in relationships. You can now create self-referencing models-foreign keys and many-to-many relationships. ForwardRef can be used with to and throughModel.
-
Introducing the possibility of performing two identical relational joins in one query to handle complex relationships, such as:
1 2 3 4 5 6 7
B = X = Y // A \ C = X = Y <= before you could link from X to Y only once in one query unless two different relation were used (two relation fields with different names) -
Introduction of pagination methods that allow limit/offset by page and page_size. Available for QuerySet and QuerysetProxy.
else
- Refactoring and performance optimization in queries and joins.
- Python 3.9 was added to the test and pypi settings.
- Update API documentation and documentation->, i.e. split of query documentation.
0.8.0
break
- Breaking:remove() The parent of the child end of a reverse foreign key relationship now needs to pass a relationship name because the same model can be registered multiple times, and ormar needs to know which relationship of the parent you want to remove the child from.
- Break: By default, limits and offsets are applied using select_lated only to the primary table before the join-> meaning that it is not the total number of rows that is limited, but only the number of primary models (the first in the query, used to construct it). You can still use the limit_raw_sql=True flag on the limit or offset (or both) to limit all rows in the database response
- destruction: issuing first() now gets the first row sorted by primary key asc (hence the first row inserted (may be different for non-numeric primary keys-i.e. alphabetical order of strings))
- destruction: issuing get() without any filters now gets the first row sorted by primary key desc (and therefore should be the last inserted row (may differ for non-numeric primary keys-i.e. alphabetical order of strings))
- Destruction (internal): sqlalchemy columns that remain in Meta.columns are no longer bound to tables, so you can't get columns directly from there
characteristic
- Introduction of inheritance. Two types of inheritance are currently possible:
- Mixins -Do not subclass ormar.Model, just define fields that are used later on different models (e.g. created_date and updated_date on each submodel), only the actual model creates tables, but add those fields in mixins
- concrete table inheritance-means that the parent is marked as abstract=True in the Meta class, and each child has its own table, which contains columns from the parent and its own child columns, somewhat similar to Mixins, but the parent is also an (abstract) model
- To learn more, see the documentation for the Model-> Inheritance section.
- QuerySet first() can be used with prefetch_lated
repair
- Fixed minor bug in order_by of main model order by
- Fix prefetch_query for multiple related_names of the same model.
- fixes an issue where using the same related_name on different models causes the same related models to overwrite each other, now raises ModelDefinitionError and you need to change the name.
- Fixed an issue where order_by overrides condition when multiple joins are applied to the same table.
document
- Splitting and cleaning in documents:
- Divide the model into smaller parts
- Divide the relationship section into subsections
- Divide the field section into smaller sections
- Add model inheritance
- Add API (beta) documentation
0.7.5
- Fixed incorrect relationship column names in Many_to_many relationship joins (fix #73)
0.7.4
- Multiple relationships to the same related model/table are allowed.
- Fixed the wrong set of relationships used in Many_to_many relational connections (fix #73)
- Fixed incorrect relationship population for m2m relationships when fk relationships also exist for the same model.
- If multiple relationships exist for the same table on a model, add a check to see if the user provides related_name.
- More eager to clean up dead weak proxy models.
0.7.3
- Fixed issue with getting correlation model using UUDI pk setting, UUDI pk is original string (fix #71)
0.7.2
- fix using pk only to overwrite related models
Model.update() with fields passed as parameters(fix #70)
0.7.1
- Fixed pk overwriting related models only in Model.save()(fix #68)
0.7.0
- corruption: QuerySetbulk_update method now raises ModelPersistenceError instead of QueryDefinitionError for passed unsaved models
- Breakthrough: Model initialization with unknown field names now raises ModelError instead of KeyError
- adds signals with predefined list signals and decorators: post_delete, post_save, post_update, pre_delete, pre_save, pre_update
- Add py.typed and modify setup.py to support mypy
- performance optimization
- update document
0.6.2
- performance optimization
- Fixed bug requiring pydantic_only field
- adds the property_field decorator to register the function as an attribute that will be included in Model.model_dump() and fastapi responses
- update document
0.6.1
- Explicitly set None to exclude nullable fields to avoid pydantic defaults (fix #60).
0.6.0
- Breaking: Calling instance.load () when deleting instance rows from the database now raises NoMatch instead of ValueError
- Destruction: Calls to add and delete on the ReverseForeignKey relationship will now update the database settings/delete submodels in the fk column
- Breakthrough: ReverseForeignKey relationships now expose QuerySetProxy API like ManyToMany relationships
- Destruction: Querying related models from ManyToMany clears the list of related models loaded on the parent model:
- Example: post.categories. first () sets post.categories to a list of 1 related models-> first () returned models
- Example 2: If a post has 4 categories, len(post.categories) == 4 calls
post.categories.limit(2).all()-> Now only 2 child processes will be loadedassert len(post.categories) == 2
- adds get_or_create, update_or_create, fields, exclude_fields, exclusion, prefetch_lated, and order_by to QuerySetProxy, so you can now use these methods directly from relationships
- update document
0.5.5
- fixes alembic autogeneration problem for migrating UUID columns. It should only generate sqlalchemy CHAR (32) or CHAR (36)
- In order for it to work, you must set user_module_prefix ='sa '.(Must equal sqlalchemy_module_prefix option (default is' sa'.))
0.5.4
-
allows you to pass uuid_format (allowing "hex"(default) or "string ") to a UUID field to change its save format. By default, fields are saved in hex format (pruned to 32 characters without dashes), but you can pass format ='string 'to use 36 with dashes to adjust existing databases or other libraries.
Sample:
- Hex value = c616 ab 438 cce 49 dbbf 4380 d109251 dce
- String value = c616 ab 43 - 8 cce-49 db-bf 43 - 80 d109251 dce
0.5.3
- Fixed Model.model_dump () method ignoring exclusion parameters and not including dictionary parameters.
0.5.2
- Added prefetch_lated method to load subsequent models in a separate query.
- update document
0.5.1
- Switch to github actions instead of travis
- Update badges in documents
0.5.0
- Added save state-> You can check if the model has been saved using the ModelInstance. saved property
- Save model after saving/updating/loading/updating insert method on model
- After saving the model
create/get/first/all/get_or_create/update_or_createmethod - The model is saved when passed to bulk_update and bulk_create
- Save the model after adding/deleting ManyToMany related objects (automatically save/delete through model instances)
- The model is not saved after changing any of its own fields (including pk as an alias for Model.pk)
- Model not saved after adding/deleting ForeignKey related objects (fk column not saved)
- does not save the model after instantiation with__init__(without QuerySet.create or before calling save)
- adds Model.upsert(**kwargs), if pk is not set, save(), otherwise update(**kwargs)
- extra
Model.save_related(follow=False)iterates over all related objects in all relationships and checks if they are saved. If not, it calls upsert() on each object. - Destruction: Add throws exception if add/delete unsaved (pk is None) model to many-to-many relationship
- Allow dictionaries and collections to be passed to fields and except_fields
- Dictionary that automatically translates str and lists into fields and except_fields
- Breaking: Nested models are passed to fields and excre_fields now via the associated foreign key name instead of the target model name
- Performance optimization-in modelproxy, newbasemodel-query reduction>, some attributes cached on model
- Clean up unused relationship codes
- Add optional performance dependency orjson (highly recommended)
- update document
0.4.4
- Add the except_fields() method to exclude fields from sql
- Refactor Column Name Settings (Alias)
- Fix how columns with aliases are sorted
- Additional tests for fields and except_fields
- update document
0.4.3
- Contains attributes from models.model_dump() and model.model_dump_json()
0.4.2
- Modify the creation of pydantic models to allow return of correlation models populated with pk only
0.4.1
- Add the order_by method to the query set to allow sorting
- update document
0.4.0
- changed the symbol in the model definition-> now uses name = ormar.Field() instead of name: ormar.Field()
- Note that the old notation is still supported, but deprecated, and does not work well with static checkers such as mypy and pydantic pycharm plugins
- Enter prompt documentation and test
- You can't use mypy for testing, you can only use ormar packages
- Fix scale and precision conversion using max_digits and decimal_places pydantic Decimal fields
- Update documentation-Best practices for adding dependencies
- Refactor metaclasses and model_fields to make better use of type hinting
- Add mypy and pydantic plugins to documents
- Expand documentation on many-to-many relationships
0.3.11
- Fix setting server_default to default field value in python
0.3.10
- Fix postgresql check to avoid exception with no driver installed when using different backends
0.3.9
- Fix json pattern generation from #19
- Fixed uninitialized ManyToMany relationships in fastapi copy of ormar.Models
- Update documentation on fastapi usage
- Add tests to verify fastapi/docs are generated correctly
0.3.8
- Added the possibility of providing alternate database column names with name parameters for all fields.
- Fixed an error using fields() to select related ManyToMany fields if they were empty.
- update document
0.3.7
- Publish documentation and update readme files
0.3.6
- Add fields() method to restrict selected columns in the database-exclude only nullable columns.
- UniqueColumns and constraint lists were added to model elements to build unique constraints on column lists.
- Added UUID field type based on Char(32) column type.
0.3.5
- Added bulk_create and bulk_update for operations on multiple objects.
0.3.4
Add Query Set Level Method
- delete
- update
- acquiring or creating
- update or create
0.3.3
- Add extra filters-beginning and ending
0.3.2
- Add option arguments to all fields-Limits the accepted values to those provided
0.3.1
- Added exclusions to filter where not conditions.
- Added tests for mysql and postgres, and fixed postgres.
- Rafactors and cleanup.
0.3.0
- Added the ManyToMany field and supports many-to-many relationships