F() Expression strange behavior with Django - python

I have this template tag that ultimately returns a list of 'active' advertisements (checks if Campaign with the active field is True, then pulls ads from the Campaign with a queryset)
#register.assignment_tag
def get_current_campaigns(amount):
# Get all the campaigns that are active
current_campaigns = Campaign.objects.filter(active=True)
current_campaigns_count = current_campaigns.count()
# To avoid the list index being out of range and throwing an IndexError
# We reduce the amount to match the amount of rows in the model if the
# amount of rows is less than the amount being requested.
if amount > current_campaigns_count:
amount = current_campaigns_count
# Select active campaigns randomly
random_camps = []
for i in range(amount):
random_camps.append(random.choice(current_campaigns))
# prepare all the ads to return
output = []
for campaign in random_camps:
# get all the ads that a campaign has
ads = campaign.advertisement_set.all()
# now select one randomly
ad = random.choice(ads)
# hand it to output
output.append(ad)
# mark that this campaign has been seen
campaign.impressions = F('impressions') + 1
campaign.save()
# checks and sets if the campaign is still active
campaign.check_active()
return output
And here is the Model that goes with it:
class Campaign(models.Model):
''' Represents an Advertisement Campaign '''
title = models.CharField(max_length=50, blank=True, verbose_name='Campaign Title')
impressions = models.IntegerField()
impression_limit = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
def check_active(self):
''' Checks if the Campaign is currently active '''
if self.impressions >= self.impression_limit:
self.active = False
self.save()
The strange bit: Every time I visit the page the ad is on and then check it in the admin, the object impressions goes up by 2 (it should be 1) and gets marked as False, even if this if self.impressions >= self.impression_limit isn't true, it still somehow changes the active field to being False regardless.
Any clue why this strange behavior is happening? I can provide more info if needed.

random.choice does not guarantee to produce non-repeating items.
import random
random_camps = random.sample(current_campaigns, amount)
is the way to go here.
Update
If you're worried about the speed, this question addresses quick random row selection in postgres.

Related

Shopify Python - Set inventory quantity

I'm trying to set inventory quantity of a product in shopify using the Shopify Python Api.
As i understand it, i need to set the 'inventory_level' of the 'inventory_item' that belongs to the product, but after a few days of searching and testing i still have no luck.
Where i'm at
I have my products showing up in my store with all the data but the inventory quantity.
I'm not sure how to proceed as there's not a whole lot of documentation.
Here's my code for creating a product
def CreateShopifyProduct(data):
# CREATE PRODUCT
product = shopify.Product()
# Add stuff to product, variant and inventoryItem here
product.title = data['title']
#product.status = ""
#product.tags = data['tags']
product.body_html = data['description']
if 'catagory' in data:
product.product_type = data['category']
if 'vendor' in data:
product.vendor = data['vendor']
if 'image_url' in data:
image_path = data['image_url']
image = shopify.Image()
image.src = image_path
product.images = [image]
else:
try:
image = GetLocalImageFiles(data['id'])
product.images = [image]
except:
print("No local images found")
success = product.save() #returns false if the record is invalid
# CREATE VARIANT
variant = shopify.Variant()
if 'ean' in data:
variant.barcode = data['ean']
variant.price = data['gross_price']
variant.weight = data['weight']
#variant.count = data['inventory']
variant.inventory_management = 'shopify'
product.variants = [variant]
variant.product_id = product.id
s = variant.save()
success = product.save() #returns false if the record is invalid
# CREATE INVENTORYITEM
inventoryItem = shopify.InventoryItem()
#inventoryItem = variant.inventory_item
inventoryItem.tracked = True
inventoryItem.id = product.id
variant.inventory_quantity = data['inventory']
inventoryItem.inventory_quantity = data['inventory']
variant.inventory_item = inventoryItem
s = variant.save()
success = product.save()
#ii = inventoryItem.save() # this returns 406
#inv_level = shopify.InventoryLevel.find(inventory_item_ids=6792364982390, location_ids=61763518582)
#quantity = inv_level[0].__dict__['attributes']['available']
#shopify.InventoryLevel.set(location_id=61763518582, inventory_item_id=variant.inventory_item.id, available=data['inventory'])
#shopify.InventoryLevel.connect(61763518582, variant.inventory_item.id)
if product.errors:
#something went wrong, see new_product.errors.full_messages() for example
print("error")
print(product.errors.full_messages())
If i try to set the InventoryLevel with
shopify.InventoryLevel.set(61527654518, inventoryItem.id, 42)
# or
shopify.InventoryLevel.set(location_id=61527654518, inventory_item_id=inventoryItem.id, available=17)
I recieve a
pyactiveresource.connection.ResourceNotFound: Not Found: https://domain.myshopify.com/admin/api/2022-07/inventory_levels/set.json
You need three things to update an inventory level. One, you need a valid location ID. Two, you need the inventory item ID. Finally, you need the amount to adjust inventory to, that will adjust the inventory there to match your needs.
You should really play at the command-line and ensure you can quickly get the information you need, then try your updates. In other words, ensure you are getting a good location ID, inventory item ID and finally, that you know the amount of inventory already in Shopify. Since you have to calculate a delta change, these are the minimum steps most people take.
Note that once you get good at doing one item, you'll realize Shopify also accepts up to 100 at a time, making your updates a lot faster.

