Mongoengine updating embedded document - python

I have the following code and i'm trying to update an embedded document in a listfield.
store = store_service.get_store_from_product_id(product_id)
got_product, idx = get_product_from_store(store, product_id)
product = Product()
product.pid = got_product.pid
product.display_name = display_name
product.description = description
product.rank = rank
product.price = price
product.categories = categories
product.properties = properties
store.catalog.products[idx] = product
print store.catalog.products[idx].__unicode__()
store.save()
When I print out my product, it has the correct values, but when I save it, its not persisting. There are no errors being thrown. Any thoughts one what I could be doing wrong?

store.catalog.products[idx] = product can be applied for DictField(). For ListField(). You can try:
store.catalog.products = [product]
or
store.catalog.products.append(product)
And you need to call save on the object:
store.save()
There is the possibility of atomic updates which can help in other cases:
Store.objects(id='123400000').update_one(push__catalog__products=product)

Related

How to insert ManyToMany field in django

I want to insert a ManyToMany fields in my db using django.I select some customers using checkboxes.
This is my models.py :
class Campaign(models.Model):
title = models.CharField(max_length=255)
channel = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
target_prospect = models.ManyToManyField(ProspectClient,related_name='campaigns_prospect')
target_partner = models.ManyToManyField(PartnerClient,related_name='campaigns_partners')
I try the code below in my views.py but didn't work :
def campaigns_page(request):
if request.user.is_authenticated:
if request.user.profile == 'D' or request.user.profile == 'E' or request.user.is_superuser:
campaigns = Campaign.objects.all()
prospects = ProspectClient.objects.all()
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
context = {
'campaigns':campaigns,
'prospects':prospects,
'partners':partners
}
if request.method == 'POST':
title = request.POST['title']
channel = request.POST['channel']
start_date = request.POST['start_date']
end_date = request.POST['end_date']
descriptions = request.POST['goals'].split(",")
targets = request.POST['targets']
campaign = Campaign.objects.create(title=title,channel=channel,start_date=start_date,end_date=end_date)
for description in descriptions:
goal = Goal.objects.create(description=description)
goal.campaign.add(campaign)
for target in targets:
prospects.campaign.add(campaign)
partners.campaign.add(campaign)
return render(request,'CampaignManagement/campaigns_page.html',context)
return render(request, 'Login/logout.html')
If I delete the part of tergets it works.
But with this part it gives me This error : 'QuerySet' object has no attribute 'campaign'
How I can solve this ?
I see a couple of errors. Perhaps one or more are leading to the problem.
One
Try printing this:
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
print(partners)
I suspect it will print None since you are excluding all id's in PartnerClient.objects.values('id'). On another note you don't need the all() since exclude() will return all the results you are looking for.
Two
In the line for target in targets: what exactly are you iterating through? targets = request.POST['targets'] is just giving you a string, so it would iterate through each letter. Perhaps you meant:
targets = request.POST['targets'].split(", ")
like you did for descriptions? Or perhaps you are getting a list of items from your form, in which case you can use:
targets = request.POST.getlist('targets')

Django: Make a single DB call instead of iterating QuerySet

I have the following code that iterates the tags queryset, and for each item, creates a Department object and adds it to the departments list:
departments: List[Department] = []
tags = Tag.objects.filter(type="department")
for tag in tags:
dept_id = tag.reference_id
dept_name = tag.name
parent_tag = Tag.objects.get(type="department", reference_id=tag.parent_reference_id)
dept_parent_id = parent_tag.reference_id
departments.append(Department(dept_id, dept_name, dept_parent_id))
However, as you can see, it is making multiple DB calls via Tag.objects.get(), which seems highly inefficient. Is there an efficient way to populate that departments list without making so many DB calls?
TIA.
What you need to use is "in" in your query.
check querysets
Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')
so in your case you can use the the following example :
tags = Tag.objects.filter(id=some_id, type="department").values('id')
tags_list = [tag['id'] for tag in tags]
parent_tag = Tag.objects.get(id__in=tags_list, type="department")
I have used parts of the answer from #Vanda to write the following solution, and this solves my problem.
departments: List[Department] = []
tags = Tag.objects.filter(type="department")
parents_set = {tag.parent_reference_id for tag in tags}
for tag in tags:
dept_id = tag.reference_id
dept_name = tag.name
dept_parent_id = tag.parent_reference_id
if(dept_parent_id not in parents_set):
dept_parent_id = None
departments.append(Department(dept_id, dept_name, dept_parent_id))

