Django: How to take my query out of my for loop - django

I got the following code, that contains N queries:
for qty in total_qty_bought:
product_id = qty["product"]
quantity = int(qty["quantity__sum"])
try:
method_title = (
self.shipment_model.get(order_id=qty["order_id"])
.method_title.replace("Hent-selv", "")
.strip()
)
To solve the issue I tried to take the method_title query out of the for loop like this:
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id'])
.method_title.replace("Hent-selv", "")
.strip()
)
Note! There will be a full refrence further down, to understand the bigger picture
The issue in my solution is, that I am hard choosing which dict to enter , as I select [0] before order_id, and not in a for loop like before, would be selecting every individual item in the loop.
Is there a more sufficient way to do this? I do not see a solution without the for loop, but django debugger tool tells me it creates 2k+ queries.
CODE FOR REFRENCE
class InventoryStatusView(LoginRequiredMixin, View):
template_name = "lager-status.html"
cinnamon_form = CinnamonForm(prefix="cinnamon_form")
peber_form = PeberForm(prefix="peber_form")
pc_model = InventoryStatus
product_model = Product.objects.all()
order_item_model = WCOrderItem.objects.all()
shipment_model = WCOrderShipment.objects.all()
def get(self, request):
# Get all added objects that hasn't been deleted
objects = self.pc_model.objects.filter(is_deleted=False)
# Get all added objects that has been deleted
deleted_objects = self.pc_model.objects.filter(is_deleted=True)
# Sum all cinnamon that isn't deleted
total_cinnamon = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("cinnamon"))
.get("cinnamon__sum", 0.00)
)
# Sum all peber that isn't deleted
total_peber = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("peber"))
.get("peber__sum", 0.00)
)
# Get the amount of kilo attached to products
product_data = {}
queryset = ProductSpy.objects.select_related('product')
for productSpy in queryset:
product_data[productSpy.product.product_id] = productSpy.kilo
# Get quantity bought of each product
total_qty_bought = self.order_item_model.values(
"order_id", "product"
).annotate(Sum("quantity"))
# Get the cities from the inventory model
cities = dict(self.pc_model.CITIES)
# Set our total dict for later reference
our_total = {}
product = Product.objects.filter(
product_id__in={qty['product'] for qty in total_qty_bought}
).first()
# Check if we deal with kanel or peber as a product based on slug
index = 0
if product.slug.startswith("kanel-"):
index = 0
elif product.slug.startswith("peber-"):
index = 1
else:
pass
try:
# Sum the total quantity bought
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
# Get the inventory the order is picked from based on shipment method title
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id']) # The error
.method_title.replace("Hent-selv", "")
.strip()
)
# If the order isn't picked, but sent, use this inventory
if method_title not in cities.values():
method_title = "Hovedlager"
try:
# Get the total of kanel and peber bought
kilos = quantity * product_data[product.id]
# If there is no inventory, set it to 0 peber and 0 kanel
if method_title not in our_total:
our_total[method_title] = [0, 0]
# Combine place and kilos
our_total[method_title][index] += kilos
except KeyError as ex:
print(ex)
pass
except WCOrderShipment.DoesNotExist as ed:
print(ed)
pass
# Quantities BOUGHT! (in orders!)
print(our_total)
context = {
"cinnamon_form": self.cinnamon_form,
"peber_form": self.peber_form,
"objects": objects,
"deleted_objects": deleted_objects,
"total_cinnamon": total_cinnamon,
"total_peber": total_peber,
"our_total": our_total,
}
return render(request, self.template_name, context)

You can only do one query by using the __in operator:
shipments = self.shipment_model.get(order_id__in=list_containing_order_ids)
Then you can do a normal for loop in which you verify that condition.

Related

Django queryset for current year

I have my query written to get some thing from database and display on my website that query is getting all the data from db but what if i want to get the data particular to current year only
def get(self, request, *args, **kwargs):
filters = self.get_filters()
result = Model.objects.all().filter(*filters).distinct().aggregate(
t=Sum('t'),
b=Sum('b'),
)
result2 = Model.objects.all().filter(*filters).distinct().aggregate(
past_due=Sum('balance', filter=Q(due_date__lt=timezone.now()))
)
zero = Decimal('0.00')
total = result['t'] or zero
balance = result['b'] or zero
past_due = result2['past_due'] or zero
amount_received = t - b
I think filter=Q(due_date__year=datetime.now().date().year) should work for you, if I understand your question correctly.
This worked for me
result = Model.objects.all().filter(*filters, created__year=datetime.now().year).distinct().aggregate(
t=Sum('t'),
b=Sum('b'),
)
result2 = Model.objects.all().filter(*filters, created__year=datetime.now().year).distinct().aggregate(
past_due=Sum('balance', filter=Q(due_date__lt=timezone.now()))
)

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.

