MongoEngine: How to append document to ListField - mongoengine

I'm using mongodb with python.
Also use MongoEngine to communicate with mongodb.
Now I made some simple board system that has comment function.
[model.py]
import datetime
from mongoengine import *
from config import DB_NAME
connect(DB_NAME)
class User(Document):
no = SequenceField()
userid = StringField(unique=True, required=True)
userpw = StringField(required=True)
created_at = DateTimeField(default=datetime.datetime.now())
class Comment(EmbeddedDocument):
content = StringField(required=True)
writer = ReferenceField(User, required=True)
class Board(Document):
no = SequenceField()
subject = StringField(required=True)
content = StringField(required=True)
writer = ReferenceField(User, required=True)
comments = ListField(EmbeddedDocumentField(Comment))
created_at = DateTimeField(default=datetime.datetime.now())
updated_at = DateTimeField(default=datetime.datetime.now())
In this code, How can I append new list to Board's comments field?
After searching for a hour, some document says that,
Board.objects(no=_no).update_one(push__comments=['123', '456']) will be works perfectly.
But it throw mongoengine.errors.InvalidQueryError: Querying the embedded document 'Comment' failed, due to an invalid query value error.
Maybe there is some syntax error, But I'm new at MongoEngine.
How can I solve this issue?
Thanks.

[SOLVED]
comment = Comments(content='test', writer='hide')
board = Board.objects(no=_no).get()
board.comments.append(comment)
board.save()
I solved issue like this.
But, if is there any solution, please comment it.

You can also use push operator https://docs.mongoengine.org/guide/querying.html?highlight=operators#atomic-updates

Related

Django Factory Boy object does not exist

