Can I trust Django's bulk_create to preserve order? - django

When I do something like this:
model_list = [Model(name = 'First'), Model(name = 'Second'), Model(name = 'Third')]
Model.objects.bulk_create(model_list)
Can I trust that they will be created in that same order?
That is:
pk1 = Model.objects.get(name = 'First').pk
pk2 = Model.objects.get(name = 'Second').pk
pk3 = Model.objects.get(name = 'Third').pk
(pk3 > pk2) and (pk2 > pk1)

I think you can.
model_list = [Model(name = 'First'),
Model(name = 'Second'),
Model(name = 'Third')]
Model.objects.bulk_create(model_list)
This code will be translated to the following SQL:
INSERT INTO app_model (name) VALUES ('First'), ('Second'), ('Third')
It is very unlikely that regular SQL server will insert these rows in different order.

Related

Django queryset from raw SQL

I want an equivalent of this sql query in Django
SELECT Gender, ServCode
FROM [openimisproductTestDb_16_08_22].[dbo].[tblInsuree]
JOIN [openimisproductTestDb_16_08_22].[dbo].[tblServices] ON [openimisproductTestDb_16_08_22].[dbo].[tblInsuree].AuditUserID = [openimisproductTestDb_16_08_22].[dbo].[tblServices].AuditUserID
WHERE Gender = 'F'
AND ServCode = 'F4'
What I have tried:
def assisted_birth_with_cs_query(user, **kwargs):
date_from = kwargs.get("date_from")
date_to = kwargs.get("date_to")
hflocation = kwargs.get("hflocation")
format = "%Y-%m-%d"
date_from_object = datetime.datetime.strptime(date_from, format)
date_from_str = date_from_object.strftime("%d/%m/%Y")
date_to_object = datetime.datetime.strptime(date_to, format)
date_to_str = date_to_object.strftime("%d/%m/%Y")
dictBase = {
"dateFrom": date_from_str,
"dateTo": date_to_str,
}
dictGeo = {}
if hflocation and hflocation!="0" :
hflocationObj = HealthFacility.objects.filter(
code=hflocation,
validity_to__isnull=True
).first()
dictBase["fosa"] = hflocationObj.name
claimItem = Insuree.objects.filter(
validity_from__gte = date_from,
validity_to__lte = date_to,
**dictGeo,
gender = 'F'
).count()
data = Service.objects.filter(code = 'F4').count() | Insuree.objects.filter(gender = 'F').count()
dictGeo['health_facility'] = hflocationObj.id
dictBase["post"]= str(data)
return dictBase
I tried like that but the one just adds when I want the women included in the insured table and the F4 code contained in the service table. both tables have the auditUserID column in common
It would be great if you could add the models to better see the relations between Insuree and Service. Assuming it's a 1-M, I'd go with this query:
Service.objects.filter(code='F4', insuree__gender='F').count()

Updating database based on previous csv file uploads - delete - create - or update Python/Dajngo

