Django model aggregate matches count in ManyToMany - django

There is Model with ManyToMany field:
class Number(Model):
current_number = IntegerField()
class MyModel(models.Model):
numbers_set = models.ManyToMany(Number)
For example we have such dataset:
my_model_1.numbers_set = [1, 2, 3, 4]
my_model_2.numbers_set = [2, 3, 4, 5]
my_model_3.numbers_set = [3, 4, 5, 6]
my_model_4.numbers_set = [4, 5, 6, 7]
my_model_5.numbers_set = [4, 5, 6, 7]
I'm looking for a way to aggregate MyModel by amount of same numbers.
f.e. MyModel objects that have at least 3 same numbers in theirs numbers_set.
[
[my_model_1, my_model_2],
[my_model_2, my_model_3],
[my_model_3, my_model_4, my_model_5],
]

if you are using Postgres version 9.4 and Django version 1.9 , It's better to use JSONField() rather than using ManyToMany(), for indexing purpose use jsonb indexing on Postgres which will provide you efficient query for fetching data. Check here

Related

Why the output of Set in Pyomo is disordered?

I am learning Pyomo and I have some questions regarding the use of Set.
Below is the code:
import pyomo.environ as pe
model = ConcreteModel()
model.A = pe.Set(initialize=[4,8,10,7])
print(model.A.pprint())
print(model.A.data())
Output:
A : Dim=0, Dimen=1, Size=4, Domain=None, Ordered=False, Bounds=(4, 10)
[4, 7, 8, 10]
None
{8, 10, 4, 7}
By using model.A.pprint(), the output is [4,7,8,10]
But with model.A.data(), the output is {8,10,4,7}
I am wondering why this situation would happen.

Put Django ORM iterations to one request

I have three models:
class Box(models.Model):
name = models.TextField(blank=True, null=True)
class Toy(models.Model):
box = models.ForeignKey(Box, related_name='toys')
class ToyAttributes(models.Model):
toy = models.ForeignKey(Toy)
color = models.ForeignKey(Color, related_name='colors')
And list:
pairs = [[10, 3], [4, 5], [1, 2]]
Where every value is a pair or box and color id's.
I need to filter this data and return boxes objects with toys of needed color.
Now I do this:
for n in list:
box = Box.objects.filter(id=n[0], toys__colors=n[1])
if box.exist():
...
But it takes a lot of time for long lists, as I understand because of multiple SQL requests. Can I make it faster? Is it possible to get only needed boxes with one request and how can I make it? Thanks!
You should look at django Q function and construct your query in a loop adding values to Q like so
query = Q()
for box_id, toy_color in [[10, 3], [4, 5], [1, 2]]:
query |= Q(Q(id=box_id) & Q(toys__colors=toy_color))
Box.objects.filter(query)
This should work for you.
from django.db.models import Q
pairs = [[10, 3], [4, 5], [1, 2]]
conditions = [Q(id=box) & Q(toys__colors=color) for box, color in pairs]
query = Q()
for c in conditions:
query |= c
Box.objects.filter(query)

Appending values to existing keys in a dictionary

I am working on a website crawler.
The task of this crawler is to look for products and their respective brands.
The written crawler gives me two lists as output.
This works fine so far.
The problem I am facing is that I want to put this two list into a dictionary.
The brands should be the keys and the products the values.
So that I can ask for the brands(keys) on this website and get the products(values) as output.
e.g.:
brands = ["a", "b", "c", "a", "a", "b"]
products = [ 1, 2, 3, 4, 5, 6]
offer = {}
for i in range(0,len(brands)-1):
offer[brands[i]] = products[i]
desired output:
offer = { a: [1, 4, 5] ; b: [2, 6] ; c: [3]}
actual output:
offer = { a: 5 ; b: 6 ; c: 3}
I kinda see that the for-loop could be the problem since I am using equal-sign, which leads that the values are updating, but not appending.
thanks for your help
You have got to the right mistake
What you need to do is save all the results in a list.
brands = ["a", "b", "c", "a", "a", "b"]
products = [ 1, 2, 3, 4, 5, 6]
offer = {}
for i in range(0,len(brands)-1):
if brands[i] not in offer:
offer[brands[i]] = []
offer[brands[i]].append(products[i])
You can avoid the if condition while iterating by using defaultdict.
defaultdict is an awesome thing for your use case, without making many changes to your code, the following is the way to do it:
from collections import defaultdict
brands = ["a", "b", "c", "a", "a", "b"]
products = [ 1, 2, 3, 4, 5, 6]
offer = defaultdict(list)
for brand, product in zip(brands, products):
offer[brand].append(product)

