Django/Postgres: Choose from DISTINCT ON sets - django

I have these models:
class Product(Model):
...
class Scanning(Model):
product = ForeignKey(..)
datetime = DateTimeField(...)
...
I'm trying to get one scanning for each product where the scanning is a latest one from product.scanning.all() set.
s1 = (product1,01.01.1000)
s2 = (product2,01.01.1200)
s3 = (product1,01.01.1900)
s4 = (product2,01.01.1988)
s5 = (product3,01.01.2015)
s6 = (product3,01.01.1970)
would return <s4,s3,s5>
Scanning.objects.filter(product__user=u,product__active=True).distinct('product_id').order_by('datetime')
Raises exception:
ProgrammingError: SELECT DISTINCT ON expressions must match initial
ORDER BY expressions LINE 1: SELECT DISTINCT ON
("productapp_scanning"."product_id") "pro...
^
How to make it work?

With postgres, you cannot do distinct on a field, unless it's also what you sort by:
Scanning.objects.filter(product__user=u,product__active=True).distinct('product_id').order_by('product_id', 'datetime')
If that's not good enough, one solution is to make a double query like this:
q1 = Scanning.objects.filter(product__user=u,product__active=True).values('product_id').distinct().annotate(x=Max('id'))
q2 = Scanning.objects.filter(id__in=[i["x"] for i in q1])

Related

Compare two different classes based on their values

I'm trying to compare two variables based on their actual values but it's just not working and I think it's because they are from different classes.
Here's an example :
models = Model_info.objects.all()
m = 'X-POWER 3'
for model in models:
if m == model:
check = 'accepted'
print(check)
break
else:
check = 'rejected'
print(check)
print(f'final resulte is {check}')
So Knowing that the value of variable m does exist in the queryset models and :
type(m) = < class 'str' >
type(model) = < class 'application.models.Model_info' >
is there any method to compare the value of two variables no matter what class they belong to.
You cannot make a comparison with a queryset, you need to do this with a field name of your choosing.
Here's an example:
Assuming ModelInfo had a field like name, all you have to do is
compare the string m with the model instance's field(assuming the field name is also a string), i.e:
m = 'X-POWER 3'
for model in models:
if m == model.name:
check = 'accepted'
... # continuation of your code

Get several unique fields values in django

I have a model:
class MyModel(models.Model):
a = models.CharField(...)
b = models.CharField(...)
And some substring "substring"
To get all unique values from fields a and b which contains those substring I can do that:
a_values = MyModel.objects.filter(a__icontains=substring).values_list("a", flat=True).distinct()
b_values = MyModel.objects.filter(b__icontains=substring).values_list("b", flat=True).distinct()
unique_values = list(set([*a_values, *b_values]))
Is it possible to rewrite it with one database request?
PS to clarify
from objects:
MyModel(a="x", b="y")
MyModel(a="xx", b="xxx")
MyModel(a="z", b="xxxx")
by substring "x" I expect to get:
unique_values = ["x", "xx", "xxx", "xxxx"]
You can here make a union of two queries that are never evaluated individually, like:
qa = MyModel.objects.filter(a__icontains=query).values_list('a', flat=True)
qb = MyModel.objects.filter(b__icontains=query).values_list('b', flat=True)
result = list(qa.union(qb))
Since qa and qb are lazy querysets, we never evaluate these. The query that will be performed is:
(
SELECT mymodel.a
FROM mymodel
WHERE mymodel.a LIKE %query%
)
UNION
(
SELECT mymodel.b
FROM mymodel
WHERE mymodel.b
LIKE %query%
)
The union furthermore will only select distinct values. Even if a value occurs both in a and in b, it will only be retrieved once.
For example:
>>> MyModel(a="x", b="y").save()
>>> MyModel(a="xx", b="xxx").save()
>>> MyModel(a="z", b="xxxx").save()
>>> query = 'x'
>>> qa = MyModel.objects.filter(a__icontains=query).values_list('a', flat=True)
>>> qb = MyModel.objects.filter(b__icontains=query).values_list('b', flat=True)
>>> list(qa.union(qb))
['x', 'xx', 'xxx', 'xxxx']
why not get both of them in the values and then flatten the list ?
unique_values = MyModel.objects.filter(
Q(a__icontains=substring) | Q(b__icontains=substring)
).values_list("a", "b").distinct()
unique_values = sum(unqiue_values, [])
if sum doesnt work here, then you can probably use flatten from itertools. Sum won't probably work because we getting a list of tuples instead of list of lists. Sorry i couldn't test this right now.

Join with more than one, specific one-to-many rows

I have to join one row from table Type1 with two rows from table Type2. There are example models of database:
class Type1(models.Model):
name = models.CharField(max_length=300)
class Type2(models.Model):
foreign = models.ForeignKey(Type1)
name = models.CharField(max_length=50)
value = models.IntegerField()
In SQL, query looks like this:
SELECT A.name, B.value, C.value
FROM Type1 A, Type2 B, Type2 C
WHERE B.foreign = C.foreign = A.id
AND B.name = _VAR1_ AND C.name = _VAR2_
How to do that query using Django methods (not raw SQL!)?
Not sure if I understand your question correctly, and know very little SQL to understand the SQL code.
Interpretation 1 is you want a reverse relationship query and a Q object for the OR part. See 'lookups that span relationship' and 'Complex lookups with Q objects' in this page.
Ex.
Type1.objects.filter(Q(type2__name = '_VAR1_' | type2__name = '_VAR2_'))
This return all rows of model Type1 that has an associated Type2 row with value VAR1 or VAR2 for name.
Interpretation 2: you want to define the relationship.
Well, that's easier. You have a Type1 row and want to associate two other rows with this one.
Query the Type1 row and save it to a variable.
t1 = Type1.object.get(id = X)
Then create type2 objects using t1 object in the foreign key:
t2 = Type2(foreign = t1, name = 'whatever', value = 'value')
t2.save()
and the same for the other object
t3 = Type2(foreign = t1, name = 'whatever2', value = 'value2')
t3.save()
Let me know if I misinterpreted the question.
Cheers.