counting/filtering database-entries over multiple foreign key-relations

These are my DB-Models:
class Category(models.Model):
name = models.CharField(max_length = 20, unique = True)
...
class Feed(models.Model):
title = models.CharField(max_length = 100)
category = models.ForeignKey(Category)
...
class Article(models.Model):
title = models.CharField(max_length = 100)
read = models.BooleanField(default = False)
feed = models.ForeignKey(Feed)
...
Every Article belongs to one Feed (source) and each Feed is in a Category.
Now, i want to create a view to display all categories with some meta-information,
e.g. how many unread articles are in category x.
I tried things like this, but nothing worked:
categories = Category.objects.filter(feed__article__read=False)\
.annotate(Count('feed__article'))
What is the proper way to extract those information?
Especially if i want to add further information like: number of feeds in category and
number of favored articles in one QuerySet (If possible)...
Any ideas?
Thanks.
EDIT: Since i had no idea how to 'solve' this problem, i've written an ugly workaround:
result = categories.values_list('name',
'feed__title',
'feed__article__title',
'feed__article__read')
for i in range(0, len(result)):
#if pointer changed to a new category
#dump current dict to list and clear dict for the new values
if last != result[i][0]:
category_list.append(category_info.copy())
category_info.clear()
last = result[i][0]
if some values None:
insert values
elif some other values None:
insert values
else:
category_info['name'] = result[i][0]
category_info['feed_count'] = category_info.get('feed_count', 0) + 1
category_info['all_article_count'] = category_info.get('all_article_count', 0) + 1
#if a article has not been read yet
if result[i][3] == False:
category_info['unread_article_count'] = category_info.get('unread_article_count', 0) + 1
#if this category is the last in the result-list
if i+1 == len(result):
category_list.append(category_info.copy())
i += 1
I am pretty sure there is a quicker and nicer way to get those information, but at least i can work with it for the moment :/
You must label the information. You should be able to use category.article_count for the items in the queryset if you use the query below.
categories = Category.objects.filter(feed__article__read=False)\
.annotate(article_count=Count('feed__article'))

Creating a django query that will retrieve the previous and next object based on alphabetical order

I have a django model that looks something like this:
class Definition
name = models.CharField(max_length=254)
text = models.TextField()
If I do the following query:
animal = Definition.objects.get(name='Owl')
and if I have the following definitions with these names in my database:
Elephant, Owl, Zebra, Human
is there a way to do a django query(ies) that will show me the previous and the next Definitions based on the animal object based on alphabetical order of the name field in the model?
I know that there are ways of getting previous/next based on datetime fields, but I am not so sure for this case.
I don't know of any way of doing this in less than three queries.
target = 'Owl'
animal = Definition.objects.get(name=target)
previous_animal = Definition.objects.order_by('name').filter(name__lt=target)[0]
next_animal = Definition.objects.order_by('name').filter(name__gt=target)[0]
If anyone comes across this like I just did...
heres my solution... it also loops(so if on last item it shows first item as next and if on first item shows last item as previous)
def get_previous_by_title(self):
curr_title = self.get_object().title
queryset = self.my_queryset()
try:
prev = queryset.filter(title__lt=curr_title).order_by("-title")[0:1].get()
except Video.DoesNotExist:
prev = queryset.order_by("-title")[0:1].get()
return prev
def get_next_by_title(self):
curr_title = self.get_object().title
queryset = self.my_queryset()
try:
next = queryset.filter(title__gt=curr_title).order_by("title")[0:1].get()
except Video.DoesNotExist:
next = queryset.order_by("title")[0:1].get()
return next
i have custom querysets based on user level so could just set the queryset as a normal queryset like... Video.objects.all() but anyplace I repeat code more than once I make a function

