2015-10-09 3 views
4

Я разрабатываю api в Flask, используя Marshmallow для сериализации/десериализации/проверки и SQLAlchemy как мой ORM.Обновление таблицы sqlalchemy с помощью схемы Marshmallow

В моих функциях обновления я хотел бы ограничить поля, которые можно обновить, например. Я не хочу, чтобы пользователи могли менять свою электронную почту на данный момент.

Для этого я создал схему (UserSchema) с ее полями, ограниченными кортежем (UserSchemaTypes.UPDATE_FIELDS). Кортеж не включает электронную почту.

Проблема, с которой я столкнулась, заключается в том, что письмо является обязательным полем для пользовательских строк в моей базе данных.

Поэтому, когда я создаю объект модели пользователя с помощью схемы (users_schema.load (user_json)), на сеанс sqlalchemy добавляется незаконный объект.

#schema to validate the posted fields against 
users_schema = UserSchema(only=UserSchemaTypes.UPDATE_FIELDS) 
#attempt to deserialize the posted json to a User model object using the schema 
user_data = users_schema.load(user_json) 
if not user_data.errors:#update data passed validation 
    user_update_obj = user_data.data 
    User.update(user_id,vars(user_update_obj)) 

В самой моей функции обновления Затем я должен удалить этот незаконный объект из сеанса через db.session.expunge_all(), как если бы я не получаю OperationalError.

@staticmethod  
def update(p_id,data): 
    db.session.expunge_all()#hack I want to remove 
    user = User.query.get(p_id) 
    for k, v in data.iteritems(): 
     setattr(user, k, v) 
    db.session.commit() 

OperationalError получил, когда db.session.expunge_all() Удален:

OperationalError: (raised as a result of Query-invoked autoflush; consider   
using a session.no_autoflush block if this flush is occurring prematurely) 
(_mysql_exceptions.OperationalError) (1048, "Column 'email' cannot be null") [SQL: u'INSERT INTO user (email, password, active, phone, current_login_at, last_login_at, current_login_ip, last_login_ip, login_count) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)'] [parameters: (None, None, 1, '0444', None, None, None, None, None)] 

Есть ли лучше/уборщик способа сделать это?

ответ

1

От Стивена Лория https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/33#issuecomment-147008000

Вот несколько различных подходов можно принять:

Вариант 1: параметр поля

from marshmallow import Schema, fields, pre_load 

class BaseSchema(Schema): 
    @pre_load 
    def check_update_fields(self, data) 
     non_update_fields = set([ 
      fname, fobj for fname, obj in self.fields 
      if fobj.metadata.get('can_update') is False 
     ]) 
     return { 
      key: value for key, value in data.items() 
      if key not in non_update_fields 
     } 

class UserSchema(BaseSchema): 
    name = fields.Str() 
    email = fields.Str(can_update=False) 

Вариант 2: класс Meta опция

from marshmallow import Schema, SchemaOpts, fields, pre_load 
class BaseSchemaOpts(SchemaOpts): 
    def __init__(self, meta): 
     super().__init__(meta) 
     self.update_fields = getattr(meta, 'update_fields', set()) 

class BaseSchema(Schema): 
    OPTIONS_CLASS = BaseSchemaOpts 

    email = fields.Str(can_update=False) 

    @pre_load 
    def check_update_fields(self, data) 
     non_update_fields = set(self.fields) - set(self.opts.update_fields) 
     return { 
      key: value for key, value in data.items() 
      if key not in non_update_fields 
     } 

class UserSchema(BaseSchema): 

    name = fields.Str() 
    email = fields.Str() 

    class Meta: 
     update_fields = ('name',) 
0

Ваша база данных не соответствует вашей модели.

Вы должны синхронизировать базу данных, потому что вы что-то изменили о своей модели (по электронной почте null, а не null). Либо удалите таблицу и выполните повторную синхронизацию базы данных, либо используйте инструмент миграции, чтобы изменить таблицу в соответствии с вашей моделью.

Смежные вопросы