cleaning up my SQLAlchemy operations (reducing repetition)

I have some server side processing of some data (client-side library = jQuery DataTables)
I am using POST as my ajax method. In my Flask webapp, I can access the POST data with request.values
The data type / structure of request.values is werkzeug.datastructures.CombinedMultiDict
If the user wants to sort a column, the request contains a key called action with a value of filter (note the below printouts are obtained with for v in request.values: print v, request.values[v])
...
columns[7][data] role
columns[8][search][regex] false
action filter
columns[10][name]
columns[3][search][value]
...
all the column names are also contained in the request as keys. The columns that have search terms will have the search string as a value for the column name key (as opposed to empty for columns with no search term entered. So, If I want to search for firstname containing bill, I would see the following in my request
columns[7][searchable] true
...
columns[6][name]
firstname bill
columns[0][search][value]
columns[2][searchable] true
...
columns[5][data] phone
role
columns[10][data] registered_on
...
columns[0][searchable] true
email
columns[7][orderable] true
...
columns[2][search][value]
Notice how role and email are empty. So my code below is very non-DRY
rv = request.values
if rv.get('action') == 'filter':
if len(rv.get('firstname')):
q = q.filter(User.firstname.ilike('%{0}%'.format(rv.get('firstname'))))
if len(rv.get('lastname')):
q = q.filter(User.lastname.ilike('%{0}%'.format(rv.get('lastname'))))
if len(rv.get('username')):
q = q.filter(User.username.ilike('%{0}%'.format(rv.get('username'))))
if len(rv.get('email')):
q = q.filter(User.email.ilike('%{0}%'.format(rv.get('email'))))
if len(rv.get('phone')):
q = q.filter(User.phone.ilike('%{0}%'.format(rv.get('phone'))))
if len(rv.get('region')):
q = q.filter(User.region.name.ilike('%{0}%'.format(rv.get('region'))))
if len(rv.get('role')):
q = q.filter(User.role.name.ilike('%{0}%'.format(rv.get('role'))))
if len(rv.get('is_active')):
q = q.filter(User.is_active_ == '{0}'.format(rv.get('is_active')))
if len(rv.get('is_confirmed')):
q = q.filter(User.is_confirmed == '{0}'.format(rv.get('is_confirmed')))
if len(rv.get('registered_on_from')):
fdate = datetime.strptime(rv.get('registered_on_from'), '%Y-%m-%d')
q = q.filter(User.registered_on > fdate)
if len(rv.get('registered_on_to')):
tdate = datetime.strptime(rv.get('registered_on_to'), '%Y-%m-%d')
q = q.filter(User.registered_on < tdate)
I was building the sorting functionality, and I found the following statement that greatly simplified my life (see this answer)
q = q.order_by('{name} {dir}'.format(name=sort_col_name, dir=sort_dir))
I was wondering if there was a way to simplify this set of filtering queries like the above sorting code since I will have to do this for many other models.
This should help:
from sqlalchemy import inspect
from sqlalchemy.sql.sqltypes import String,Boolean
def filter_model_by_request(qry,model,rv):
if rv.get('action') == 'filter':
mapper = inspect(model).attrs # model mapper
col_names = list(set([c.key for c in mapper]) & set(rv.keys()))
# col_names is a list generated by intersecting the request values and model column names
for col_name in col_names:
col = mapper[col_name].columns[0]
col_type = type(col.type)
if col_type == String: # filter for String
qry = qry.filter(col.ilike('%{0}%'.format(rv.get(col_name))))
elif col_type == Boolean: # filter for Boolean
qry = qry.filter(col == '{0}'.format(rv.get(col_name)))
return qry
Example call (I used it with a #app.before_request and a cURL call to verify):
qry = db.session.query(User)
print filter_model_by_request(qry,User,request.values).count()
The date range filtering is not included in the function, add this feature if you wish, your code is fine for that purpose.
side note: be careful with the bigger/smaller operators for the dates. You're excluding the actual requested dates. Use <= or >= to include dates in filtering action. It's always a pitfall for me..

What is the difference between these two Django Q-based queries:

Assuming a simple model like this:
class Item(models.Model):
name = models.CharField(max_length=10)
class Relation(models.Model):
item = models.ForeignKey(Item)
weight = models.IntegerField()
And a couple of Q objects like this:
some = Q(relation__x__gt=3)
others = Q(relation__x=7)
What is the semantic difference between:
first = Item.objects.filter(some, ~others)
and
second = Item.objects.filter(some).exclude(others)
NOTE: Querying through the relation appears to be different than querying a single simple object. The SQL generated for the two above queries is different.
Here's the generated SQL for first:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
WHERE ("item_relation"."weight" > 3
AND NOT ("item_item"."id" IN
(SELECT U1."item_id"
FROM "item_relation" U1
WHERE (U1."weight" = 7 AND U1."item_id" IS NOT NULL))))
And the SQL for second:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
INNER JOIN "item_relation" T3
ON ("item_item"."id" = T3."item_id")
WHERE ("item_relation"."weight" > 3 AND NOT (T3."weight" = 7 ))