Please need help with the following
I am trying to update database in comparison to previous uploaded csv file. I need to update all fields except the vin if it changes (vin is the unique value), delete the item if it is no longer in the csv file and create one if one is new
vin. stock_no make model trim miles
12345789098765432 4535 honda civic lx 89000
j4j4jj49098765432 3453 toyota corolla DX 54555
12345345438765432 6254 ford mustang es 101299
When I change any value and the csv is uploaded it makes a duplicate:
def upload_file__view(request):
form = form(request.POST or None, request.FILES or
None)
company = Comp_info.objects.last()
if form.is_valid():
form.save()
obj = c.objects.get(activated=False)
with open(obj.file_name.path, 'r+') as f:
reader = c.reader(f)
for i, row in enumerate(reader):
if i==0:
pass
else:
# row = "".join(row)
# row = row.replace(",", " ")
# row = row.split()
print(row)
print(type(row))
vin = row[0].upper()
condition = row[1].replace("U", "Used").replace("N", "New")
stock_no = row[2]
year = int(row[5])
make = row[3]
model = row[4]
trim = row[6]
mileage = row[8]
mpg_city = row[18]
mpg_hwy = row[19]
engine = row[9]
transmission = row[12]
fuel_type = row[11]
vehicle_type = row[7]
drive_type = row[20].replace("4X2", "2WD").replace("4X4", "4WD")
exterior_color = row[15]
interior_color = row[16]
price = row[13].replace("0", "")
description = row[22]
features_2 = row[21]
images_data = row[23]
raw_images_list = images_data.split(',')
images_list = [""] * 25
for x in range(image_count):
if x == 25:
break
images_list[x] = raw_images_list[x]
for x in images_list:
print(x)
if images_list[0] == "":
images_list[0] = "https://www.beverlyhillscarclub.com/template/images/ina_f.jpg"
car_photo = images_list[0]
car_photo_1 = images_list[1]
car_photo_2 = images_list[2]
car_photo_3 = images_list[3]
car_photo_4 = images_list[4]
car_photo_5 = images_list[5]
car_photo_6 = images_list[6]
car_photo_7 = images_list[7]
car_photo_8 = images_list[8]
car_photo_9 = images_list[9]
car_photo_10 = images_list[10]
car_photo_11 = images_list[11]
car_photo_12 = images_list[12]
car_photo_13 = images_list[13]
car_photo_14 = images_list[14]
car_photo_15 = images_list[15]
car_photo_16 = images_list[16]
car_photo_17 = images_list[17]
car_photo_18 = images_list[18]
car_photo_19 = images_list[19]
car_photo_20 = images_list[20]
car_photo_21 = images_list[21]
car_photo_22 = images_list[22]
car_photo_23 = images_list[23]
car_photo_24 = images_list[24]
# notes = pip._vendor.requests(images_list[0], stream=True)
#car_photo = row[23]
# user = User.objects.get(username=row[3])
Cars.objects.update_or_create(
vin = vin,
condition = condition,
stock_no = stock_no,
year = year,
make = make,
model = model,
trim = trim,
mileage = mileage,
mpg_city = mpg_city,
engine = engine,
transmission = transmission,
fuel_type = fuel_type,
vehicle_type = vehicle_type,
drive_type = drive_type,
exterior_color = exterior_color,
interior_color = interior_color,
price = price,
description = description,
company_name = company.company_name,
address = company.company_address,
city = company.city,
state = company.state,
zip = company.zip_code,
phone_number = company.phone_number,
email = company.fax_number,
features_2 = features_2,
car_photo = downloadFile(car_photo),
car_photo_1 = downloadFile(car_photo_1),
car_photo_2 = downloadFile(car_photo_2),
car_photo_3 = downloadFile(car_photo_3),
car_photo_4 = downloadFile(car_photo_4),
car_photo_5 = downloadFile(car_photo_5),
car_photo_6 = downloadFile(car_photo_6),
car_photo_7 = downloadFile(car_photo_7),
car_photo_8 = downloadFile(car_photo_8),
car_photo_9 = downloadFile(car_photo_9),
car_photo_10 = downloadFile(car_photo_10),
car_photo_11 = downloadFile(car_photo_11),
car_photo_12 = downloadFile(car_photo_12),
car_photo_13 = downloadFile(car_photo_13),
car_photo_14 = downloadFile(car_photo_14),
car_photo_15 = downloadFile(car_photo_15),
car_photo_16 = downloadFile(car_photo_16),
car_photo_17 = downloadFile(car_photo_17),
car_photo_18 = downloadFile(car_photo_18),
car_photo_19 = downloadFile(car_photo_19),
car_photo_20 = downloadFile(car_photo_20),
car_photo_21 = downloadFile(car_photo_21),
car_photo_22 = downloadFile(car_photo_22),
car_photo_23 = downloadFile(car_photo_23),
car_photo_24 = downloadFile(car_photo_24)
#car_photo = car_photo,
# quantity = int(row[2]),
# salesman = user
)
obj.activated = True
obj.save()
data = {
'form' : form,
'now' : now,
}
return render(request, 'uploads.html', data)
Thanks in advance for any help!
Thank you
Step 1
An empty list was created to compare with uploaded data:
imported_cars = []
Step 2
Created a filter of unique value (primary Key) and checked if it existed and used the method get to update items. Created car (item variable) to update or create ubject.
if Cars.objects.filter(vin=vin).exists():
car = Cars.objects.get(vin=vin)
Step 3
Used else statement to create item if it did not exist.
else:
car = Cars.objects.create(vin=vin, condition=condition...)
Last, out of the loop populated empty list with updated and created cars and deleted items that were in the database but not in the csv file.
imported_cars_vin_numbers = [car.vin for car in imported_cars]
for car in Cars.objects.all():
if car.vin not in imported_cars_vin_numbers:
car.delete()
Special thanks and credit to Zack Plauché who was extremely helpful and professional in helping me and teaching me how to solve this issue.
Your issue is in the model.py
you should write the Cars object with the following.
vin = models.CharField(primary_key=True, editable=False)
Confirm this works, since I am suggesting solution without actually seeing the model.py
This should handle the update aspect of your logic. The part where you delete a vin if its not in the CSV will have to be done with new process I don't see written here.But a suggestion would be to clear the DB and repopulate, or create function that compares DB with CSV and delete object if not in CSV.