How to build a Django QuerySet to check conditions on two manyToMany fields

I have the following models (simplified):
class Resource(models.Model):
name = models.CharField(max_length=64, unique=True)
class ResourceFlow(models.Model):
resource = models.ForeignKey(Resource, related_name="flow")
amount = models.IntegerField()
class Workflow(models.Model):
inputs = models.ManyToManyField(ResourceFlow, related_name="workflow")
class Stock(models):
resource = models.ForeignKey(Resource, related_name="stock")
amount = models.IntegerField()
class Producer(models.Model):
workflow = models.ForeignKey(Workflow, related_name="location")
stocks = models.ManyToManyField(Stock, related_name="location")
I would like to test with computation done by the the DB engine if I can start a production.
A production can start if I have enough stock: for my Producer's workflow, all inputs ResourcesFlow amount have to be present in the Producer'stocks
So the queryset might be one those result:
for a given producer return all stocked resources that do not fulfill Workflow inputs amounts conditions
for a given producer return inputs resources needed for the workflow that are not in sufficient quantity in its stocks
It is possible to do that in Django? And if yes how to do it?
Not sure if you've found the answer but anyways, hope I understand your question correctly.
Let's assume we have the following resources:
head = Resource.objects.create(name="head")
neck = Resource.objects.create(name="neck")
body = Resource.objects.create(name="body")
arm = Resource.objects.create(name="arm")
leg = Resource.objects.create(name="leg")
And we have a build_a_robot workflow:
build_a_robot = Workflow.objects.create()
build_a_robot.inputs.add(ResourceFlow.objects.create(resource=head, amount=1))
build_a_robot.inputs.add(ResourceFlow.objects.create(resource=neck, amount=1))
build_a_robot.inputs.add(ResourceFlow.objects.create(resource=body, amount=1))
build_a_robot.inputs.add(ResourceFlow.objects.create(resource=arm, amount=2))
build_a_robot.inputs.add(ResourceFlow.objects.create(resource=leg, amount=2))
And finally, we have a producer:
producer = Producer.objects.create(workflow=build_a_robot)
producer.stocks.add(Stock.objects.create(resource=head, amount=0))
producer.stocks.add(Stock.objects.create(resource=neck, amount=3))
producer.stocks.add(Stock.objects.create(resource=body, amount=1))
producer.stocks.add(Stock.objects.create(resource=arm, amount=10))
producer.stocks.add(Stock.objects.create(resource=leg, amount=1))
We want to find the list of resources that we have run out of to build a robot given producer.
I think here's one way to do it:
from django.db.models import OuterRef, Subquery
required_resources = ResourceFlow.objects.filter(pk__in=producer.workflow.inputs.values("pk")).values("resource")
required_amount = producer.workflow.inputs.filter(resource=OuterRef("resource")).values("amount")[:1]
missing_stocks = Stock.objects.filter(resource_id__in=required_resources).filter(amount__lt=required_amount)
In this example, missing_stocks will be equal to:
<QuerySet [<Stock: Stock [Resource [head], 0]>, <Stock: Stock [Resource [leg], 1]>]>
So, we need more head and leg to build a robot.

Search for similar products quoted by Different vendors and get cheapest in order_line

I am using Odoo and python.
I am trying to search for similar products in "purchase.order.line" quoted by different vendors and then get the cheapest vendor.
Below are the pics:
Vendor 1
Vendor 2
The upper section is class PurchaseOrder
and where there is Products and price is class PurchaseOrderLine
The below code is what I was trying to do for the cheapest product in the second class "purchase.order.line".
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
supplier_prequalification_id = fields.Many2one("supplier.prequalification", string="Supplier Prequalification")
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
supplier_prequalification_id = fields.Many2one("supplier.prequalification", related="order_id.supplier_prequalification_id", string="Supplier Prequalification", store=True)
cheapest = fields.Boolean(string="Cheapest Price", compute="compute_cheapest")
vendor = fields.Many2one("res.partner", string="Vendor", related="partner_id", store=True)
count = fields.Float()
def compute_cheapest(self):
for rec in self:
partner = rec.env['purchase.order'].search([('partner_id', '=', rec.partner_id.id)])
for each_partner in partner:
line = each_partner.order_line
for item in line:
if item.product_id.__________:
item.count = min(item.price_unit)
if item.count:
item.write({'cheapest': True})
else:
item.write({'cheapest': False})
The place where I have put the dash is where I am stuck. How can I search in every vendor's oder_line and find similar products quoted by them. The cheapest price for that product I can store it on count float and then if count I do the last code of selecting boolean true.
Doesn't have to store cheapest on count. We can just say cheapest write boolean.
....
item.write({'cheapest': True})
else:
item.write({'cheapest': False})
NB: I have set field quality to Boolean that's why in the end I am writting "True" or "False"
Or is there a better way to do it
Any help will be appreciated

