python3 marshmallow學習


python3 marshmallow學習

官方文檔:https://marshmallow.readthedocs.io/en/stable/

安裝:

pip install -U marshmallow

Object -> dict

1 簡單的例子
from marshmallow import Schema, fields


class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    age = fields.Integer()
    create_at = fields.DateTime(dump_only=True)


class User:
    def __init__(self, *, name, email, age):
        self.name = name
        self.email = email
        self.age = age


zhuyu = User(name="朱宇", email="33333@qq.com", age=22)
user_schema = UserSchema()
result = user_schema.dump(zhuyu, many=False)
print(result,isinstance(result, dict))
# {'age': 22, 'name': '朱宇', 'email': '33333@qq.com'} True
result = user_schema.dumps(zhuyu, many=False)
print(result,isinstance(result, str))
# {"age": 22, "name": "\u6731\u5b87", "email": "33333@qq.com"} True

這是一個將類轉化為字典或者json格式的字典的例子,使用UserSchema實例的dump轉為字典格式,dumps方法轉為json格式的字符串。

這種就很像django中的序列化了。通過orm查詢到的數據,轉為字典格式數據,通過json進行前后台數據傳輸。

從上面這個例子中,我們的User對象並沒有create_at這個對象,通過UserSchema的對象調用dump,得到的數據中也沒有create_at這個數據。dump這個方法,對傳入的參數的屬性的規定不是很嚴格,只會對UserSchema存在的屬性和User存在的屬性進行操作。屬性名必須要相同。

2 使用only,exclude的使用

對於上面的例子,假如有個需求,不需要看到user的age屬性。

zhuyu = User(name="朱宇", email="33333@qq.com", age=22)
user_schema = UserSchema(only=("name", "email"))
result = user_schema.dump(zhuyu, many=False)
print(result)
# {'name': '朱宇', 'email': '33333@qq.com'}
user_schema = UserSchema(exclude=("age",))
result = user_schema.dump(zhuyu, many=False)
print(result)
# {'name': '朱宇', 'email': '33333@qq.com'}

3 fields.Nested的使用

就拿orm來說,兩個model類型,很有可能存在一對一,一對多,多對多的關系,那么怎么進行序列化呢?

關於Nested具體使用:https://marshmallow.readthedocs.io/en/stable/nesting.html

from marshmallow import Schema, fields

class User:
    def __init__(self, name, pwd, email):
        self.name = name
        self.pwd = pwd
        self.email = email


class Blog:
    def __init__(self, title, author: User):
        self.title = title
        self.author = author


class UserSchema(Schema):
    name = fields.Str()
    pwd = fields.Str()
    email = fields.Email()


class BlogSchema(Schema):
    title = fields.Str()
    author = fields.Nested(UserSchema)


zhuyu = User(name="朱宇", pwd="123456", email="33333@qq.com")
blog = Blog(title="朱宇的博客", author=zhuyu)

user_schema = UserSchema()
result = user_schema.dump(zhuyu, many=False)
print(result)
# {'email': '33333@qq.com', 'name': '朱宇', 'pwd': '123456'}
blog_schema = BlogSchema()
result = blog_schema.dump(blog, many=False)
print(result)
# {'author': {'email': '33333@qq.com', 'name': '朱宇', 'pwd': '123456'}, 'title': '朱宇的博客'}

validate對數據校驗

在我們寫web的時候,經常會接受到攜帶參數的請求。我們通過這個攜帶的參數,完成相應的業務邏輯。但是服務器不能知道該請求是從哪種方式過來,攜帶的參數就會很多種,所以我們不能對前台傳來的參數百分百信任,必須做參數校驗。

當然對前台參數進行校驗的包有很多,這里就說marshmallow

from marshmallow import Schema, fields, pprint
import datetime as dt


class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()


user_dict = {
    "name": "朱宇",
    "email": "3333333.com",
}
user_schema = UserSchema()
result = user_schema.validate(data=user_dict)
pprint(result)
# {'email': ['Not a valid email address.']}

調用UserSchema實例的validate方法,參數為字典格式的數據,返回值就是一個字典類型。如果參數符合校驗規則的話,那么返回的就是一個空字典,不符合的話,返回的就是key為字段名,value為錯誤信息。上面這個例子,說email這個字段不是一個合法的郵箱地址。fields.Email它會有一套默認的校驗規則,我們闊以通過validate這個關鍵參數,定義自己需要的驗證規則。

validate關鍵字參數
from marshmallow import Schema, fields, validate, pprint
from hashlib import md5


class UserSchema(Schema):
    # 用戶名為字符串,最短為2位,最長為8位
    name = fields.String(validate=validate.Length(min=2, max=8, error="用戶名長度2-8位"))
    # 密碼必須為32位字符串,這里我們使用md5加密
    password = fields.String(validate=validate.Length(equal=32, error="密碼長度必須為32位"))
    # 年齡是14歲到77歲之間
    age = fields.Integer(validate=validate.Range(14, 77, error="必須為14-77之間"))
    # 性別必須是男,女,其他中的一個
    grade = fields.String(validate=validate.OneOf(choices=("男", "女", "其他"), error="必須為男,女,其他三個中的一個"))
    #
    email = fields.String(validate=validate.Email(error="郵箱格式錯誤"))