Making a dictionary? from 2 lists / columns

I have a large database with several columns, i need data from 2 of these.
The end result is to have 2 drop down menus where the first one sets "names" and the second one is the "numbers" values that has been merged into the name. I just need the data available so i can input it into another program.
So a list or dictionary that contains the Unique values of the "names" list, with the numbers from the numbers list appended to them.
# Just a list of random names and numbers for testing
names = [
"Cindi Brookins",
"Cumberband Hamberdund",
"Roger Ramsden",
"Cumberband Hamberdund",
"Lorean Dibble",
"Lorean Dibble",
"Coleen Snider",
"Rey Bains",
"Maxine Rader",
"Cindi Brookins",
"Catharine Vena",
"Lanny Mckennon",
"Berta Urban",
"Rey Bains",
"Roger Ramsden",
"Lanny Mckennon",
"Catharine Vena",
"Berta Urban",
"Maxine Rader",
"Coleen Snider"
]
numbers = [
6,
5,
7,
10,
3,
9,
1,
1,
2,
7,
4,
2,
8,
3,
8,
10,
4,
9,
6,
5
]
So in the above example "Berta Urban" would appear once, but still have the numbers 8 and 9 assigned, "Rey Bains" would have 1 and 3.
I have tried with
mergedlist = dict(zip(names, numbers))
But that only assigns the last of the numbers to the name.
I am not sure if i can make a dictionary with Unique "names" that holds multiple "numbers".
You only get the last number associated with each name because dictionary keys are unique (otherwise they wouldn't be much use). So if you do
mergedlist["Berta Urban"] = 8
and after that
mergedlist["Berta Urban"] = 9
the result will be
{'Berta Urban': 9}
Just as if you did:
berta_urban = 8
berta_urban = 9
In that case you would expect the value of berta_urban to be 9 and not [8,9].
So, as you can see, you need an append not an assignment to your dict entry.
from collections import defaultdict
mergedlist = defaultdict(list)
for (name,number) in zip(names, numbers): mergedlist[name].append(number)
This gives:
{'Coleen Snider': [1, 5],
'Cindi Brookins': [6, 7],
'Cumberband Hamberdund': [5, 10],
'Roger Ramsden': [7, 8],
'Lorean Dibble': [3, 9],
'Rey Bains': [1, 3],
'Maxine Rader': [2, 6],
'Catharine Vena': [4, 4],
'Lanny Mckennon': [2, 10],
'Berta Urban': [8, 9]
}
which is what I think you want. Note that you will get duplicates, as in 'Catharine Vena': [4, 4] and you will also get a list of numbers for each name, even if the list has only one number in it.
You cannot have multiple keys of the same name in a dict, but your dict keys can be unique while holding a list of matching numbers. Something like:
mergedlist = {}
for i, v in enumerate(names):
mergedlist[v] = mergedlist.get(v, []) + [numbers[i]]
print(mergedlist["Berta Urban"]) # prints [8, 9]
Not terribly efficient, tho. In dependence of the datatbase you're using, chances are that the database can get you the results in the form you prefer faster than you post-processing and reconstructing the data.

write a dictionary of different-length lists to a csv file

I am trying to write a dictionary of lists to a csv file. The solution in:Write dictionary of lists to a CSV file
will trim longer lists. For example if I want to write the following:
d = {"key1": [1, 2, 3], "key2": [4, 5, 6], "key3": [7, 8, 9, 11]}
with open("test.csv", "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow(d.keys())
writer.writerows(zip(*d.values()))
The results is
key3 key2 key1
7 4 1
8 5 2
9 6 3
11 is deleted from key3. Any ideas?
The quick and easy answer is to use itertools.izip_longest instead of zip
import itertools
import csv
d = {"key1": [1, 2, 3], "key2": [4, 5, 6], "key3": [7, 8, 9, 11]}
with open("test.csv", "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow(d.keys())
writer.writerows(itertools.izip_longest(*d.values()))