How to Handle When Request Returns None

I have a list of IDs which corresponds to a set of records (opportunities) in a database. I then pass this list as a parameter in a RESTful API request where I am filtering the results (tickets) by ID. For each match, the query returns JSON data pertaining to the individual record. However, I want to handle when the query does not find a match. I would like to assign some value for this case such as the string "None", because not every opportunity has a ticket. How can I make sure there exists some value in presales_tickets for every ID in opportunity_list? Could I provide a default value in the request for this case?
views.py
opportunities = cwObj.get_opportunities()
temp = []
opportunity_list = []
cw_presales_engineers = []
for opportunity in opportunities:
temp.append(str(opportunity['id']))
opportunity_list = ','.join(temp)
presales_tickets = cwObj.get_tickets_by_opportunity(opportunity_list)
for opportunity in opportunities:
try:
if opportunity['id'] == presales_tickets[0]['opportunity']['id']:
try:
for presales_ticket in presales_tickets:
cw_engineer = presales_ticket['owner']['name']
cw_presales_engineers.append(cw_engineer)
except:
pass
else:
cw_engineer = 'None'
cw_presales_engineers.append(cw_engineer)
except AttributeError:
cw_engineer = ''
cw_presales_engineers.append(cw_engineer)
So, lets say you have a Ticket model and Opportunity model. Connected via a foreign key.
class Opportunity(models.Model):
... some fields here ...
class Ticket(models.Model):
opportunity = models.ForeignKey(Opportunity)
and in your view, you get a list of opportunity ids
def some_view(request):
ids = request.GET['ids']
It sounds, like what you want is to fetch all the tickets for the supplied opportunities and add some default processing for the opportunities that do not have tickets. If that is the case, why not do something like
def some_view(request):
ids = request.GET['ids']
tickets = Ticket.objects.filter(opportunity__id__in=ids)
results = []
for ticket in tickets:
result = ... do your thing here ...
results.append(result)
# now handle missing opportunities
good_ids = tickets.values_list('opportunity__id', flat=True).distinct()
for id in ids:
if id not in good_ids:
result = ... do your default processing ...
results.append(result)
Is that what you are trying to do?

Django annotate model and filter for specific value

I'm trying to get into djangos annotate, but can't quite figure out how it works exactly.
I've got a function where I'd like to annotate a queryset of customers, filter them and return the number of customers
def my_func(self):
received_signatures = self.customer_set.annotate(Count('registrations').filter().count()
Now for the filter part, thats where I have a problem figuring out how to do that. The thing I'd like to filter for is my received_signatures, which is a function that is being called in my customer.py
def received_signatures(self):
signatures = [reg.brought_signature for reg in self.registrations.all() if reg.status == '1_YES']
if len(signatures):
return all(signatures)
else:
return None
brough_signature is a DB Field
So how can I annotate the queryset, filter for the received_signatures and then return a number?
Relevant Model Information:
class Customer(models.Model):
brought_signature = models.BooleanField(u'Brought Signature', default=False)
class Registration(models.Model):
brought_signature = models.BooleanField(u'Brought Signature', default=False)
status = models.CharField(u'Status', max_length=10, choices=STATUS_CHOICES, default='4_RECEIVED')
Note: A participant and a registration can have brought_signature. I have a setting in my program which allows me to either A) mark only brought_signature at my participant (which mean he brought the signature for ALL his registrations) or B) mark brought_signature for every registration he has
For this case Option B) is relevant. With my received_signatures I check if the customer has brought every signature for every registration where his status is "1_YES" and I want to count all the customers who did so and return a number (which I then use in another function for a pygal chart)
If I understand it correctly, you want to check if all the Registrations for a given Customer with status == '1_YES should have as attribute .brought_signature = True, and there should be at least such value. There are several approaches for this.
We can do this by writing it like:
received_signatures = self.customer_set.filter(
registration__status='1_YES'
).annotate(
minb=Min('registration__brought_signature')
).filter(
minb__gt=0
).count()
So what we here do is first .filter(..) on the registrations that have as status 1_YES, next we calculate for every customer a value minb that is the minimum of brought_signature of these Registrations. So in case one of the brought_signatures of the related Registrations is False (in a database that is usually 0), then Min(..) is False as well. In case all brought_signatures are True (in a database that is usually 1), then the result is 1, we can then filter on the fact that minb should thus be greater than 0.
So Customers with no Registration will not be counted, Customers with no Registration with status 1_YES, will not be counted, Customers with Registrations for which there is a Registration with status 1_YES, but with brough_signature will not be counted. Only Customers for which all Registrations that have status 1_YES (not per se all Registrations) have brough_signature = True are counted.

Categories