Skip to content

Commit

Permalink
Merge pull request #42 from Martlark/2.0.3
Browse files Browse the repository at this point in the history
2.0.3
  • Loading branch information
Martlark authored Jan 30, 2022
2 parents ca3cee9 + d7222f5 commit b5a2103
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,7 @@ Version 2.0.1 changes most of the properties, hooks and methods to use a more no
Release Notes
-------------

* 2.0.3 - Allow more use of model column variables instead of "quoted" field names. Fix missing import for FlaskSerialize.
* 2.0.2 - Fix table formatting.
* 2.0.1 - Try to get properties and methods to use more appropriate names.
* 1.5.2 - Test with flask 2.0. Add __fs_after_commit__ method to allow post create/update actions. Improve documentation.
Expand Down
83 changes: 47 additions & 36 deletions flask_serialize/flask_serialize.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime, time
from typing import Type
from typing import Type, List

from flask import request, jsonify, abort, current_app, Response
from permissive_dict import PermissiveDict
Expand Down Expand Up @@ -53,9 +53,9 @@ class FlaskSerializeMixin:
# previous values of an instance before update attempted
__fs_previous_field_value__ = {}
# current version
__fs_version__ = "2.0.2"
__fs_version__ = "2.0.3"

def __fs_before_update__(self, data_dict:dict) -> dict:
def __fs_before_update__(self, data_dict: dict) -> dict:
"""
hook to call before any__fs_update_from_dict so that you may alter update values__
before the item is written in preparation for update to db
Expand All @@ -65,15 +65,15 @@ def __fs_before_update__(self, data_dict:dict) -> dict:
"""
return data_dict

def __fs_after_commit__(self, create:bool=False):
def __fs_after_commit__(self, create: bool = False):
"""
hook to call after any put/post commit so that you may do something
self will be the new / updated committed item
:param create: True when item was just created.
"""

def __fs_to_date_short__(self, d:datetime)->str:
def __fs_to_date_short__(self, d: datetime) -> str:
"""
convert the given date field to a short date / time without fractional seconds
Expand All @@ -83,7 +83,7 @@ def __fs_to_date_short__(self, d:datetime)->str:
return str(d).split(".")[0]

@classmethod
def fs_query_by_access(cls, user=None, **kwargs)->list:
def fs_query_by_access(cls, user=None, **kwargs) -> list:
"""
filter a query result to that optionally owned by the given user and
__fs_can_access__()
Expand Down Expand Up @@ -143,7 +143,7 @@ def fs_json_get(cls, item_id):
if not item or not item.__fs_can_access__():
return jsonify({})
return item.fs_as_json

@classmethod
def fs_json_list(cls, query_result, prop_filters=None):
"""
Expand Down Expand Up @@ -175,12 +175,17 @@ def fs_json_list(cls, query_result, prop_filters=None):

# ascending
if cls.__fs_order_by_field__:
items = sorted(items, key=lambda i: i[cls.__fs_order_by_field__])
items = sorted(
items,
key=lambda i: i[cls._fs_get_field_name(cls.__fs_order_by_field__)],
)

# descending
elif cls.__fs_order_by_field_desc__:
items = sorted(
items, key=lambda i: i[cls.__fs_order_by_field_desc__], reverse=True
items,
key=lambda i: i[cls._fs_get_field_name(cls.__fs_order_by_field_desc__)],
reverse=True,
)

return jsonify(items)
Expand Down Expand Up @@ -302,7 +307,7 @@ def __fs_sqlite_date_converter(value):
except:
return datetime.strptime(value, "%Y-%m-%d")

def __fs_get_props(self):
def _fs_get_props(self):
"""
get the properties for this table to be used for introspection
Expand All @@ -325,10 +330,9 @@ def __fs_get_props(self):
}