Python search list of objects that contain objects, partial matches

I'm trying to build a simple search engine for a small website. My initial thought is to avoid using larger packages such as Solr, Haystack, etc. because of the simplistic nature of my search needs.
My hope is that with some guidance I can make my code more pythonic, efficient, and most importantly function properly.
Intended functionality: return product results based on full or partial matches of item_number, product name, or category name (currently no implementation of category matching)
Some code:
import pymssql
import utils #My utilities
class Product(object):
def __init__(self, item_number, name, description, category, msds):
self.item_number = str(item_number).strip()
self.name = name
self.description = description
self.category = category
self.msds = str(msds).strip()
class Category(object):
def __init__(self, name, categories):
self.name = name
self.categories = categories
self.slug = utils.slugify(name)
self.products = []
categories = (
Category('Food', ('123', '12A')),
Category('Tables', ('354', '35A', '310', '31G')),
Category('Chemicals', ('845', '85A', '404', '325'))
)
products = []
conn = pymssql.connect(...)
curr = conn.cursor()
for Category in categories:
for c in Category.categories:
curr.execute('SELECT item_number, name, CAST(description as text), category, msds from tblProducts WHERE category=%s', c)
for row in curr:
product = Product(row[0], row[1], row[2], row[3], row[4])
products.append(product)
Category.products.append(product)
conn.close()
def product_search(*params):
results = []
for product in products:
for param in params:
name = str(product.name)
if (name.find(param.capitalize())) != -1:
results.append(product)
item_number = str(product.item_number)
if (item.number.find(param.upper())) != -1:
results.append(product)
print results
product_search('something')
MS SQL database with tables and fields I cannot change.
At most I will pull in about 200 products.
Some things that jump out at me. Nested for loops. Two different if statements in the product search which could result in duplicate products being added to the results.
My thought was that if I had the products in memory (the products will rarely change) I could cache them, reducing database dependence and possibly providing an efficient search.
...posting for now... will come back and add more thoughts
Edit:
The reason I have a Category object holding a list of Products is that I want to show html pages of Products organized by Category. Also, the actual category numbers may change in the future and holding a tuple seemed like simple painless solution. That and I have read-only access to the database.
The reason for a separate list of products was somewhat of a cheat. I have a page that shows all products with the ability to view MSDS (safety sheets). Also it provided one less level to traverse while searching.
Edit 2:
def product_search(*params):
results = []
lowerParams = [ param.lower() for param in params ]
for product in products:
item_number = (str(product.item_number)).lower()
name = (str(product.name)).lower()
for param in lowerParams:
if param in item_number or param in name:
results.append(product)
print results
Prepare all variables outside of the loops and use in instead of .find if you don't need the position of the substring:
def product_search(*params):
results = []
upperParams = [ param.upper() for param in params ]
for product in products:
name = str(product.name).upper()
item_number = str(product.item_number).upper()
for upperParam in upperParams:
if upperParam in name or upperParam in item_number:
results.append(product)
print results
If both the name and number matches the search parameters, the product will appear twice on the result list.
Since the products count is a small number, I recommend constructing a SELECT query like:
def search(*args):
import operator
cats = reduce(operator.add, [list(c.categories) for c in categories], [])
query = "SELECT * FROM tblProducts WHERE category IN (" + ','.join('?' * len(cats)) + ") name LIKE '%?%' or CAST(item_number AS TEXT) LIKE '%?%' ..."
curr.execute(query, cats + list(args)) # Not actual code
return list(curr)

Categories

Resources