Django check if querysets are equals - django

I have this django code
q1 = MyModel.objects.all()
q2 = MyModel.objects.all()
When I try:
print(q1 == q2)
I get as a result:
False
So how can I check if two querysets results in django are equal?

You can convert the querysets to lists and check whether they are equal:
list(q1) == list(q2)

You can convert it to set, to check if 2 query sets have the same elements, without regard to ordering:
set(q1) == set(q2)
it will return:
True

Try this:
q1.intersection(q2).count() == q1.count() and q1.count() == q2.count()

Throwing in my two cents for a function that compares two QuerySets for equality while ignoring sort order. Note I'm not actually checking whether the QuerySets are empty or not; I'll leave that up to you.
def querysets_are_same(qs1, qs2, exclude_fields=[]):
'''
Check whether two queryset have the same content, sort order of querysets is ignored.
Params:
-------
qs1 (QuerySet) - first queryset to compare
qs2 (QuerySet) - second queryset to compare
exclude_fields (list) - fields to exclude from comparison; primary key field is automatically removed.
Yield:
------
True if both querysets contain the same data while ignoring sort order; False otherwise
'''
# lookup primary key field name
pk_qs1 = qs1[0]._meta.pk.name
pk_qs2 = qs2[0]._meta.pk.name
# update excluded fields
exclude_fields_qs1 = set(exclude_fields) | set([pk_qs1])
exclude_fields_qs2 = set(exclude_fields) | set([pk_qs2])
# convert queryset to list of dicts excluding fields
list_qs1 = [{k:v for k,v in d.items() if not k in exclude_fields_qs1} for d in qs1.values()]
list_qs2 = [{k:v for k,v in d.items() if not k in exclude_fields_qs2} for d in qs2.values()]
# sort lists
list_qs1_sorted = sorted(sorted(d.items()) for d in list_qs1)
list_qs2_sorted = sorted(sorted(d.items()) for d in list_qs2)
return list_qs1_sorted == list_qs2_sorted

You can see by .count() or:
q1 = Model.objects.all()
q2 = Model.objects.all()
equal = True
for idx, q in q1:
if q != q2[idx]:
equal = False
print(equal)

Related

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.

convert queryset into pure string only

I have some code that checks if two string matches and they do in the database, but when I query them using this code
r = Usertasks.objects.all().filter(user=request.user).filter(randomURL=payment_id).values_list("TaskPostedToNetwork", flat=True)
e = Usertasks.objects.all().filter(user=request.user).filter(randomURL=payment_id).values_list("PaymentConfirmed", flat=True)
It returns
<QuerySet [False]>
<QuerySet ['yes']>
My if loop that checks for the string is never ran because it doesnt return as a string but with the queryset around it, I guess
if r == "False" and e == "yes":
print("Works")
How do I make it return just
False
yes
I'm assuming your query is intended to return just a single value. ie: user and randomURL are unique together. In that case:
task = Usertasks.objects.get(user=request.user, randomURL=payment_id)
if not task.TaskPostedToNetwork and task.PaymentConfirmed == 'yes':
print("Works")

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..

django - remove field name and brackets

I have the following queryset -
pk = Jobmst.objects.db_manager('database1').extra(where=['jobmst_alias=%s'], params=[alias]).values('jobmst_id')
Which returns the following -
[{'jobmst_id': 21195}]
edit -
pk = Jobmst.objects.db_manager('database1').extra(where=['jobmst_alias=%s'], params=[alias]).values_list('jobmst_id', flat=True)
Returns the following -
[21195]
Close but I don't want the brackets I want only the integer.
I want it to simply give me the integer value so I can call it in another query -
mst = Jobmst.objects.db_manager('database1').raw("""
SELECT jobmst_id, jobmst_type, jobmst_prntname AS jobmst_prntid, jobmst_active,
evntmst_id, jobmst_evntoffset, jobmst_name, jobmst_mode, jobmst_owner, jobmst_desc,
jobmst_crttm, jobdtl_id, jobmst_lstchgtm, jobmst_runbook, jobcls_id, jobmst_prntname,
jobmst_alias, jobmst_dirty FROM Jobmst WHERE jobmst_id = %s""", [pk])
Which would ideally parse as this -
mst = Jobmst.objects.db_manager('database1').raw("""
SELECT jobmst_id, jobmst_type, jobmst_prntname AS jobmst_prntid, jobmst_active,
evntmst_id, jobmst_evntoffset, jobmst_name, jobmst_mode, jobmst_owner, jobmst_desc,
jobmst_crttm, jobdtl_id, jobmst_lstchgtm, jobmst_runbook, jobcls_id, jobmst_prntname,
jobmst_alias, jobmst_dirty FROM Jobmst WHERE jobmst_id = 21195""")
values returns list of dicts, so you can access value by list index and key name:
try:
pk = Jobmst.objects.db_manager('database1').extra(where=['jobmst_alias=%s'],
params=[alias]).values('jobmst_id')[0]['jobmst_id']
except IndexError:
pk = None
You can use values_list that returns only list of specified fields values and to access the value you need only index:
try:
pk = Jobmst.objects.db_manager('database1').extra(where=['jobmst_alias=%s'],
params=[alias]).values_list('jobmst_id', flat=True)[0]
except IndexError:
pk = None

