Django Postgresql JsonField query related dictionary keys - python

A part of the model that I have, which uses Django Model field, is like the following:
class SalesModel(models.Model):
some_data = models.PositiveIntegerField(db_index=True)
some_other_data = models.CharField(max_length=50)
json_data = JSONField(blank=True, null=True)
Now following is the format of the JsonData field:
[{"id": val, "contribution": "some_val", }, {"id": some_val, "contribution": "some_other_val",}, {"id": other_val, "contribution": "some_another_val"}]
i.e., the format is:
[{'id':XX, 'contribution':XX},{'id':YY, 'contribution':YY},{'id':ZZ, 'contribution':ZZ}]
Currently I can filter the Django table with the val of ID. I would now, like to know the contribution of that particular ID.
For eg, if val = 1, I would like to filter the model SalesModel which has JsonField with id = 1, and I want to show the related contribution. So, that would mean, out of the 3 possible dictionaries (as per the field construction), I would only show one dictionary (filtered by the 'ID' key of that dictionary). That would mean, if the 2nd dictionary has a matching ID, show only the 2nd contribution, if the 1st ID is matching show only the 1st contribution, and similarly for the 3rd dictionary.
Is there a way that can be done?

You could restructure your JSONField differently, by giving it a dict where the key, value pairs are id: contribution directly. This way you could use the has_key filter and KeyTransform will work, as I'm not sure it works on an array of dicts. So assuming your json_data looks like this:
{1: 'xx', 3: 'yy', 9: 'zz'}
you could query this way, based on #vanojx1 contribution:
SalesModel.filter(json_data__has_key=id)\
.annotate(contrib=KeyTransform(id, 'json_data')\
.values('contrib')
Alternatively, using raw jsonb in postgresql:
SalesModel.filter(json_data__has_key=id)\
.extra(select={'contrib': "json_data->{0}".format(id)})\
.values('contrib')

This should work DOC
SalesModel.objects.filter(json_data__id=1).values('id', 'json_data__contribution')

Yes, I guess. If I have understood it right, you will have an id to be matched or a list of ID's. So if your ID is 2:
my_id = 2
dict1 = [{"id":1, "contribution":10},{"id":2, "contribution":20},{"id":3, "contribution":30}]
for i in dict1:
if i["id"] == my_id:
print(i["contribution"])

Related

How to map dictionary keys to model fields in PeeWee with dict_to_model

How can I use dict_to_model() to populate (insert data into) a table from dictionary values without changing the model attributes? I have a model, Video, with attributes:
class Video(model):
id = IntegerField()
camera = CharField()
channel = IntegerField()
filename = CharField()
I also have a dictionary of data:
data = {'video_id': 1234, 'camera_name': "canon", 'channel_id': 5, 'video_name' = "intro.mp4"}
Each key:value pair in the dict corresponds to a column and its data. From what I understand, I can use the dict_to_model function from playhouse.shortcuts to map the dict keys to table columns and act on the values inside the dict. How can I do this without changing the names of my class attributes? The only way I could get this to work was by changing Video.id to Video.video_id so it matches the dictionary and so on. Even then, a .save() statement does not push the data to a table. If I do not change my model attributes, I get:
AttributeError: Unrecognized attribute "video_id" for model class <Model: Video>
If I change my attributes to match the dictionary, it accepts the mapping but will not send the data to the table.
Dict and model fields should match, so you have to rename either dict fields or model fields.
Also take a look at "Storing Data" section in peewee quickstart
uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15))
uncle_bob.save() # bob is now stored in the database
Probably after converting dict to model you need to call .save() method

How can I enrich in viur relations with further data fields

