pythonmongodbflaskmongoengineflask-mongoengine

MongoEngine ValidationError


i have to creating a database and what to check whether all the entries are being input in the database or not using the python shell.

i wrote a class called Trial

class Trial(db.Document):
    project_name = db.StringField(max_length=255,required=True)
    list_of_materials = db.ListField(db.EmbeddedDocumentField('List_Of_Materials'))
    abstract = db.StringField(max_length=255,required=True)
    vehicle = db.StringField(max_length=255,required=False)
    responsibilities = db.ListField(db.EmbeddedDocumentField('Responsibilities')) 

and i defined classes List_of_Materials and Responsibilities as follows:

class Responsibilities(db.EmbeddedDocument):
    employee = db.StringField(max_length=255, required = True)
    objective = db.StringField(max_length=255, required = True)

class List_Of_Materials(db.EmbeddedDocument):
    mat_name = db.StringField(max_length=255, required=True)
    mat_type = db.StringField()
    mat_comments = db.StringField(max_length = 255)

now i use the python shell to make an entry into the database.

trial_test = Trial(project_name = 'nio field trip management',
                list_of_materials = [List_Of_Materials(mat_name = 'Laptop')],
                abstract = 'All is well that ends well',
                vehicle = Vehicle(veh_name='My Laptop',veh_num='GA07EX1234'),
                responsibilities = [Responsibilities(employee='Prashant',objective='Setup the Website')],

and i get the following error :

Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
  File "C:\Anaconda\lib\site-packages\mongoengine\base\document.py", line 85, in __init__
    value = field.to_python(value)
  File "C:\Anaconda\lib\site-packages\mongoengine\base\fields.py", line 261, in to_python
    self.error('You can only reference documents once they'
  File "C:\Anaconda\lib\site-packages\mongoengine\base\fields.py", line 124, in error
raise ValidationError(message, errors=errors, field_name=field_name)
mongoengine.errors.ValidationError: You can only reference documents once they have been saved to the database

LINE 12 of the code is responsibilities=db.ListField(db.EmbeddedDocumentField('Responsibilities'))

What i could interpret from the above error is that we can have to first entry into the classes "Responsibilities" and "List_Of_Material" , but the entry in "List_Of_Material" does not show any error while the ones in "Responsibilities" show the above error.

What can i do to avoid this problem?


Solution

  • Are you sure the Trial model you send is the right one ?

    This ValidationError is thrown when you declare a ReferenceField in a document, but you try to save this document before saving the referenced document (Mongoengine represents a reference field in MongoDB as an dictionary containing the class and the ObjectId of the reference).

    EmbeddedDocumentField is not ReferenceField. They are saved at the same time as you save the main document. Thus, I don't think your error is coming from either list_of_materials or responsibilities attribute. If you remove the vehicle assignment in your example, this code works perfectly.

    Given your code example, I am guessing there is a class like

    class Vehicle(db.Document):
        veh_name = db.StringField()
        veh_num = db.StringField()
    

    and your model is:

    class Trial(db.Document):
        project_name = db.StringField(max_length=255, required=True)
        list_of_materials = db.ListField(db.EmbeddedDocumentField('List_Of_Materials'))
        abstract = db.StringField(max_length=255, required=True)
        vehicle = db.ReferenceField(Vehicle)
        responsibilities = db.ListField(db.EmbeddedDocumentField('Responsibilities'))
    

    And then, your example should be:

    trial_test = Trial(
         project_name = 'nio field trip management',
         list_of_materials = [List_Of_Materials(mat_name = 'Laptop')],
         abstract = 'All is well that ends well',
         vehicle = Vehicle(veh_name='My Laptop',veh_num='GA07EX1234'),
         responsibilities = [Responsibilities(employee='Prashant',objective='Setup the Website')]
    )
    trial_test.vehicle.save()  # Saving the reference BEFORE saving the trial.
    trial_test.save()