I have an issue regarding factory boy using in the testing of my Lets assume I have this three models:
Class Company(models.Model):
name = str
Class Domain(models.Model):
company = ForeignKey(ref=Company)
name = str
created_at = datetime
Class Record(models.Model):
domain = ForeignKey(ref=Domain)
name = str
created_at = datetime
CompanyFactory(factory.django.DjangoModelFactory):
name = str
DomainFactory(factory.django.DjangoModelFactory):
company = factory.SubFactory(CompanyFactory)
name = str
created_at = datetime
RecordFactory(factory.django.DjangoModelFactory):
domain = factory.SubFactory(DomainFactory)
name = str
created_at = datetime
Having this, when I'm testing the Record views, at the begginning of every view I check that the Domain object is, in fact, related to the Company object such as:
try:
domain = Domain.objects.get(domain=domain_id, company__id=company_id)
except ObjectDoesNotExist:
return Response(
data={"message": "Domain isn't related to the company provided."}, status=status.HTTP_403_FORBIDDEN
)
But this code always returns an ObjectDoesNotExist exception when I make the testing with pytest+factory-boy but when I do manual testing runs fine. Have you experienced something similar? What I'm missing here?
Thanks in advance.
As requested per #gbrennon I'm adding the test code:
Hi! Thanks for answering.
My test code is as it follows:
class RecordCompanyAdminTests(CompanyAdminUser):
def setUp(self):
super(RecordCompanyAdminTests, self).setUp()
self.domain = DomainFactory.create()
self.record = RecordFactory.create()
def test_record_list_get(self):
url = reverse("autoprovisioning:record_list", kwargs={"company_id": self.company.id, "domain_id": self.domain.id})
response = self.client.get(url, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
how are u doing?
the test code wasn't included but i'll try to infer things
when I do manual testing runs fine
it seems like u already populated ur database! but listen, when u run ur test suite django will be using a "test database" in favor of isolation!
what u need to do is to create, using the factory_boy lib, ur "data fixtures"!
my suggestion without any context:
class MyTestCase(TestCase):
def setUp(self):
self.existing_domain_in_database = DomainFactory.create(...) # insert here the data to populate this model
and the desired data should already exists in ur "test database" for every test that is going to be run inside of this class

Flask + Firestore adding all doc elements to a dictionary {not looping}

I must be doing something wrong
I'm trying to loop through all documents in a collection and add the contents to a dictionary.
I added the data to a dictionary, but my intention was to loop through all docs:
firebase_admin.initialize_app(cred)
db = firestore.client()
blog_col = db.collection(u'blog')
class Art_cont:
def __init__(self,title,date,link):
self.title = title
self.date = date
self.link = link
def getLast4():
query = blog_col.order_by("date").limit_to_last(4).get()
for doc in query: #Need to find a way to loop through all docs, this doesn't work
db_title=doc.to_dict()["title"]
db_date=doc.to_dict()["date"]
db_link=doc.to_dict()["link"]
content1=Art_cont(db_title,db_date,db_link)
#here wrap them up with html and return them to app
print(content1.title,content1.date,content1.link)
When I run that code it only gives me the first doc content:
Vs the other docs that have the same structure:
Any advice would be appreciated.
Assuming that query is an object of your model, write db_title = doc.your_attribute_name.to_dict()['title'] and do the same for the other attributes.
Well, I almost had it, I ended up creating a function instead:
def getlist(x):
query = blog_col.order_by("date").limit_to_last(4).get()
docs=[]
for doc in query:
docs.append(doc)
docnum=docs[x]
db_title=docnum.to_dict()["title"]
db_date=docnum.to_dict()["date"]
db_link=docnum.to_dict()["link"]
content=Art_cont(db_title,db_date,db_link)
return content
print(getlist(0).title)
print(getlist(1).title)
etc...
Hope this helps anyone in a similar situation.

PostGIS geography does not support the "~=" function/operator

I am trying to save point field in the database via update_or_create method but it is giving me this error.
My function to save data:
for city in cities_data:
obj, created = models.City.objects.update_or_create(
name = city["name"],
country = city["country"],
state = city["state"],
point = Point(
float(city["longitude"]),
float(city["latitude"])
),
radius = city["radius"],
is_curated = city["is_curated"],
is_metro_city = city["is_metro_city"]
)
obj.save()
Model:
class City(models.Model):
name = models.CharField(max_length=50)
country = models.CharField(max_length=50)
state = models.CharField(max_length=50)
point = gis_models.PointField(geography=True, null=True)
is_metro_city = models.BooleanField(default=False)
is_curated = models.BooleanField(default=False)
radius = models.IntegerField(null=True)
when I try to run this I get this error:
ValueError: PostGIS geography does not support the "~=" function/operator.
I want to know why I am getting this error, I did not find any useful information related to this. Thanks in Advance.
If you want to use this you can use it like this:
obj, created = models.City.objects.update_or_create(
**city_data,
defaults={'point': Point(
float(city["longitude"]),
float(city["latitude"])
)}
)
I do not know why it does not work that way, but I made it work this way, if you find it out why it does not work you can improve the answer.
I have the same error and the problem was that I have a unique together constraint in model's class Meta eg:
class Meta:
unique_together = 'name', 'geom'
After deleting it, the error disappears although you must check by yourself if you need such constraint.

Mongoengine String Field in List field make unique

I use mongoengine and flask-restplus for API Server.
My model is here.
from datetime import datetime
from mongoengine import *
from config import DB_NAME
connect(DB_NAME)
class Board(Document):
d = datetime.now()
date = "{}-{}-{}".format(d.year, d.month, d.day)
no = SequenceField()
title = StringField(required=True)
body = StringField(required=True)
tags = ListField(StringField(unique=True))
password = StringField(required=True)
created_at = DateTimeField(default=date)
updated_at = DateTimeField(default=date)
I defined tags = ListField(StringField(unique=True)).
Because article can't got duplicated tag.
Insert tag code is here.
tag = ~some user input here~
article = Board.objects.get(no=article_no)
article.tags.append(tag)
article.save()
But when I insert the duplicated data, it stored data twice.
I want to tags has distinct data.
Why unique=True doesn't work?
Is there any solution about this issue?
Here's an explanation why does it not work. As a workaround you could use $addToSet operator provided by Mongo. It is also implemented in Mongoengine(docs) and an example usage would look like this
Board.objects(no=article_no).update_one(add_to_set__tags=tag)
where tag can be string or list of strings

Mongoengine Link to Existing Collection

I'm working with Flask/Mongoengine-MongoDB for my latest web application.
I'm familiar with Pymongo, but I'm new to object-document mappers like Mongoengine.
I have a database and collection set up already, and I basically just want to query it and return the corresponding object. Here's a look at my models.py...
from app import db
# ----------------------------------------
# Taking steps towards a working backend.
# ----------------------------------------
class Property(db.Document):
# Document variables.
total_annual_rates = db.IntField()
land_value = db.IntField()
land_area = db.IntField()
assessment_number = db.StringField(max_length=255, required=True)
address = db.StringField(max_length=255, required=True)
current_capital_value = db.IntField
valuation_as_at_date = db.StringField(max_length=255, required=True)
legal_description = db.StringField(max_length=255, required=True)
capital_value = db.IntField()
annual_value = db.StringField(max_length=255, required=True)
certificate_of_title_number = db.StringField(max_length=255, required=True)
def __repr__(self):
return address
def get_property_from_db(self, query_string):
if not query_string:
raise ValueError()
# Ultra-simple search for the moment.
properties_found = Property.objects(address=query_string)
return properties_found[0]
The error I get is as follows: IndexError: no such item for Cursor instance
This makes complete sense, since the object isn't pointing at any collection. Despite trolling through the docs for a while, I still have no idea how to do this.
Do any of you know how I could appropriately link up my Property class to my already extant database and collection?
The way to link a class to an existing collection can be accomplished as such, using meta:
class Person(db.DynamicDocument):
# Meta variables.
meta = {
'collection': 'properties'
}
# Document variables.
name = db.StringField()
age = db.IntField()
Then, when using the class object, one can actually make use of this functionality as might be expected with MongoEngine:
desired_documents = Person.objects(name="John Smith")
john = desired_documents[0]
Or something similar :) Hope this helps!
I was googling this same question and i noticed the answer has changed since the previous answer:
According to the latest Mongoengine guide:
If you need to change the name of the collection (e.g. to use MongoEngine with an existing
database), then create a class dictionary attribute called meta on your document, and set collection to the
name of the collection that you want your document class to use:
class Page(Document):
meta = {'collection': 'cmsPage'}
The code on the grey did the trick and i could use my data instantly.

Resources