How can I enrich in viur relations with further data fields.
With SQL Alchemy I can do this with Association Objects.
Is this also possible in viur?
I have tried the following:
skeleton.relation.setBoneValue(skeleton, "relation", {"key":keyObj,"afield":"avalue}, True)
But this does not work.
the third parameter of the setBoneValue function must be a tuple containing the keyObj as the first value and a RelSkel as the second value.
So the correct way looks like this:
class exampleRelSkel(RelSkel):
afield= stringBone(descr="a Field Description")
myRelSkel = exampleRelSkel()
myRelSkel["afield"] = "avalue" #set the value
skeleton.relation.setBoneValue(skeleton, "relation",(keyObj,myRelSkel),True)

Django: queryset "loosing" a value

I have 2 models, one with a list of clients and the other with a list of sales.
My intention is to add sales rank value to the clients queryset.
all_clients = Contactos.objects.values("id", "Vendedor", "codigo", 'Nombre', "NombrePcia", "Localidad", "FechaUltVenta")
sales = Ventas.objects.all()
Once loaded I aggregate all the sales per client summing the subtotal values of their sales and then order the result by their total sales.
sales_client = sales.values('cliente').annotate(
fact_total=Sum('subtotal'))
client_rank = sales_client .order_by('-fact_total')
Then I set the rank of those clients and store the value in a the "Rank" values in the same client_rank queryset.
a = 0
for rank in client_rank:
a = a + 1
rank['Rank'] = a
Everything fine up to now. When I print the results in the template I get the expected values in the "client_rank" queryset: "client name" + "total sales per client" + "Rank".
{'cliente': '684 DROGUERIA SUR', 'fact_total': Decimal('846470'), 'Rank': 1}
{'cliente': '699 KINE ESTETIC', 'fact_total': Decimal('418160'), 'Rank': 2}
etc....
The problem starts here
First we should take into account that not all the clients in the "all_clients" queryset have actual sales in the "sales" queryset. So I must find which ones do have sales, assign them the "Rank" value and a assign a standard value for the ones who don´t.
for subject in all_clients:
subject_code = str(client["codigo"])
try:
selected_subject = ranking_clientes.get(cliente__icontains=subject_code)
subject ['rank'] = selected_subject['Rank']
except:
subject ['rank'] = "Some value"
The Try always fails because "selected_subject" doesn´t seems to hace the "Rank" value. If I print the "selected_subject" I get the following:
{'cliente': '904 BAHIA BLANCA BASKET', 'fact_total': Decimal('33890')}
Any clues on why I´, lossing the "Rank" value? The original "client_rank" queryset still has that value included.
Thanks!
I presume that ranking_clientes is the same as client_rank.
The problem is that .get will always do a new query against the database. This means that any modifications you made to the dictionaries returned in the original query will not have been applied to the result of the get call.
You would need to iterate through your query to find the one you need:
selected_subject = next(client for client in ranking_clientes if subject_code in client.cliente)
Note, this is pretty inefficient if you have a lot of clients. I would rethink your model structure. Alternatively, you could look into using a database function to return the rank directly as part of the original query.

getting a list after grouping in sqlalchemy

I have a table Food. It has fields: cust_name, phone_number , order_date
I am trying to build a dictionary where a key of pair (cust_name, phone_number) gives a list of order_date. For that I need to query appropriately in sqlalchemy. I'm using Postgres.
So far I have:
db.session.query(Food.cust_name, Food.phone_number).group_by(Food.cust_name, Food.phone_number).all()
What do I need to change so that I get a corresponding list of order_date
Use the array_agg() aggregate function to produce a list of order dates:
res = db.session.query(Food.cust_name,
Food.phone_number,
db.func.array_agg(Food.order_date).label('order_dates')).\
group_by(Food.cust_name, Food.phone_number).\
all()
the_dict = {(r.cust_name, r.phone_number): r.order_dates for r in res}

Django - Query models where id equals attribute on python list

I need to query all models where the ID matches the 'id' attribute of JSON array, something like that:
I have 3 saved model objects with respective ID's:
ID 1
ID 3
ID 4
I have a JSON array like that:
[{'id' : 1}, {'id' : 2}, {'id' : 5}]
I want to filter in that way:
model.objects.filter('Objects by ID that is not listed in the JSON array')
The result of the filter should be a list with models objects that the ID is not in the JSON:
result = [model_pk=3, model_pk=4]
Any ideas?
You can use exclude method to achieve that:
ids = [i['id'] for i in json_array]
qs = model.objects.exclude(id__in=ids)

Categories