Set initial values in form passing parameters (kwargs) with view

I want to prefill a form with values taken in a table.
First I pass the PK relative to the line where I wan't to get values and build the kwargs list:
views.py
def NavetteToFicheCreateView(request, pk):
navette = Navette.objects.get(id=pk)
ref = navette.id
attribute_set = navette.famille.pk
cost = navette.cost
qty = navette.qty
etat = navette.etat
etat_ebay = navette.etat.etat_ebay
ean = get_last_ean()
form = NavetteToFicheForm(
request.POST,
ref=ref,
attribute_set=attribute_set,
cost=cost,
qty=qty,
etat=etat,
etat_ebay=etat_ebay,
ean=ean,
)
[...]
then I retrieve the kwargs in the form.py and setup my initial values
class NavetteToFicheForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.ref = kwargs.pop('ref', 'noref')
self.attribute_set = kwargs.pop('attribute_set', 9999)
self.cost = kwargs.pop('cost', 0)
self.qty = kwargs.pop('qty', 0)
self.etat = kwargs.pop('etat', 0)
self.etat_ebay = kwargs.pop('etat_ebay', 9999)
self.ean = kwargs.pop('ean', 9999)
super(NavetteToFicheForm, self).__init__(*args, **kwargs)
self.fields['ref'].initial = self.ref
self.fields['attribute_set'].initial = self.attribute_set
self.fields['cost'].initial = self.cost
self.fields['qty'].initial = self.qty
self.fields['etat'].initial = self.etat
self.fields['etat_ebay'].initial = self.etat_ebay
self.fields['ean'].initial = self.ean
[...]
My problem : some fields like "ref" or "attribute_set" are foreignKeys and are not transmitted when i display the form.
For checking my values :
print(self.ref)
print(self.attribute_set)
output
34
2
noref
9999
questions :
Why does the "print" displays 2 couples of values ? This looks like as if the "noref" and "999" are taken in account.
Why if i set manually 34 and 2 values, it works ?
self.fields['ref'].initial = 34
self.fields['attribute_set'].initial = 2
There's maybe a better way of doing this but I don't know it yet .

Django querysets to subtract items

I have two query sets to get the values for stock_in and stock_out. How do i loop to subtract stock_out from stock_in
here are the querysets
stock_in = OrderItems.objects.values('drug').annotate(
quantity_received=Sum('quantity_received'),
).order_by('drug')
stock_out = RequestItems.objects.values('drug').annotate(
quantity_received=Sum('quantity_issued')
).order_by('drug')
Approach it from the Drug model. It makes it a bit easier on the query.
Drug.objects.annotate(
stock_in=Sum('orderitems_set__quantity_received'),
stock_out=Sum('requestitems_set__quantity_issued'),
).annotate(
current_stock=F('stock_in')-F('stock_out')
)
Or you might be able to do it in one. I'm not sure.
Drug.objects.annotate(
current_stock=Sum('orderitems_set__quantity_received') - Sum('requestitems_set__quantity_issued'),
)
def stock_list(request):
stock_in = OrderItems.objects.values('drug__drugTitle','drug__drugCode', 'drug__denom_quantity').order_by('drug').annotate(quant_in=Sum('quantity_received'))
stock_out = RequestItems.objects.values('drug__drugTitle', 'drug__drugCode', 'drug__denom_quantity').order_by('drug').annotate(quant_out=Sum('quantity_issued'))
stock_available=[]
index = 0
for item in stock_out:
if item is None:
remaining_stc = stock_in[index]['quant_in']
stock_available.append({'drug__drugTitle':stock_in[index]['drug__drugTitle'],'drug__drugCode':stock_in[index]['drug__drugCode'], 'drug__denom_quantity':stock_in[index]['drug__denom_quantity'], 'quantr':remaining_stc})
else:
remaining_stc = stock_in[index]['quant_in']-stock_out[index]['quant_out']
stock_available.append({'drug__drugTitle':stock_in[index]['drug__drugTitle'],'drug__drugCode':stock_in[index]['drug__drugCode'], 'drug__denom_quantity':stock_in[index]['drug__denom_quantity'], 'quantr':remaining_stc})
index = index + 1
context = {
"stock_available":stock_available,
}
return render(request, 'stock/stock_list.html', context)

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