error_user_dict = {
    "name": "朱",
    "password": "333444",
    "age": 13,
    "grade": "不清楚",
    "email": "3333.com"
}
user_schema = UserSchema()
result = user_schema.validate(data=error_user_dict, many=False)
pprint(result)
# {'age': ['必須為14-77之間'],
#  'email': ['郵箱格式錯誤'],
#  'grade': ['必須為男,女,其他三個中的一個'],
#  'name': ['用戶名長度2-8位'],
#  'password': ['密碼長度必須為32位']}

user_dict = {
    "name": "朱春雨",
    "password": md5(bytes("1", encoding="utf-8")).hexdigest(),
    "age": 22,
    "grade": "男",
    "email": "333444@qq.com"
}
result = user_schema.validate(data=user_dict, many=False)
pprint(result)
# {}

關於validate這個模塊下還有其他的校驗規則,具體可以去源碼當中看。

除了本身提供的校驗方法之外,我們同樣可以自定義校驗規則

# name = fields.String(validate=validate.Length(min=2, max=8, error="用戶名長度2-8位"))

這是我們上面代碼中,name字段的校驗方法,validate的值為Length對象的實例,當要對name這個字段進行校驗時,會將name對應的值會這樣執行:validate.Length(min=2, max=8, error="用戶名長度2-8位")(name的值),那么這樣的話,就會調用Length對象里的__call__方法,將name的值傳入。我們也可以這樣去寫自己的校驗方法。大致的意思是validate=值,這個值他是可調用的,也就是有__call__方法。

# 方法一:類的方式
import typing
from marshmallow import Schema, fields, validate, ValidationError, pprint


class CustomizeVal(validate.Validator):

    def __init__(self, *, word: str, error: str = None):
        self.word = word
        self.error = error or "不能已什么開頭{}".format(self.word)

    def __call__(self, value: str, *args, **kwargs) -> typing.Any:
        if value.startswith(self.word):
            raise ValidationError(self.error)

        return value


class TestSchema(Schema):
    name = fields.String(validate=CustomizeVal(word="SB"))


error_test_dict = {
    "name": "SB_SBSBBSBS"
}
test_schema = TestSchema()
res = test_schema.validate(data=error_test_dict, many=False)
pprint(res)
# 'name': ['不能已什么開頭SB']}
test_dict = {
    "name": "朱宇"
}
res = test_schema.validate(data=test_dict, many=False)
pprint(res)
# {}

# 方法二:函數的方法
def func_val(value: str) -> typing.Any:
    """定義不能以SB結尾的校驗規則"""
    if value.endswith("SB"):
        raise ValidationError("不能以SB結尾")
    return value


class Test2Schema(Schema):
    name = fields.String(validate=func_val)


error_test_dict = {
    "name": "朱宇_SB"
}
test_2_schema = Test2Schema()
res = test_2_schema.validate(data=error_test_dict,many=False)
pprint(res)
# {'name': ['不能以SB結尾']}

dict -> Object

1 簡單的例子
from marshmallow import Schema, fields
import json


class User:
    def __init__(self, name, password):
        self.name = name
        self.password = password


class UserSchema(Schema):
    name = fields.String()
    password = fields.String()


user_dict = {
    "name": "朱宇",
    "password": "123"
}
user_dict_json = json.dumps(user_dict)
user_schema = UserSchema()
res = user_schema.load(data=user_dict, many=False)
print(res, isinstance(res, dict))
# {'password': '123', 'name': '朱宇'} True
res = user_schema.loads(user_dict_json, many=False)
print(res, isinstance(res, dict))
# {'password': '123', 'name': '朱宇'} True

調用load或者loads方法,它也會進行校驗,但是一旦校驗失敗了,就會拋異常,且不會捕捉。所以一般的話,還是先調用validate方法進行校驗,檢驗無誤的話,再進行load方法調用。上面的例子中變量res的類型為dict。如果想要res的類型變為User類型呢?

from marshmallow import Schema, fields, post_load


class User:
    def __init__(self, name, password):
        self.name = name
        self.password = password

    def __repr__(self):
        return "<User-{}>".format(self.name)


class UserSchema(Schema):
    name = fields.String()
    password = fields.String()

    @post_load
    def make_user(self, data, **kwargs):
        return User(**data)


user_dict = {
    "name": "朱宇",
    "password": "123"
}
user_schema = UserSchema()
res = user_schema.load(data=user_dict, many=False)
print(res, isinstance(res, User))
# <User-朱宇> True

知識整理就寫到這里。

最好是去看marshmallow的官方文檔:https://marshmallow.readthedocs.io/en/stable/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM