SQLAlchemy-serializer


NameSQLAlchemy-serializer JSON
Version 1.4.12 PyPI version JSON
download
home_pagehttps://github.com/n0nSmoker/SQLAlchemy-serializer
SummaryMixin for SQLAlchemy-models serialization without pain
upload_time2024-04-01 14:51:51
maintainerNone
docs_urlNone
authorY-Bro
requires_pythonNone
licenseNone
keywords sqlalchemy serialize to_dict json
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # SQLAlchemy-serializer
Mixin for SQLAlchemy models serialization without pain.

If you want to serialize SQLAlchemy model instances with only one line of code,
and tools like `marshmallow` seems to be redundant and too complex for such a simple task,
this mixin definitely suits you.

**Contents**
- [Installation](#Installation)
- [Usage](#Usage)
- [Advanced usage](#Advanced-usage)
- [Custom formats](#Custom-formats)
- [Custom types](#Custom-types)
- [Timezones](#Timezones)
- [Troubleshooting](#Troubleshooting)
- [Tests](#Tests)

## Installation

```bash
pip install SQLAlchemy-serializer
```

## Usage

If you want SQLAlchemy model to become serializable,
add **SerializerMixin** in class definition:
```python
from sqlalchemy_serializer import SerializerMixin


class SomeModel(db.Model, SerializerMixin):
    ...
```

This mixin adds **.to_dict()** method to model instances.
So now you can do something like this:
```python
item = SomeModel.query.filter(...).one()
result = item.to_dict()
```
You get values of all SQLAlchemy fields in the `result` var, even nested relationships
In order to change the default output you shuld pass tuple of fieldnames as an argument

- If you want to exclude or add some extra fields (not from database) 
  You should pass `rules` argument
- If you want to define the only fields to be presented in serializer's output
  use `only` argument

If you want to exclude a few fields for this exact item:
```python
result = item.to_dict(rules=('-somefield', '-some_relation.nested_one.another_nested_one'))
```

If you want to add a field which is not defined as an SQLAlchemy field:
```python
class SomeModel(db.Model, SerializerMixin):
    non_sql_field = 123

    def method(self):
        return anything

result = item.to_dict(rules=('non_sql_field', 'method'))
```
**Note** that method or a function should have no arguments except ***self***,
in order to let serializer call it without hesitations.

If you want to get exact fields:
```python

result = item.to_dict(only=('non_sql_field', 'method', 'somefield'))
```
**Note** that if ***somefield*** is an SQLAlchemy instance, you get all it's
serializable fields. So if you want to get only some of them, you should define it like below:
```python

result = item.to_dict(only=('non_sql_field', 'method', 'somefield.id', 'somefield.etc'))
```
You can use negative rules in `only` param too.
So `item.to_dict(only=('somefield', -'somefield.id'))`
will return `somefiled` without `id`. See [Negative rules in ONLY section](#Negative-rules-in-ONLY-section)

If you want to define schema for all instances of particular SQLAlchemy model,
add serialize properties to model definition:
```python
class SomeModel(db.Model, SerializerMixin):
    serialize_only = ('somefield.id',)
    serialize_rules = ()
    ...
    somefield = db.relationship('AnotherModel')

result = item.to_dict()
```
So the `result` in this case will be `{'somefield': [{'id': some_id}]}`
***serialize_only*** and  ***serialize_rules*** work the same way as ***to_dict's*** arguments


# Advanced usage 
For more examples see [tests](https://github.com/n0nSmoker/SQLAlchemy-serializer/tree/master/tests)

```python
class FlatModel(db.Model, SerializerMixin):
    """
    to_dict() of all instances of this model now returns only following two fields
    """
    serialize_only = ('non_sqlalchemy_field', 'id')
    serialize_rules = ()

    id = db.Column(db.Integer, primary_key=True)
    string = db.Column(db.String(256), default='Some string!')
    time = db.Column(db.DateTime, default=datetime.utcnow())
    date = db.Column(db.Date, default=datetime.utcnow())
    boolean = db.Column(db.Boolean, default=True)
    boolean2 = db.Column(db.Boolean, default=False)
    null = db.Column(db.String)
    non_sqlalchemy_dict = dict(qwerty=123)


class ComplexModel(db.Model, SerializerMixin):
   """
   Schema is not defined so
   we will get all SQLAlchemy attributes of the instance by default
   without `non_sqlalchemy_list`
   """

    id = db.Column(db.Integer, primary_key=True)
    string = db.Column(db.String(256), default='Some string!')
    boolean = db.Column(db.Boolean, default=True)
    null = db.Column(db.String)
    flat_id = db.Column(db.ForeignKey('test_flat_model.id'))
    rel = db.relationship('FlatModel')
    non_sqlalchemy_list = [dict(a=12, b=10), dict(a=123, b=12)]

item = ComplexModel.query.first()


# Now by default the result looks like this:
item.to_dict()

dict(
    id=1,
    string='Some string!',
    boolean=True,
    null=None,
    flat_id=1,
    rel=[dict(
        id=1,
        non_sqlalchemy_dict=dict(qwerty=123)
    )]


# Extend schema
item.to_dict(rules=('-id', '-rel.id', 'rel.string', 'non_sqlalchemy_list'))

dict(
    string='Some string!',
    boolean=True,
    null=None,
    flat_id=1,
    non_sqlalchemy_list=[dict(a=12, b=10), dict(a=123, b=12)],
    rel=dict(
        string='Some string!',
        non_sqlalchemy_dict=dict(qwerty=123)
    )
)


# Exclusive schema
item.to_dict(only=('id', 'flat_id', 'rel.id', 'non_sqlalchemy_list.a'))

dict(
    id=1,
    flat_id=1,
    non_sqlalchemy_list=[dict(a=12), dict(a=123)],
    rel=dict(
        id=1
    )
)
```
# Recursive models and trees
If your models have references to each other or you work with large trees
you need to specify where the serialization should stop.
```python
item.to_dict('-children.children')
```
In this case only the first level of `children` will be included
See [Max recursion](#Max-recursion)

# Custom formats
If you want to change datetime/date/time/decimal format in one model you can specify it like below:
```python
from sqlalchemy_serializer import SerializerMixin

class SomeModel(db.Model, SerializerMixin):
    __tablename__ = 'custom_table_name'
    
    date_format = '%s'  # Unixtimestamp (seconds)
    datetime_format = '%Y %b %d %H:%M:%S.%f'
    time_format = '%H:%M.%f'
    decimal_format = '{:0>10.3}'

    id = sa.Column(sa.Integer, primary_key=True)
    date = sa.Column(sa.Date)
    datetime = sa.Column(sa.DateTime)
    time = sa.Column(sa.Time)
    money = Decimal('12.123')  # same result with sa.Float(asdecimal=True, ...)
```

If you want to change format in every model, you should write
your own mixin class inherited from `SerializerMixin`:
```python
from sqlalchemy_serializer import SerializerMixin

class CustomSerializerMixin(SerializerMixin):
    date_format = '%s'  # Unixtimestamp (seconds)
    datetime_format = '%Y %b %d %H:%M:%S.%f'
    time_format = '%H:%M.%f'
    decimal_format = '{:0>10.3}'
```
And later use it as usual:
```python
from decimal import Decimal
import sqlalchemy as sa
from some.lib.package import CustomSerializerMixin


class CustomSerializerModel(db.Model, CustomSerializerMixin):
    __tablename__ = 'custom_table_name'

    id = sa.Column(sa.Integer, primary_key=True)
    date = sa.Column(sa.Date)
    datetime = sa.Column(sa.DateTime)
    time = sa.Column(sa.Time)
    money = Decimal('12.123')  # same result with sa.Float(asdecimal=True, ...)

```
All `date/time/datetime/decimal` fields will be serialized using your custom formats.

- Decimal uses python `format` syntax
- To get **unixtimestamp** use `%s`, 
- Other `datetime` formats you can find [in docs](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior)


# Custom types
By default the library can serialize the following types:
 ```
 - int 
 - str 
 - float 
 - bytes 
 - bool 
 - type(None)
 - uuid.UUID
 - time
 - datetime
 - date
 - Decimal
 - Enum
 - dict (if values and keys are one of types mentioned above, or inherit one of them)
 - any Iterable (if types of values are mentioned above, or inherit one of them)
 ```
 If you want to add serialization of any other type or redefine the default behaviour. 
 You should add something like this:

```python

serialize_types = (
    (SomeType, lambda x: some_expression),
    (AnyOtherType, some_function)
)
```
To your own mixin class inherited from `SerializerMixin`:

```python
from sqlalchemy_serializer import SerializerMixin
from geoalchemy2.elements import WKBElement
from geoalchemy2.shape import to_shape

def serialize_int(value):
    return value + 100

class CustomSerializerMixin(SerializerMixin):
    serialize_types = (
        (WKBElement, lambda x: to_shape(x).to_wkt()),
        (int, serialize_int)
    )
```
... or directly to the model:
```python
from geoalchemy2 import Geometry
from sqlalchemy_serializer import SerializerMixin

class Point(Base, SerializerMixin):
    serialize_types = (
        (WKBElement, lambda x: to_shape(x).to_wkt()),
        (AnyOtherType, serialize_smth)
    )
    __tablename__ = 'point'
    id = Column(Integer, primary_key=True)
    position = Column(Geometry('POINT'))
```

Unfortunately you can not access formats or tzinfo in that functions.
I'll implement this logic later if any of users needs it.


# Timezones
To keep `datetimes` consistent its better to store it in the database normalized to **UTC**.
But when you return response, sometimes (mostly in web, mobile applications can do it themselves)
you need to convert all `datetimes` to user's timezone.
So you need to tell serializer what timezone to use.
There are two ways to do it:
-  The simplest one is to pass timezone directly as an argument for `to_dict` function
```python
import pytz

item.to_dict(timezone=pytz.timezone('Europe/Moscow'))
```
- But if you do not want to write this code in every function, you should define
  timezone logic in your custom mixin (how to use customized mixin see [Castomization](#Castomization))
 ```python
import pytz
from sqlalchemy_serializer import SerializerMixin
from some.package import get_current_user

class CustomSerializerMixin(SerializerMixin):
    def get_tzinfo(self):
        # you can write your own logic here, 
        # the example below will work if you store timezone
        # in user's profile
        return pytz.timezone(get_current_user()['timezone'])
```

# Troubleshooting

## Max recursion
If you've faced with **maximum recursion depth exceeded** exception,
most likely the serializer have found instance of the same class somewhere among model's relationships.
Especially if you use backrefs. In this case you need to tell it where to stop like below:
```python
class User(Base, SerializerMixin):
    __tablename__ = 'users'
    
    # Exclude nested model of the same class to avoid max recursion error
    serialize_rules = ('-related_models.user',)
    ...
    related_models = relationship("RelatedModel", backref='user')
    
    
class RelatedModel(Base, SerializerMixin):
    __tablename__ = 'some_table'

    ...
    user_id = Column(Integer, ForeignKey('users.id'))
    ...
```
If for some reason you need the field `user` to be presented in `related_models` field.
You can change `serialize_rules` to `('-related_models.user.related_models',)`
To break the chain of serialisation a bit further.
[Recursive models and trees](#Recursive-models-and-trees)

## Controversial rules
If you add controversial rules like `serialize_rules = ('-prop', 'prop.id')`
The serializer will include `prop` in spite of `-prop` rule.

## Negative rules in ONLY section
If you pass rules in `serialize_only` the serializer becomes **NOT** greedy and returns **ONLY** fields listed there.
So `serialize_only = ('-model.id',)` will return nothing
But `serialize_only = ('model', '-model.id',)` will return `model` field without `id`

## One element tuples
Do not forget to add **comma** at the end of one element tuples, it is trivial, 
but a lot of developers forget about it:
```python
serialize_only = ('some_field',)  # <--- Thats right!
serialize_only = ('some_field')  # <--- WRONG it is actually not a tuple

```

# Tests
To run tests and see tests coverage report just type the following command:(doker and doker-compose should be installed on you local machine)
```bash
make test
```
To run a particular test use
```bash
make test file=tests/some_file.py
make test file=tests/some_file.py::test_func
```

I will appreciate any help in improving this library, so feel free to submit issues or pull requests.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/n0nSmoker/SQLAlchemy-serializer",
    "name": "SQLAlchemy-serializer",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "sqlalchemy, serialize, to_dict, JSON",
    "author": "Y-Bro",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/86/c9/8fb4469d12306a4bd0ab787fd62dc265adb35a068f5f8f9c5c9f69a9a930/SQLAlchemy-serializer-1.4.12.tar.gz",
    "platform": null,
    "description": "# SQLAlchemy-serializer\nMixin for SQLAlchemy models serialization without pain.\n\nIf you want to serialize SQLAlchemy model instances with only one line of code,\nand tools like `marshmallow` seems to be redundant and too complex for such a simple task,\nthis mixin definitely suits you.\n\n**Contents**\n- [Installation](#Installation)\n- [Usage](#Usage)\n- [Advanced usage](#Advanced-usage)\n- [Custom formats](#Custom-formats)\n- [Custom types](#Custom-types)\n- [Timezones](#Timezones)\n- [Troubleshooting](#Troubleshooting)\n- [Tests](#Tests)\n\n## Installation\n\n```bash\npip install SQLAlchemy-serializer\n```\n\n## Usage\n\nIf you want SQLAlchemy model to become serializable,\nadd **SerializerMixin** in class definition:\n```python\nfrom sqlalchemy_serializer import SerializerMixin\n\n\nclass SomeModel(db.Model, SerializerMixin):\n    ...\n```\n\nThis mixin adds **.to_dict()** method to model instances.\nSo now you can do something like this:\n```python\nitem = SomeModel.query.filter(...).one()\nresult = item.to_dict()\n```\nYou get values of all SQLAlchemy fields in the `result` var, even nested relationships\nIn order to change the default output you shuld pass tuple of fieldnames as an argument\n\n- If you want to exclude or add some extra fields (not from database) \n  You should pass `rules` argument\n- If you want to define the only fields to be presented in serializer's output\n  use `only` argument\n\nIf you want to exclude a few fields for this exact item:\n```python\nresult = item.to_dict(rules=('-somefield', '-some_relation.nested_one.another_nested_one'))\n```\n\nIf you want to add a field which is not defined as an SQLAlchemy field:\n```python\nclass SomeModel(db.Model, SerializerMixin):\n    non_sql_field = 123\n\n    def method(self):\n        return anything\n\nresult = item.to_dict(rules=('non_sql_field', 'method'))\n```\n**Note** that method or a function should have no arguments except ***self***,\nin order to let serializer call it without hesitations.\n\nIf you want to get exact fields:\n```python\n\nresult = item.to_dict(only=('non_sql_field', 'method', 'somefield'))\n```\n**Note** that if ***somefield*** is an SQLAlchemy instance, you get all it's\nserializable fields. So if you want to get only some of them, you should define it like below:\n```python\n\nresult = item.to_dict(only=('non_sql_field', 'method', 'somefield.id', 'somefield.etc'))\n```\nYou can use negative rules in `only` param too.\nSo `item.to_dict(only=('somefield', -'somefield.id'))`\nwill return `somefiled` without `id`. See [Negative rules in ONLY section](#Negative-rules-in-ONLY-section)\n\nIf you want to define schema for all instances of particular SQLAlchemy model,\nadd serialize properties to model definition:\n```python\nclass SomeModel(db.Model, SerializerMixin):\n    serialize_only = ('somefield.id',)\n    serialize_rules = ()\n    ...\n    somefield = db.relationship('AnotherModel')\n\nresult = item.to_dict()\n```\nSo the `result` in this case will be `{'somefield': [{'id': some_id}]}`\n***serialize_only*** and  ***serialize_rules*** work the same way as ***to_dict's*** arguments\n\n\n# Advanced usage \nFor more examples see [tests](https://github.com/n0nSmoker/SQLAlchemy-serializer/tree/master/tests)\n\n```python\nclass FlatModel(db.Model, SerializerMixin):\n    \"\"\"\n    to_dict() of all instances of this model now returns only following two fields\n    \"\"\"\n    serialize_only = ('non_sqlalchemy_field', 'id')\n    serialize_rules = ()\n\n    id = db.Column(db.Integer, primary_key=True)\n    string = db.Column(db.String(256), default='Some string!')\n    time = db.Column(db.DateTime, default=datetime.utcnow())\n    date = db.Column(db.Date, default=datetime.utcnow())\n    boolean = db.Column(db.Boolean, default=True)\n    boolean2 = db.Column(db.Boolean, default=False)\n    null = db.Column(db.String)\n    non_sqlalchemy_dict = dict(qwerty=123)\n\n\nclass ComplexModel(db.Model, SerializerMixin):\n   \"\"\"\n   Schema is not defined so\n   we will get all SQLAlchemy attributes of the instance by default\n   without `non_sqlalchemy_list`\n   \"\"\"\n\n    id = db.Column(db.Integer, primary_key=True)\n    string = db.Column(db.String(256), default='Some string!')\n    boolean = db.Column(db.Boolean, default=True)\n    null = db.Column(db.String)\n    flat_id = db.Column(db.ForeignKey('test_flat_model.id'))\n    rel = db.relationship('FlatModel')\n    non_sqlalchemy_list = [dict(a=12, b=10), dict(a=123, b=12)]\n\nitem = ComplexModel.query.first()\n\n\n# Now by default the result looks like this:\nitem.to_dict()\n\ndict(\n    id=1,\n    string='Some string!',\n    boolean=True,\n    null=None,\n    flat_id=1,\n    rel=[dict(\n        id=1,\n        non_sqlalchemy_dict=dict(qwerty=123)\n    )]\n\n\n# Extend schema\nitem.to_dict(rules=('-id', '-rel.id', 'rel.string', 'non_sqlalchemy_list'))\n\ndict(\n    string='Some string!',\n    boolean=True,\n    null=None,\n    flat_id=1,\n    non_sqlalchemy_list=[dict(a=12, b=10), dict(a=123, b=12)],\n    rel=dict(\n        string='Some string!',\n        non_sqlalchemy_dict=dict(qwerty=123)\n    )\n)\n\n\n# Exclusive schema\nitem.to_dict(only=('id', 'flat_id', 'rel.id', 'non_sqlalchemy_list.a'))\n\ndict(\n    id=1,\n    flat_id=1,\n    non_sqlalchemy_list=[dict(a=12), dict(a=123)],\n    rel=dict(\n        id=1\n    )\n)\n```\n# Recursive models and trees\nIf your models have references to each other or you work with large trees\nyou need to specify where the serialization should stop.\n```python\nitem.to_dict('-children.children')\n```\nIn this case only the first level of `children` will be included\nSee [Max recursion](#Max-recursion)\n\n# Custom formats\nIf you want to change datetime/date/time/decimal format in one model you can specify it like below:\n```python\nfrom sqlalchemy_serializer import SerializerMixin\n\nclass SomeModel(db.Model, SerializerMixin):\n    __tablename__ = 'custom_table_name'\n    \n    date_format = '%s'  # Unixtimestamp (seconds)\n    datetime_format = '%Y %b %d %H:%M:%S.%f'\n    time_format = '%H:%M.%f'\n    decimal_format = '{:0>10.3}'\n\n    id = sa.Column(sa.Integer, primary_key=True)\n    date = sa.Column(sa.Date)\n    datetime = sa.Column(sa.DateTime)\n    time = sa.Column(sa.Time)\n    money = Decimal('12.123')  # same result with sa.Float(asdecimal=True, ...)\n```\n\nIf you want to change format in every model, you should write\nyour own mixin class inherited from `SerializerMixin`:\n```python\nfrom sqlalchemy_serializer import SerializerMixin\n\nclass CustomSerializerMixin(SerializerMixin):\n    date_format = '%s'  # Unixtimestamp (seconds)\n    datetime_format = '%Y %b %d %H:%M:%S.%f'\n    time_format = '%H:%M.%f'\n    decimal_format = '{:0>10.3}'\n```\nAnd later use it as usual:\n```python\nfrom decimal import Decimal\nimport sqlalchemy as sa\nfrom some.lib.package import CustomSerializerMixin\n\n\nclass CustomSerializerModel(db.Model, CustomSerializerMixin):\n    __tablename__ = 'custom_table_name'\n\n    id = sa.Column(sa.Integer, primary_key=True)\n    date = sa.Column(sa.Date)\n    datetime = sa.Column(sa.DateTime)\n    time = sa.Column(sa.Time)\n    money = Decimal('12.123')  # same result with sa.Float(asdecimal=True, ...)\n\n```\nAll `date/time/datetime/decimal` fields will be serialized using your custom formats.\n\n- Decimal uses python `format` syntax\n- To get **unixtimestamp** use `%s`, \n- Other `datetime` formats you can find [in docs](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior)\n\n\n# Custom types\nBy default the library can serialize the following types:\n ```\n - int \n - str \n - float \n - bytes \n - bool \n - type(None)\n - uuid.UUID\n - time\n - datetime\n - date\n - Decimal\n - Enum\n - dict (if values and keys are one of types mentioned above, or inherit one of them)\n - any Iterable (if types of values are mentioned above, or inherit one of them)\n ```\n If you want to add serialization of any other type or redefine the default behaviour. \n You should add something like this:\n\n```python\n\nserialize_types = (\n    (SomeType, lambda x: some_expression),\n    (AnyOtherType, some_function)\n)\n```\nTo your own mixin class inherited from `SerializerMixin`:\n\n```python\nfrom sqlalchemy_serializer import SerializerMixin\nfrom geoalchemy2.elements import WKBElement\nfrom geoalchemy2.shape import to_shape\n\ndef serialize_int(value):\n    return value + 100\n\nclass CustomSerializerMixin(SerializerMixin):\n    serialize_types = (\n        (WKBElement, lambda x: to_shape(x).to_wkt()),\n        (int, serialize_int)\n    )\n```\n... or directly to the model:\n```python\nfrom geoalchemy2 import Geometry\nfrom sqlalchemy_serializer import SerializerMixin\n\nclass Point(Base, SerializerMixin):\n    serialize_types = (\n        (WKBElement, lambda x: to_shape(x).to_wkt()),\n        (AnyOtherType, serialize_smth)\n    )\n    __tablename__ = 'point'\n    id = Column(Integer, primary_key=True)\n    position = Column(Geometry('POINT'))\n```\n\nUnfortunately you can not access formats or tzinfo in that functions.\nI'll implement this logic later if any of users needs it.\n\n\n# Timezones\nTo keep `datetimes` consistent its better to store it in the database normalized to **UTC**.\nBut when you return response, sometimes (mostly in web, mobile applications can do it themselves)\nyou need to convert all `datetimes` to user's timezone.\nSo you need to tell serializer what timezone to use.\nThere are two ways to do it:\n-  The simplest one is to pass timezone directly as an argument for `to_dict` function\n```python\nimport pytz\n\nitem.to_dict(timezone=pytz.timezone('Europe/Moscow'))\n```\n- But if you do not want to write this code in every function, you should define\n  timezone logic in your custom mixin (how to use customized mixin see [Castomization](#Castomization))\n ```python\nimport pytz\nfrom sqlalchemy_serializer import SerializerMixin\nfrom some.package import get_current_user\n\nclass CustomSerializerMixin(SerializerMixin):\n    def get_tzinfo(self):\n        # you can write your own logic here, \n        # the example below will work if you store timezone\n        # in user's profile\n        return pytz.timezone(get_current_user()['timezone'])\n```\n\n# Troubleshooting\n\n## Max recursion\nIf you've faced with **maximum recursion depth exceeded** exception,\nmost likely the serializer have found instance of the same class somewhere among model's relationships.\nEspecially if you use backrefs. In this case you need to tell it where to stop like below:\n```python\nclass User(Base, SerializerMixin):\n    __tablename__ = 'users'\n    \n    # Exclude nested model of the same class to avoid max recursion error\n    serialize_rules = ('-related_models.user',)\n    ...\n    related_models = relationship(\"RelatedModel\", backref='user')\n    \n    \nclass RelatedModel(Base, SerializerMixin):\n    __tablename__ = 'some_table'\n\n    ...\n    user_id = Column(Integer, ForeignKey('users.id'))\n    ...\n```\nIf for some reason you need the field `user` to be presented in `related_models` field.\nYou can change `serialize_rules` to `('-related_models.user.related_models',)`\nTo break the chain of serialisation a bit further.\n[Recursive models and trees](#Recursive-models-and-trees)\n\n## Controversial rules\nIf you add controversial rules like `serialize_rules = ('-prop', 'prop.id')`\nThe serializer will include `prop` in spite of `-prop` rule.\n\n## Negative rules in ONLY section\nIf you pass rules in `serialize_only` the serializer becomes **NOT** greedy and returns **ONLY** fields listed there.\nSo `serialize_only = ('-model.id',)` will return nothing\nBut `serialize_only = ('model', '-model.id',)` will return `model` field without `id`\n\n## One element tuples\nDo not forget to add **comma** at the end of one element tuples, it is trivial, \nbut a lot of developers forget about it:\n```python\nserialize_only = ('some_field',)  # <--- Thats right!\nserialize_only = ('some_field')  # <--- WRONG it is actually not a tuple\n\n```\n\n# Tests\nTo run tests and see tests coverage report just type the following command:(doker and doker-compose should be installed on you local machine)\n```bash\nmake test\n```\nTo run a particular test use\n```bash\nmake test file=tests/some_file.py\nmake test file=tests/some_file.py::test_func\n```\n\nI will appreciate any help in improving this library, so feel free to submit issues or pull requests.\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Mixin for SQLAlchemy-models serialization without pain",
    "version": "1.4.12",
    "project_urls": {
        "Homepage": "https://github.com/n0nSmoker/SQLAlchemy-serializer"
    },
    "split_keywords": [
        "sqlalchemy",
        " serialize",
        " to_dict",
        " json"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "86c98fb4469d12306a4bd0ab787fd62dc265adb35a068f5f8f9c5c9f69a9a930",
                "md5": "e4fcc048c6df9b97f68234157b1c79f7",
                "sha256": "5e1f83fc6d8a4f7618100c1b9a6af949498210756b974527ec3c8c1ec7e1300f"
            },
            "downloads": -1,
            "filename": "SQLAlchemy-serializer-1.4.12.tar.gz",
            "has_sig": false,
            "md5_digest": "e4fcc048c6df9b97f68234157b1c79f7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 16739,
            "upload_time": "2024-04-01T14:51:51",
            "upload_time_iso_8601": "2024-04-01T14:51:51.261882Z",
            "url": "https://files.pythonhosted.org/packages/86/c9/8fb4469d12306a4bd0ab787fd62dc265adb35a068f5f8f9c5c9f69a9a930/SQLAlchemy-serializer-1.4.12.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-01 14:51:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "n0nSmoker",
    "github_project": "SQLAlchemy-serializer",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "sqlalchemy-serializer"
}
        
Elapsed time: 0.32603s