Getting next and previous objects in Django

I'm trying to get the next and previous objects of a comic book issue. Simply changing the id number or filtering through date added is not going to work because I don't add the issues sequentially.
This is how my views are setup and it WORKS for prev_issue and does return the previous object, but it returns the last object for next_issue and I do not know why.
def issue(request, issue_id):
issue = get_object_or_404(Issue, pk=issue_id)
title = Title.objects.filter(issue=issue)
prev_issue = Issue.objects.filter(title=title).filter(number__lt=issue.number)[0:1]
next_issue = Issue.objects.filter(title=title).filter(number__gt=issue.number)[0:1]
Add an order_by clause to ensure it orders by number.
next_issue = Issue.objects.filter(title=title, number__gt=issue.number).order_by('number').first()
I know this is a bit late, but for anyone else, django does have a nicer way to do this, see https://docs.djangoproject.com/en/1.7/ref/models/instances/#django.db.models.Model.get_previous_by_FOO
So the answer here would be something something like
next_issue = Issue.get_next_by_number(issue, title=title)
Django managers to do that with a bit of meta class cleaverness.
If it's required to find next and previous objects ordered by field values that can be equal and those fields are not of Date* type, the query gets slightly complex, because:
ordering on objects with same values limiting by [:1] will always produce same result for several objects;
object can itself be included in resulting set.
Here's are querysets that also take into account the primary keys to produce a correct result (assuming that number parameter from OP is not unique and omitting the title parameter as it's irrelevant for the example):
Previous:
prev_issue = (Issue.objects
.filter(number__lte=issue.number, id__lt=instance.id)
.exclude(id=issue.id)
.order_by('-number', '-id')
.first())
Next:
next_issue = (Issue.objects
.filter(number__gte=issue.number, id__gt=instance.id)
.exclude(id=issue.id)
.order_by('number', 'id')
.first())
from functools import partial, reduce
from django.db import models
def next_or_prev_instance(instance, qs=None, prev=False, loop=False):
if not qs:
qs = instance.__class__.objects.all()
if prev:
qs = qs.reverse()
lookup = 'lt'
else:
lookup = 'gt'
q_list = []
prev_fields = []
if qs.query.extra_order_by:
ordering = qs.query.extra_order_by
elif qs.query.order_by:
ordering = qs.query.order_by
elif qs.query.get_meta().ordering:
ordering = qs.query.get_meta().ordering
else:
ordering = []
ordering = list(ordering)
if 'pk' not in ordering and '-pk' not in ordering:
ordering.append('pk')
qs = qs.order_by(*ordering)
for field in ordering:
if field[0] == '-':
this_lookup = (lookup == 'gt' and 'lt' or 'gt')
field = field[1:]
else:
this_lookup = lookup
q_kwargs = dict([(f, get_model_attr(instance, f))
for f in prev_fields])
key = "%s__%s" % (field, this_lookup)
q_kwargs[key] = get_model_attr(instance, field)
q_list.append(models.Q(**q_kwargs))
prev_fields.append(field)
try:
return qs.filter(reduce(models.Q.__or__, q_list))[0]
except IndexError:
length = qs.count()
if loop and length > 1:
return qs[0]
return None
next_instance = partial(next_or_prev_instance, prev=False)
prev_instance = partial(next_or_prev_instance, prev=True)
note that do not use object.get(pk=object.pk + 1) these sorts of things, IntegrityError occurs if object at that pk is deleted, hence always use a query set
for visitors:
''' Useage '''
"""
# Declare our item
store = Store.objects.get(pk=pk)
# Define our models
stores = Store.objects.all()
# Ask for the next item
new_store = get_next_or_prev(stores, store, 'next')
# If there is a next item
if new_store:
# Replace our item with the next one
store = new_store
"""
''' Function '''
def get_next_or_prev(models, item, direction):
'''
Returns the next or previous item of
a query-set for 'item'.
'models' is a query-set containing all
items of which 'item' is a part of.
direction is 'next' or 'prev'
'''
getit = False
if direction == 'prev':
models = models.reverse()
for m in models:
if getit:
return m
if item == m:
getit = True
if getit:
# This would happen when the last
# item made getit True
return models[0]
return False
original author
Usage
# you MUST call order by to pass in an order, otherwise QuerySet.reverse will not work
qs = Model.objects.all().order_by('pk')
q = qs[0]
prev = get_next_or_prev(qs, q, 'prev')
next = get_next_or_prev(qs, q, 'next')
next_obj_id = int(current_obj_id) + 1
next_obj = Model.objects.filter(id=next_obj_id).first()
prev_obj_id= int(current_obj_id) - 1
prev_obj = Model.objects.filter(id=prev_obj_id).first()
#You have nothing to loose here... This works for me