# SQL columns
props.__exclude_fields = [
"fs_as_dict",
"fs_as_json",
] + self.__fs_exclude_serialize_fields__
props.__exclude_fields = ["fs_as_dict", "fs_as_json",] + [
self._fs_get_field_name(f) for f in self.__fs_exclude_serialize_fields__
]
field_list = list(self.__table__.columns)
if "sqlite" in self.__table__.dialect_options:
props.DIALECT = "sqlite"
Expand All @@ -350,7 +354,7 @@ def __fs_get_props(self):
]
# add relationships
field_list += [
PermissiveDict(name=p, type="RELATIONSHIP")
PermissiveDict(name=self._fs_get_field_name(p), type="RELATIONSHIP")
for p in self.__fs_relationship_fields__
]
# add custom converters
Expand All @@ -365,25 +369,25 @@ def __fs_get_props(self):
if not f.converter:
# any non json supported types gets a str
if (
getattr(f.type, "python_type", None)
not in self.__fs_json_types
getattr(f.type, "python_type", None)
not in self.__fs_json_types
):
f.converter = str
props.field_list.append(f)

self.__fs_model_props[self.__table__] = props
return props

def __fs_get_fields(self):
def _fs_get_fields(self) -> List[str]:
"""
return a list of field objects that are valid
using __fs_private_field__
[{name,c__type,converter},...]
:return:
:return: list of strings
"""
fields = []
for c in self.__fs_get_props().field_list:
for c in self._fs_get_props().field_list:
if not self.__fs_private_field__(c.name):
fields.append(c)
return fields
Expand All @@ -404,7 +408,7 @@ def fs_as_dict(self):
# can be replaced using __fs_column_type_converters__
d = {}

for c in self.__fs_get_fields():
for c in self._fs_get_fields():
try:
d[c.name] = v = getattr(self, c.name, "")
except Exception as e:
Expand All @@ -429,22 +433,22 @@ def __fs_get_update_field_type(self, field, value):
:param field:
:return: class of the type
"""
props = self.__fs_get_props()
props = self._fs_get_props()
if props:
for f in props.field_list:
if f.name == field:
if (
f.c_type.startswith("VARCHAR")
or f.c_type.startswith("CHAR")
or f.c_type.startswith("TEXT")
f.c_type.startswith("VARCHAR")
or f.c_type.startswith("CHAR")
or f.c_type.startswith("TEXT")
):
return str
if f.c_type.startswith("INTEGER"):
return int
if (
f.c_type.startswith("FLOAT")
or f.c_type.startswith("REAL")
or f.c_type.startswith("NUMERIC")
f.c_type.startswith("FLOAT")
or f.c_type.startswith("REAL")
or f.c_type.startswith("NUMERIC")
):
return float
if f.c_type.startswith("DATE") or f.c_type.startswith("TIME"):
Expand Down Expand Up @@ -481,6 +485,14 @@ def __fs_convert_value(self, name, value):
return value
return value

@classmethod
def _fs_get_field_name(cls, field):
if isinstance(field, str):
return field
if cls.db and isinstance(field, cls.db.Column):
return field.name
return str(field).split(".")[-1]

@classmethod
def fs_request_create_form(cls, **kwargs):
"""
Expand All @@ -498,8 +510,9 @@ def fs_request_create_form(cls, **kwargs):
if len(__fs_create_fields__ or "") == 0:
__fs_create_fields__ = [
c.name
for c in new_item.__fs_get_fields()
if isinstance(c, cls.db.Column) and c.name != new_item.__fs_get_props().primary_key_field
for c in new_item._fs_get_fields()
if isinstance(c, cls.db.Column)
and c.name != new_item._fs_get_props().primary_key_field
]

try:
Expand All @@ -509,8 +522,7 @@ def fs_request_create_form(cls, **kwargs):

