Skip to content

Releases: collerek/ormar

BETA - Support for pydantic v2

16 Mar 19:49
Compare
Choose a tag to compare
Pre-release

BETA

0.20.0

You can find updated documentation at: https://collerek.github.io/ormar/0.20.0b1/

✨ Breaking changes

  • ormar Model configuration

    Instead of defining a Meta class now each of the ormar models require an ormar_config parameter that is an instance of the OrmarConfig class.
    Note that the attribute must be named ormar_config and be an instance of the config class.

    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

    The ormar_config expose the same set of settings as Meta class used to provide.
    That means that you can use any of the following parameters initializing the config:

    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 equivalent - best practice

    Note that to reduce the duplication of code and ease of development it's still recommended to create a base config and provide each of the models with a copy.
    OrmarConfig provides a convenient copy method for that purpose.

    The copy method accepts the same parameters as OrmarConfig init, so you can overwrite if needed, but by default it will return already existing attributes, except for: tablename, order_by and constraints which by default are cleared.

    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)
  • choices Field parameter is no longer supported.

    Before version 0.20 you could provide choices parameter to any existing ormar Field to limit the accepted values.
    This functionality was dropped, and you should use ormar.Enum field that was designed for this purpose.
    If you want to keep the database field type (i.e. an Integer field) you can always write a custom validator.

    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 parameter is no longer supported

    pydantic_only fields were already deprecated and are removed in v 0.20. Ormar allows defining pydantic fields as in ordinary pydantic model.

    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
  • property_field decorator is no longer supported

    property_field decorator was used to provide a way to pass calculated fields that were included in dictionary/ serialized json representation of the model.
    Version 2.X of pydantic introduced such a possibility, so you should now switch to the one native to the pydantic.

    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 methods

    All methods listed below are deprecated and will be removed in version 0.30 of ormar.

    • dict() becomes the 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"
        )
    
        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 parameters remain the same i.e. include, exclude etc.

    • json() becomes the model_dump_json()

      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 parameters remain the same i.e. include, exclude etc.

    • construct() becomes the 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"
        )
    
        id: int = ormar.Integer(primary_key=True)
        name: str = ormar.String(max_length=100)
        favorite: bool = ormar.Boolean(default=False)
        
    params = {
        "n...
Read more

Bump fastapi and python

18 Jun 17:05
c793d66
Compare
Choose a tag to compare

0.12.2

✨ Features

  • Bump support for FastAPI up to the newest version (0.97.0) #1110
  • Add support and tests for Python 3.11 #1110

Performance improvements

29 Jan 12:21
fd77f02
Compare
Choose a tag to compare

0.12.1

✨ Features

  • Massive performance improvements in the area of loading the models due to recursive loads and caching of the models and related models. (by @erichaydel - thanks!) #853

💬 Internals

  • Benchmarks for comparing the performance effect of implemented changes in regard of trends (again, by @erichaydel - thanks!) #853

Force save in upset and bug fixes

21 Oct 17:55
dda5e24
Compare
Choose a tag to compare

0.12.0

✨ Breaking Changes

  • Queryset.bulk_create will now raise ModelListEmptyError on empty list of models (by @ponytailer - thanks!) #853

✨ Features

  • Model.upsert() now handles a flag __force_save__: bool that allow upserting the models regardless of the fact if they have primary key set or not.
    Note that setting this flag will cause two queries for each upserted model -> get to check if model exists and later update/insert accordingly. #889

🐛 Fixes

  • Fix for empty relations breaking construct method (by @Abdeldjalil-H - thanks!) #870
  • Fix save related not saving models with already set pks (including uuid) #885
  • Fix for wrong relations exclusions depending on the order of exclusions #779
  • Fix property_fields not being inherited properly #774

CheckColumn constraint, ReferentialAction and bug fixes

07 Sep 10:56
3cd33e6
Compare
Choose a tag to compare

0.11.3

✨ Features

  • Document onupdate and ondelete referential actions in ForeignKey and provide ReferentialAction enum to specify the behavior of the relationship (by @SepehrBazyar - thanks!) #724
  • Add CheckColumn to supported constraints in models Meta (by @SepehrBazyar - thanks!) #729

🐛 Fixes

  • Fix limiting query result to 0 should return empty list (by @SepehrBazyar - thanks!) #766

💬 Other

Bug fixes and packaging fix

26 Jun 17:37
6af92aa
Compare
Choose a tag to compare

0.11.2

🐛 Fixes

  • Fix database drivers being required, while they should be optional #713
  • Fix boolean field problem in limit queries in postgres without limit_raw_sql flag #704
  • Fix enum_class spilling to schema causing errors in OpenAPI #699

Fix deepcopy issues for pydantic >=1.9

08 Jun 17:20
4d76934
Compare
Choose a tag to compare

0.11.1

🐛 Fixes

  • Fix deepcopy issues introduced in pydantic 1.9 #685

Add python 3.10 support, drop python 3.6 and minor fixes/changes

28 Mar 16:49
90f78e2
Compare
Choose a tag to compare

0.11.0

✨ Breaking Changes

  • Dropped support for python 3.6
  • Queryset.get_or_create returns now a tuple with model and bool value indicating if the model was created (by @mojixcoder - thanks!) #554
  • Queryset.count() now counts the number of distinct parent model rows by default, counting all rows is possible by setting distinct=False (by @erichaydel - thanks) #588

✨ Features

  • Added support for python 3.10

🐛 Fixes

  • Fix inconsistent JSON fields behaviour in save and bulk_create #584
  • Fix maximum recursion error #580

Add plugable queryset_class, enhance IndexColumns and bug fixes

25 Feb 11:18
989e11e
Compare
Choose a tag to compare

0.10.25

✨ Features

  • Add queryset_class option to Model.Meta that allows you to easily swap QuerySet for your Model (by @ponytailer - thanks!) #538
  • Allow passing extra kwargs to IndexColumns that will be passed to sqlalchemy Index (by @zevisert - thanks) #575

🐛 Fixes

  • Fix nullable setting on JSON fields #529
  • Fix bytes/str mismatch in bulk operations when using orjson instead of json (by @ponytailer - thanks!) #538

Add support for pydantic 1.9, add post_bulk_update signal, and bug fixes

14 Jan 22:45
6ec4825
Compare
Choose a tag to compare

0.10.24

✨ Features

🐛 Fixes

💬 Other

  • Improve performance of bulk_create by bypassing databases execute_many suboptimal implementation. (by @Mng-dev-ai thanks!) #520
  • Bump min. required databases version for sqlalchemy 1.4 to >=5.4.