Удалить элемент ListField по идентификатору с помощью Mongoengine

Я пытаюсь удалить элемент из ListField, ориентируясь на _id элемента списка с помощью mongoengine. Я имею в виду документы mongoengine по атомарным обновлениям здесь: http://docs.mongoengine.org/guide/querying.html#atomic-updates

Модель

class Prospect(db.Document):
  comments          = db.ListField(db.EmbeddedDocumentField('Comment'))

class Comment(db.EmbeddedDocument):
  _id             = db.ObjectIdField(default=bson.ObjectId())
  created_at      = db.DateTimeField(default=datetime.datetime.now, required=True)
  body            = db.StringField(verbose_name="Note", required=True)
  author          = db.StringField(verbose_name="Name", max_length=255, required=True)

Документ Mongo выглядит так:

...
"comments": [
      {
        "_id": {
            "$oid": "53bebb55c3b5a0db7829c15f"
        },
        "created_at": {
            "$date": "2014-07-09T18:26:58.444Z"
        },
        "body": "Did something\n",
        "author": "Dave Roma"
    },

И я пытаюсь удалить такой комментарий:

prospect = Prospect.objects(id=request.form['prospect_id']).update_one(pull___id=request.form['comment_id'])

И я получаю mongoengine invalidQueryError:

InvalidQueryError: Cannot resolve field "_id"

person Dave Roma    schedule 10.07.2014    source источник
comment
Попробуйте использовать id, а также _id = db.ObjectIdField(default=bson.ObjectId()) установит значение по умолчанию при загрузке модуля, поэтому все они будут иметь одинаковый идентификатор! Вам нужно использовать callable - т.е. пропустить ()   -  person Ross    schedule 11.07.2014
comment
@Ross Спасибо - вы правы, каждый раз создается один и тот же идентификатор - как вы рекомендуете генерировать уникальный ObjectId для EmbeddedDocument в этом случае (я хочу имитировать, как mongoengine создает идентификатор для обычных документов)   -  person Dave Roma    schedule 11.07.2014
comment
вы можете сделать лямбу: bson.ObjectId()   -  person Julian Hille    schedule 03.02.2015


Ответы (1)


Ответом на ваш вопрос будет:

prospect = Prospect.objects(id=request.form['prospect_id']).update_one(pull__comments__id=request.form['comment_id'])

Ваш первоначальный запрос пытался найти поле _id в mongoengine, которого, очевидно, не существовало.

А теперь решение проблемы, о которой вы говорили в комментарии.

Решение 1.

class Prospect(db.Document):
    comments = db.ListField(db.EmbeddedDocumentField('Comment'))

    def get_next_comment_id(self):
        return len(self.comments)+1

    def add_new_comment(self, author, body):
        self.update(add_to_set__comments=Comment(_id=self.get_next_comment_id(), author=author, body=body)

class Comment(db.EmbeddedDocument):
  _id             = db.IntField(required=True)
  created_at      = db.DateTimeField(default=datetime.datetime.now, required=True)
  body            = db.StringField(verbose_name="Note", required=True)
  author          = db.StringField(verbose_name="Name", max_length=255, required=True)

Приведенное выше решение может дать сбой, если два клиента одновременно попытаются добавить новый пост. У них может быть одинаковый _id. Невозможно обеспечить уникальность встроенных полей документа в монго. Вы можете сделать это только с клиентским кодом.

Решение 2.

Сделайте отдельную коллекцию комментариев.

class Prospect(db.Document):
    comments = db.ListField(db.EmbeddedDocumentField('Comment'))

class Comment(db.Document):
  on_prospect     = db.ReferenceField(Prospect)
  created_at      = db.DateTimeField(default=datetime.datetime.now, required=True)
  body            = db.StringField(verbose_name="Note", required=True)
  author          = db.StringField(verbose_name="Name", max_length=255, required=True)

def add_new_comment(prospect, author, body):
    comment = Comment(on_prospect = prospect, author=author, body=body).save()
    return comment.id

def get_all_posts_on_prospect(prospect):
    return Comment.objects(on_prospect = prospect).order_by('-id')

Этот уникальный идентификатор будет присвоен каждому комментарию.

Решение 3.

Это просто идея, я не уверен в плюсах и минусах.

class Prospect(db.Document):
    comments = db.ListField(db.EmbeddedDocumentField('Comment'))

    def get_id_for_comment(self, comment):
        return self.comments.index(comment)

class Comment(db.EmbeddedDocument):
  _id             = db.IntField()
  created_at      = db.DateTimeField(default=datetime.datetime.now, required=True)
  body            = db.StringField(verbose_name="Note", required=True)
  author          = db.StringField(verbose_name="Name", max_length=255, required=True)

def add_new_comment(prospect, author, body)
    comment = Comment(_id=self.get_next_comment_id(), author=author, body=body)
    prospect.update(add_to_set__comments = comment)
    cid = prospect.get_id_for_comment(comment)
    prospect.update(set__comments__cid__id = cid)
    return cid
person shshank    schedule 24.07.2014