if len(json_data) > 0:
for field in __fs_create_fields__:
if cls.db and isinstance(field, cls.db.Column):
field = field.name
field = cls._fs_get_field_name(field)
if field in json_data:
value = json_data.get(field)
setattr(new_item, field, new_item.__fs_convert_value(field, value))
Expand Down Expand Up @@ -592,13 +604,12 @@ def fs_update_from_dict(self, data_dict):
if len(self.__fs_update_fields__ or "") == 0:
__fs_update_fields__ = [
c.name
for c in self.__fs_get_fields()
for c in self._fs_get_fields()
if isinstance(c, self.db.Column)
and c.name != self.__fs_get_props().primary_key_field
and c.name != self._fs_get_props().primary_key_field
]
for field in __fs_update_fields__:
if self.db and isinstance(field, self.db.Column):
field = field.name
field = self._fs_get_field_name(field)
self.__fs_previous_field_value__[field] = getattr(self, field)
if field in data_dict:
setattr(self, field, self.__fs_convert_value(field, data_dict[field]))
Expand Down
4 changes: 2 additions & 2 deletions pypar.commands.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ pip install pytest-flask

. venv3/bin/activate
python setup.py sdist bdist_wheel
twine check dist/flask_serialize-2.0.2*
twine upload dist/flask_serialize-2.0.2* -u martlark
twine check dist/flask_serialize-2.0.3*
twine upload dist/flask_serialize-2.0.3* -u martlark
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ setuptools
wheel
twine
restructuredtext_lint
black
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from codecs import open
from setuptools import setup

VERSION = "2.0.2"

VERSION = "2.0.3"
LONG_DESCRIPTION = open("README.rst", "r", encoding="utf-8").read()

setup(
Expand Down
3 changes: 3 additions & 0 deletions test/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,9 @@ def test_user(client):


def test_readme_list():
"""
Test that README.rst is good restructured text
"""
import restructuredtext_lint
errors = restructuredtext_lint.lint_file('../README.rst')
if len(errors):
Expand Down
17 changes: 9 additions & 8 deletions test/test_flask_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def route_setting_update_post(item_id):
item = Setting.query.get_or_404(item_id)
try:
if not item.fs_request_update_form():
return Response('update not allowed', 403)
return Response("update not allowed", 403)
except Exception as e:
print(e)
return Response("POST Error updating item: " + str(e), 500)
Expand Down Expand Up @@ -250,7 +250,7 @@ def route_user(item_id=None):

@app.route("/user_add_data/<int:item_id>", methods=["POST"])
def route_user_add_data(item_id):
value = request.form.get('data', '')
value = request.form.get("data", "")
user = User.query.get_or_404(item_id)
user_data = UserData(user=user, value=value)
db.session.add(user_data)
Expand Down Expand Up @@ -313,7 +313,7 @@ class User(fs_mixin, db.Model):
"UserData", backref="user", cascade="all, delete-orphan"
)

__fs_relationship_fields__ = ['data_items']
__fs_relationship_fields__ = ["data_items"]


class UserData(fs_mixin, db.Model):
Expand Down Expand Up @@ -366,7 +366,7 @@ class Setting(fs_mixin, FormPageMixin, db.Model):
"key",
"active",
"number",
"floaty",
floaty,
"scheduled",
"deci",
"lob",
Expand All @@ -382,9 +382,10 @@ class Setting(fs_mixin, FormPageMixin, db.Model):
"deci",
"lob",
]
__fs_exclude_serialize_fields__ = ["created"]

__fs_exclude_serialize_fields__ = [created]
__fs_exclude_json_serialize_fields__ = ["updated"]
__fs_relationship_fields__ = ["sub_settings", "single"]
__fs_relationship_fields__ = [sub_settings, "single"]
__fs_update_properties__ = ["prop_test"]
__fs_order_by_field__ = "value"
# lob
Expand Down Expand Up @@ -438,7 +439,7 @@ def __fs_can_access__(self):

# checks if Flask-Serialize can access
def __fs_can_update__(self):
if self.value == '9999':
if self.value == "9999":
return False
if not self.__fs_can_access__():
raise Exception("Update not allowed. Magic value!")
Expand Down Expand Up @@ -523,7 +524,7 @@ def __repr__(self):
class BadModel(fs_mixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(30), default="")
__fs_column_type_converters__ = {'VARCHAR': lambda x: x / 0}
__fs_column_type_converters__ = {"VARCHAR": lambda x: x / 0}

def __repr__(self):
return "<BadModel %r>" % (self.value)
Expand Down

0 comments on commit b5a2103

Please sign in to comment.