Django ORM. Joining subquery on condition

I have a table TickerStatement, which contains financial statements about companies
class Statements(models.TextChoices):
"""
Supported statements
"""
capital_lease_obligations = 'capital_lease_obligations'
net_income = 'net_income'
price = 'price'
total_assets = 'total_assets'
short_term_debt = 'short_term_debt'
total_long_term_debt = 'total_long_term_debt'
total_revenue = 'total_revenue'
total_shareholder_equity = 'total_shareholder_equity'
class TickerStatement(TimeStampMixin):
"""
Model that represents ticker financial statements
"""
name = models.CharField(choices=Statements.choices, max_length=50)
fiscal_date_ending = models.DateField()
value = models.DecimalField(max_digits=MAX_DIGITS, decimal_places=DECIMAL_PLACES)
ticker = models.ForeignKey(Ticker, on_delete=models.CASCADE, null=False,
related_name='ticker_statements')
And now I'm trying to calculate a multiplier. The formula looks like:
(short_term_debt + total_long_term_debt) / total_shareholder_equity
I wrote a raw SQL query
SELECT "fin_tickerstatement"."fiscal_date_ending",
t2.equity AS "equity",
value AS "debt",
short_term_debt AS "short_term_debt",
(value + short_term_debt) / t2.equity AS "result"
FROM "fin_tickerstatement"
JOIN
(SELECT "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value AS "equity"
FROM "fin_tickerstatement"
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'total_shareholder_equity')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC) t2
ON fin_tickerstatement.fiscal_date_ending = t2.fiscal_date_ending
JOIN
(SELECT "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value AS "short_term_debt"
FROM "fin_tickerstatement"
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'short_term_debt')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC) t3
ON fin_tickerstatement.fiscal_date_ending = t3.fiscal_date_ending
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'total_long_term_debt')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
equity,
debt,
short_term_debt
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC;
and have no idea how to translate it into Django ORM. Maybe you have some ideas or know some Django plugins that can help me.
The only way to solve this problem is to install django-query-builder.

Can you use django extra(select={}) to select multiple fields?

