Как использовать почтальона для загрузки данных DBRef в json?

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

Вот моя модель:

class Users(db.Document):
    _id = db.StringField()
    name = db.StringField()
    picture = db.FileField()
    email = db.StringField()
    password = db.StringField()
    meta = {'collection': 'Users'}

В почтальоне я пытаюсь опубликовать данные следующим образом:

{
"_id" : "1",
"name" : "John Doe",
"picture": [{"$id": "5e6a...f9q102"}], #This is the reference id for the image already in the database, in fs.files
"password" : "<hashed pw>",
"email" : "[email protected]"
}

Я использую flask restful api, поэтому в скрипте python функция post определяется так:

def post(self):
    body = request.get_json()
    print (body)
    user = Users()
    user = Users(**body).save()
    return 'Successful Upload', 200

Я получаю сообщение об ошибке, когда пытаюсь использовать вышеуказанное соглашение:

mongoengine.errors.ValidationError: ValidationError (Users:None) ('list' object has no attribute
    'grid_id': ['picture'])

Как опубликовать нового пользователя в почтальоне? Ваша помощь приветствуется


person blazedtrailz    schedule 15.03.2020    source источник


Ответы (2)


Вам нужно немного изменить свой код

Добавьте эти импорты:

from mongoengine.fields import ImageGridFsProxy
from mongoengine import ReferenceField, DynamicDocument
from bson.dbref import DBRef
from bson import ObjectId

Измените определение поля класса picture + добавьте дополнительный класс fs

class Fs(DynamicDocument):
    #Add 'db_alias':'default' to meta
    meta = {'collection': 'fs.files'}

class Users(Document):
    ...
    picture = ReferenceField('Fs', dbref=True)
    ...

Теперь вам нужно создать новый экземпляр для DBRef следующим образом:

def post(self):
    body = request.get_json()
    body["picture"] = DBRef('fs.files', ObjectId(body["picture"]))

    #mongoengine assumes `ObjectId('xxx')` already exists in `fs.files`. 
    #If you want to check, run below code:
    #if Fs.objects(_id=body["picture"].id).first() is None:
    #    return 'Picture ' + str(body["picture"].id) + ' not found', 400

    user = Users(**body).save()
    return 'Successful Upload', 200

В конце, если вам нужно прочитать содержимое изображения:

image = ImageGridFsProxy(grid_id=ObjectId('xxx'))
f = open("image.png", "wb")
f.write(image.read())
f.close()
person Valijon    schedule 15.03.2020

Это была ошибка проверки. База данных принимала JSON в определенном формате, отличном от того, что я публиковал. И то, как я обрабатывал почтовый запрос, тоже было неверным. Это ожидаемый формат:

{
    ...,
    "picture" = {"$ref": "fs.files", 
                 "$id": ObjectId("5e6a...f9q102")},
    ...
}

Почтальон не может принять вышеуказанный формат, вместо этого он принял это:

{
    "_id" : "1",
    "name" : "John Doe",
    "picture": {"$ref": "fs.files", "$id": {"$oid": "5e6a...f9q102"}}, 
    "password" : "<hashed pw>",
    "email" : "[email protected]"
}

Чтобы это сработало, я изменил модель, чтобы она выглядела так в моем приложении для фляги:

class Users(db.Document):
    _id = db.StringField()
    name = db.StringField()
    picture = db.ReferenceField('fs.files') #I changed this to a reference field because it holds the reference for the file and not the actual file in the database
    upload_picture = db.FileField() #I added this field so I can still upload pics via flask and via this document
    email = db.StringField()
    password = db.StringField()
    meta = {'collection': 'Users'}

Затем мне пришлось добавить этот импорт и изменить код, чтобы он считывал ввод как JSON и передал эталонное значение изображения в ObjectId(id), чтобы оно соответствовало формату, ожидаемому базой данных.

from bson.json_util import loads

def post(self):
        body = str(request.get_json())
        x = body.replace("'", '"') #replace single quotes as double quotes to match JSON format
        data = loads(x)
        officer = Officers(**data).save()
        return 'Successful Upload', 200

Тогда вуаля, это работает!

person blazedtrailz    schedule 16.03.2020