django - remove field name and brackets - django

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

Related

How to filter by range OR "null" value? (I.e. combine NumberFilter range and BooleanFilter for null=True IntegerField)

I have a Item model with a numeric number field. This number field defaults to null.
# models.py
class Item(models.Model):
number = models.IntegerField(default=None, blank=True, null=True)
I want to set-up filters that can return a queryset of Items where number is in range - which is straightforward enough:
# filters.py
class ItemFilter(django_filters.FilterSet):
min_num = django_filters.NumberFilter(method="min_num_filter")
max_num = django_filters.NumberFilter(method="max_num_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
def min_num_filter(self, queryset, name, value):
return queryset.filter(number__gte=value)
def max_num_filter(self, queryset, name, value):
return queryset.filter(number__lte=value)
But what if I want to have an additional Boolean filter that can include Items that has null for number along with whatever Items matches the min_num and max_num range?
So for example, a URL query in the form of ?min_num=1&max_num=10&incl_null=True should return all Items where number is between 1 and 10 OR number is equal to None.
The following code does not work:
class ItemFilter(django_filters.FilterSet):
...
incl_null = django_filters.BooleanFilter(method="incl_null_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
// doesn't work
class incl_null_filter(self, queryset, name, value):
if value is True:
return queryset | Item.objects.filter(number=None)
if value is False:
return queryset
Edit: I've tried the methods in the "Filtering by empty values" documentation but I think that's for null values exclusively - where I'm looking for a range match OR a null value.
Try this query:
from django.db.models import Q
min_ = 0
max_ = 10
Item.objects.filter(Q(number__gte=min_, number__lte=max_) | Q(number__isnull=True))
Well, the only solution I can think of is to pass the min range, max range, and is_null boolean into a single char field then convert it into the 3 individual filters for actioning.
So the query URL will look like ?master_num=1-10-1 for range 1 - 10 incl. None and ?master_num=1-10-0 for range 1 - 10 excl. None.
class ItemFilter(django_filters.FilterSet):
master_num = django_filters.CharFilter(method="master_num_filter")
class Meta:
model = Item
fields = ("master_num")
def master_num_filter(self, queryset, name, value):
# array = [min, max, 1 or 0 for True and False]
array = value.split("-")
min = Q(year_published__gte=int(array[0]))
max = Q(year_published__lte=int(array[1]))
if array[2] == "1":
incl_null = Q(year_published=None)
return queryset.filter((min & max) | incl_null)
else:
return queryset.filter(min & max)
Would like to know if there's a better way to do this.

Accessing Python List

Hi I have this list which is working for what initially intended for
gp_list_t = [int(i) for i in gp_list]
gp_lits3 = gp_list_t[0]
agencys_sp = GPSpecial.objects.filter(agencys=user_agency,is_active=True,id =gp_lits3).values_list('agencys')
Now say they are more than 1 values on the list -gp_list_t. How do I access other values on the list that i want to use on my variable agencys_sp
Updated Code :
gp_list_t = [int(i) for i in gp_list]
agencys_sp=GPSpecial.objects.filter(agencys=user_agency,is_active=True,id=gp_list_t).values_list('agencys')
for value, in agencys_sp:
agency_tu = [i for i, in agencys_sp]
returns error int() argument must be a string, a bytes-like object or a number, not 'list'
You can use the in filter operator to query all rows matching the IDs in the gp_list_t. Then iterate over the queryset with a for loop:
gp_list_t = [int(i) for i in gp_list]
agencys_sp = GPSpecial.objects.filter(agencys=user_agency,is_active=True,id__in=gp_list_t).values_list('agencys')
for value, in agencys_sp:
print(value)

Looking for a best way to insert a records from one model to another based on selection in odoo

I did the code for insert records from so_parts table to so_bo table using Query...How can I use ORM method to do this kind of job. Is there any other way(best)to do that? Here is my code`
`
#api.multi
def save_rapair_parts(self, vals):
#get todays date and convert it to string
created_date = datetime.datetime.today().strftime("%m/%d/%Y")
str_date = str(created_date)
so_p_id = self.so_p_id.id
bo_status = self.bo_status
so_part_t = self.so_part_t
so_part_sno = self.so_part_sno
product = self.so_part_product
product_str = 'Repair '+str(product)
part_id = self.id
bench_order_table.search(['id','bo_sno','created_date','bo_number','rep_description','bo_status'])
#insert details intoso bench orders
`
if so_part_t=='r_b':
try:
sequence = self.env['ir.sequence'].next_by_code('so.benchorder') or '/'
str_sequence = str(sequence)
query = """SELECT so_work_authorization FROM my_depots_so WHERE id=%d """ % (so_p_id)
self.env.cr.execute(query)
result = self.env.cr.fetchall()
result_number = json.dumps(result, ensure_ascii=False)
strip_number = result_number.strip('\' \" [] ')
work_auth_no = str(strip_number)
work_auth_no += "-"
work_auth_no += str_sequence
insert ="""INSERT INTO my_depots_so_bo(id,so_bo_id,bo_sno,created_date,bo_number,rep_description,bo_status) values %s """
parameters = (part_id,so_p_id,so_part_sno,str_date,work_auth_no,product_str,bo_status)
self.env.cr.execute(insert,(parameters,))
my_depots_bo(id,bo_sno,created_date,bo_number,rep_description,bo_status) values %s """
# self.env.cr.execute(insert_query, (parameters,))
except Exception:
print "Error in inserting values"`
yes there is a better way because when you use ORM
method you also checks access right for user to:
for your select query:
rec = self.env['my.depots.so'].search_read(['id', '=', so_p_id], ['so_work_authorization'])
if rec:
rec = rec[0] # search_read return a list of dictionary
so_work_authorization = rec['so_work_authorization']
# and do what ever you want with the result
# to create
# call create method witch accept a dictionary
# field_name : value
new_rec = self.env['my.depots.so.bo'].create({
'so_bo_id': so_p_id, # many2one must be an integer value
'bo_sno': bo_nso_value,
'bo_number': value_of_number,
# ....
# ....
# add al field
}) # create return the new created record as model object
for inserting use: self.env['model.name'].create(vals)
for updating use : self.env['model.name'].write(vals)
using ORM method makes sure that user don't pass the security access rigths
Hope you get the idea

Django check if querysets are equals

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)

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