I am trying to add two new fields to my django product queryset and I do something like this
products = Product.objects.filter(company=company).filter(Q(name__icontains=search_term) | Q(code__icontains=search_term))
products = products.extra(select={"is_distributed": product_is_distributed_query(False)})
products = products.extra(select={"expense_type": product_expense_type_query(False)})
But the two queries (product_is_distributed_query and product_expense_type_query) are identical just returning different fields. Can I do this in only one extra(select{}) by using just one query to return both fields?
The query would be like this:
def product_is_distributed_query(use_product_code):
product_query = """
select vt.is_distributed, coalesce(et.name, '')
from (
SELECT
cp.id,
CASE WHEN SUM(coalesce(snp.id, 0)) > 0 THEN true
ELSE false
END as is_distributed,
max(coalesce(snp.expense_type_id, 0)) as maxId1
FROM
core_product AS cp
LEFT JOIN
stock_non_product AS snp
ON (cp.name = snp.name """
product_query = product_query + ("AND cp.code = snp.code" if use_product_code else "")
product_query += """
AND cp.company_id = snp.company_id AND snp.is_deleted = false)
LEFT JOIN expense_type as et ON(snp.expense_type_id = et.id AND snp.company_id = et.company_id)
WHERE
cp.id = core_product.id
AND cp.company_id = core_product.company_id
-- AND snp.company_id = core_product.company_id
GROUP BY
cp.id
) as vt
-- LEFT JOIN stock_non_product AS snp ON(snp.id = maxId)
LEFT JOIN expense_type as et ON(et.id = vt.maxId)
"""
return product_query
instead of being two separated queries that are identical to this one but return one or the other field

How to include "None" in lte/gte comparisons?

I've got this complex filtering mechanism...
d = copy(request.GET)
d.setdefault('sort_by', 'created')
d.setdefault('sort_dir', 'desc')
form = FilterShipmentForm(d)
filter = {
'status': ShipmentStatuses.ACTIVE
}
exclude = {}
if not request.user.is_staff:
filter['user__is_staff'] = False
if request.user.is_authenticated():
exclude['user__blocked_by__blocked'] = request.user
if form.is_valid():
d = form.cleaned_data
if d.get('pickup_city'): filter['pickup_address__city__icontains'] = d['pickup_city']
if d.get('dropoff_city'): filter['dropoff_address__city__icontains'] = d['dropoff_city']
if d.get('pickup_province'): filter['pickup_address__province__exact'] = d['pickup_province']
if d.get('dropoff_province'): filter['dropoff_address__province__exact'] = d['dropoff_province']
if d.get('pickup_country'): filter['pickup_address__country__exact'] = d['pickup_country']
if d.get('dropoff_country'): filter['dropoff_address__country__exact'] = d['dropoff_country']
if d.get('min_price'): filter['target_price__gte'] = d['min_price']
if d.get('max_price'): filter['target_price__lte'] = d['max_price']
if d.get('min_distance'): filter['distance__gte'] = d['min_distance'] * 1000
if d.get('max_distance'): filter['distance__lte'] = d['max_distance'] * 1000
if d.get('available_on'): # <--- RELEVANT BIT HERE ---
filter['pickup_earliest__lte'] = d['available_on'] # basically I want "lte OR none"
filter['pickup_latest__gte'] = d['available_on']
if d.get('shipper'): filter['user__username__iexact'] = d['shipper']
order = ife(d['sort_dir'] == 'desc', '-') + d['sort_by']
shipments = Shipment.objects.filter(**filter).exclude(**exclude).order_by(order) \
.annotate(num_bids=Count('bids'), min_bid=Min('bids__amount'), max_bid=Max('bids__amount'))
And now my client tells me he wants pickup/drop-off dates to be 'flexible' as an option. So I've updated the DB to allow dates to be NULL for this purpose, but now the "available for pickup on" filter won't work as expected. It should include NULL/None dates. Is there an easy fix for this?
Flip the logic and use exclude(). What you really want to do is exclude any data that specifies a date that doesn't fit. If pickup_latest and pickup_earliest are NULL it shouldn't match the exclude query and wont be removed. Eg
exclude['pickup_latest__lt'] = d['available_on']
exclude['pickup_earliest__gt'] = d['available_on']
Most database engines don't like relational comparisons with NULL values. Use <field>__isnull to explicitly check if a value is NULL in the database, but you'll need to use Q objects to OR the conditions together.
Don't think that's actually a django-specific question. Variable 'd' is a python dictionary, no? If so, you can use this:
filter['pickup_latest__gte'] = d.get('available_on', None)