Save method of Django model does not update fields of existing record even if force update - python-3.x

I am trying to update the record that already exists in the database and therefore I use this code
if 'supplierId' not in req.keys():
return JsonResponse({'code': 0, 'msg': "supplier was not selected", 'result': ''}, safe=False)
assigneeId = User.objects.get(pk=req.get('assigneeId', 1))
responsibleId = User.objects.get(pk=req.get('responsibleId', 1))
redistributionMethod = req.get('redistributionMethod', 0)
amount = req.get('allCost', 0)
procurement_doc = ProcurementDocJournal.objects.get(id=pk)
print(procurement_doc)
procurement_doc.docType = req['docType']
procurement_doc.status = req['status']
procurement_doc.companyId = Company.objects.get(pk=req['companyId'])
procurement_doc.datetime = req['datetime']
procurement_doc.supplierId = Partner.objects.get(pk=req['supplierId'])
procurement_doc.assigneeId = assigneeId
procurement_doc.warehouseId = Warehouse.objects.get(pk=req['warehouseId'])
procurement_doc.responsibleId = responsibleId
procurement_doc.redistributionMethod = redistributionMethod
procurement_doc.amount = amount
procurement_doc.comment = req['comment']
procurement_doc.save(force_update=True, update_fields=['comment', 'amount', 'redistributionMethod',
'responsibleId', 'warehouseId',
'supplierId', 'datetime',
'companyId', 'assigneeId', 'status', 'docType'])
where req contains a request
something like this
{
'docType': 3,
'status': 1,
'companyId': '2',
'warehouseId': '3',
'assigneeId': '5',
'supplierId': '12671',
'responsibleId': '5',
'datetime': '2020-04-01 08:01:00',
'comment': ''
}
As you can see there is a print which assures me that I selected the correct row
when I noticed that these records are not updated I searched for causes and found
this question where the guy who asked says The message field was missing from the model definition
in my case, none of these are missing from the model's description
class ProcurementDocJournal(models.Model):
id = models.IntegerField(primary_key=True, null=False)
docNumber = models.IntegerField()
status = models.IntegerField()
docType = models.IntegerField()
depended = models.IntegerField()
companyId = models.ForeignKey(Company, on_delete=models.CASCADE,
db_column='companyId')
created_at = models.DateTimeField(auto_now_add=True)
datetime = models.DateTimeField()
currencyId = models.ForeignKey(Currency, db_column='currencyId', on_delete=models.CASCADE)
currencyRate = models.FloatField()
redistributionMethod = models.IntegerField()
assigneeId = models.ForeignKey(User, on_delete=models.CASCADE, related_name='ProcurementDocJournal',
db_column='assigneeId')
warehouseId = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
db_column='warehouseId')
responsibleId = models.ForeignKey(User, on_delete=models.CASCADE, related_name='ProcurementDoc',
db_column='responsibleId')
supplierId = models.ForeignKey(Partner, on_delete=models.CASCADE,
db_column='supplierId')
amount = models.FloatField()
comment = models.TextField()
class Meta:
db_table = 'procurementDocJournal'
get_latest_by = 'id'
Edit
I have an action that contains
procurement_doc_journal_item = ProcurementDocJournal.objects.get(id=pk)
currencyId = req['currency']
currency = Currency.objects.get(id=currencyId)
currencyRate = CurrencyRate(date, currency.name)
procurement_doc_journal_item.currencyId = currency
procurement_doc_journal_item.currencyRate = currencyRate['rate']
procurement_doc_journal_item.save()
and works like a charm
there is no any error that logs say

You don't specify if there's any error in your logs. I'd kind of expect to see something since if it's not saving, it must be bombing out before that as no fields are mandatory.
I am, however, not sure you're setting your FKs correctly here:
procurement_doc.companyId = Company(req['companyId'])
Should be
procurement_doc.companyId = Company.objects.get(pk=req['companyId'])
and that assumes companyId is set to a value that exists of course ... Whether this is the source of your issue or not, I'm not sure. I don't see any of your FKs are mandatory.
Edit: I've just tested this on one of my models with a simple id key:
>>> Contact(1)
Contact()
>>> c = Contact(1)
>>> c.name
''
>>> c = Contact.objects.get(pk=1)
>>> c.name
'Mike'
It really doesn't seem to me as though it works ...
Sidenote: You can simplify code like this:
if 'allCost' not in req.keys():
amount = 0
else:
amount = req['allCost']
By doing:
amount = req.get('allCost', 0)

Related

django returning duplicates even though distinct() is used

I'm using django=4.1, python=3.8 & sqlite. What I want to do filter our the top five player points and unique players in a given month and present it in a template, there shouldn't be any duplicates. This is the PlayerPoint model:
class PlayerPoint(models.Model):
OPERATION_CHOICES = (('ADD', 'ADD'), ('SUB', 'SUBTRACT'), ('RMN', 'REMAIN'))
points = models.IntegerField(null=False, default=0)
operation = models.CharField(
max_length=3,
null=False,
choices=OPERATION_CHOICES,
default=OPERATION_CHOICES[2][0]
)
operation_amount = models.IntegerField(null=False)
operation_reason = models.CharField(null=False, max_length=1500)
player = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=False,
on_delete=models.PROTECT,
to_field="phone_number",
related_name="player_points"
)
points_ts = models.DateTimeField(auto_now_add=True, null=False)
And this is the player model:
class Player(AbstractUser):
phone_number = models.CharField(
max_length=14,
unique=True,
help_text="Please ensure +251 is included"
)
first_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
father_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
grandfather_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
email = models.EmailField(
unique=True,
help_text="please ensure that the email is correct"
)
age = models.CharField(max_length=3)
profile_pic = models.ImageField(upload_to='profile_pix', default='profile_pix/placeholder.jpg')
joined_ts = models.DateTimeField(auto_now_add=True, null=False)
username = None
When I run the below query it gives me the top 5 Player Points but it's not distinct, the player_ids are duplicates. It keeps repeating the same Player.
q = PlayerPoint.objects.filter(points_ts__year=2023, points_ts__month=1, points__gte=2000)
x = q.order_by('-points')
y = x.values_list('player_id', flat=True).distinct()
print(y)
The output is: <QuerySet [('+251703445566',), ('+251703445566',), ('+251703445566',), ('+251703445566',), ('+251703445566',), ('+251703445566',)]>
As you can see it's the same phone numbers meaning the same player.
How do I fix it?
My gratitude before hand for reading and helping.

how to work with foreign key field in django

Hi Everyone i am working work django framework, where i used to upload excel file in Dailytrip table, current i get car_mumber from car table, but now i need to store car_number from Car_team table also team_id, i am storing car_id and team_id in car_team table also i need to store team_id in dailytrip table automaticlly based on car_id(car_number) i am to much confuse how to i work that, pls help me out
models.py
class Car_team(BaseModel):
team = models.ForeignKey(
Team,
models.CASCADE,
verbose_name='Team',
null=True,
)
car=models.ForeignKey(
Car,
models.CASCADE,
verbose_name='Car',
null=True)
city =models.ForeignKey(
City,
models.CASCADE,
verbose_name='City',
)
start_date=models.DateField(null=True, blank=True)
end_date=models.DateField(null=True, blank=True)
views.py
def add_payout_uber_daily_data(request):
if request.method == 'POST':
form = UberPerformanceDataForm(request.POST, request.FILES, request=request)
if form.is_valid():
date = form.cleaned_data['date']
excel_file = request.FILES['file']
df = pd.read_excel(excel_file)
is_na = pd.isna(df['Date']).sum().sum() + pd.isna(df['Name']).sum().sum() + pd.isna(df['UUID']).sum().sum() + pd.isna(df['Net Fare With Toll']).sum().sum() + pd.isna(df['Trips']).sum().sum() + pd.isna(df['Uber KMs']).sum().sum() + pd.isna(df['CashCollected']).sum().sum() + pd.isna(df['UberToll']).sum().sum() + pd.isna(df['Tips']).sum().sum() + pd.isna(df['Hours Online']).sum().sum() + pd.isna(df['Ratings']).sum().sum() + pd.isna(df['Acceptance Rate']).sum().sum() + pd.isna(df['Cancellation Rate']).sum().sum()
error_list = []
if is_na > 0:
error_list.append('Found #N/A or blank values in the sheet. Please correct and re-upload')
context = {'error_list': error_list, 'menu_payout': 'active','submenu_daily_data': 'active','form': form, }
return render(request, 'add_payout_uber_daily_data.html', context=context)
date_match = True
for d in df['Date']:
if str(d.strftime("%Y-%m-%d")) != str(date):
date_match = False
break
if not date_match:
error_list.append('Some dates are not matching in excel')
if len(error_list) > 0:
context = {'error_list': error_list, 'menu_payout': 'active','submenu_daily_data': 'active','form': form, }
return render(request, 'add_payout_uber_daily_data.html', context=context)
DailyTrip.objects.filter(date=date).update(is_active=0)
for i in df.index:
uuid = df['UUID'][i]
driver_id = None
car_id = None
fleet_id = None
manager_id = None
try:
driver = Driver.objects.get(uber_uuid=uuid)
driver_id = driver.id
except Driver.DoesNotExist:
driver_id = None
#replce car code and store car_number,car_id,team_id via car_team only this logic need to change current get car_number direct car table but we need car_number vai foriegn key
try:
car = Car.objects.get(car_number=df["Car Number"][i])
car_id = car.id
manager_id = car.manager_id
except Car.DoesNotExist:
car_id = None
try:
fleet = Fleet.objects.get(name=df["Fleet Name"][i])
fleet_id = fleet.id
except Fleet.DoesNotExist:
fleet_id = None
name = df['Name'][i]
car_number = df['Car Number'][i]
fare_total = df['Net Fare With Toll'][i]
trips = df['Trips'][i]
pool_trips = 0
hours_online = df['Hours Online'][i]
total_km = df['Uber KMs'][i]
cash_collected = abs(df['CashCollected'][i])
toll = df['UberToll'][i]
tip_amount = df['Tips'][i]
fare_avg = float(fare_total)/int(trips)
fare_per_hour_online = float(fare_total)/float(hours_online)
fare_per_km = fare_total/total_km
trips_per_hour = trips/hours_online
km_per_trip = total_km/trips
rating = df['Ratings'][i]
acceptance_rate_perc = float(df['Acceptance Rate'][i])/100
driver_cancellation_rate = float(df['Cancellation Rate'][i])/100
obj, created = DailyTrip.all_objects.update_or_create(
date=date, uuid=uuid,
defaults={
'car_id': car_id,
'manager_id': manager_id,
'car_number': car_number,
'driver_id': driver_id,
'car_id': car_id,
'fleet_id': fleet_id,
'driver_name': name,
'fare_total': fare_total,
'trips': trips,
'pool_trips': pool_trips,
'hours_online': hours_online,
'total_km': total_km,
'cash_collected': cash_collected,
'toll': toll,
'tip_amount': tip_amount,
'fare_avg': fare_avg,
'fare_per_hour_online':fare_per_hour_online,
'fare_per_km':fare_per_km,
'trips_per_hour': trips_per_hour,
'km_per_trip': km_per_trip,
'rating': rating,
'acceptance_rate_perc': acceptance_rate_perc,
'driver_cancellation_rate': driver_cancellation_rate,
'is_active': 1,
'comments': None}
)
if len(error_list) > 0:
DailyTrip.objects.filter(date=date).update(is_active=0)
context = {'error_list': error_list, 'menu_payout': 'active','submenu_daily_data': 'active','form': form, }
return render(request, 'add_payout_uber_daily_data.html', context=context)
else:
messages.success(request, 'Daily Trips added Successfully...')
return redirect('/fleet/payout/daily_data/add/uber')
else:
form = UberPerformanceDataForm(initial={})
context = {
'menu_payout': 'active',
'submenu_daily_data': 'active',
'form': form,
}
return render(request, 'add_payout_uber_daily_data.html', context=context)
You can try that :
to get car_number from car_team -->
car_team = car_team.objects.objects.all().last() # to get the last car_team for example
car_number = car_team.car.car_number # to get the car number from the car_team
try:
car = Car.objects.get(car_number=df["Car Number"][i])
car_id = car.id
car1=Car_team.objects.filter(car_id=car_id)
if car1:
team_id=car1[0].team_id
else:
team_id=None
except Car.DoesNotExist:
car_id = None
team_id= None

Django - 'Merging' or Sorting similar objects from a queryset

Good day, I have a django model that represents users (node) that have a relation (edge) with other users. User A is a friend of User B and User A interacted with User B .
class Node(BaseInfo):
firstname = models.CharField(max_length=200,blank = True, null=True)
lastname = models.CharField(max_length=200,blank = True,null=True)
dob = models.DateField(blank=True,null=True)
username = models.CharField(max_length=200, blank=True, null=True)
picture_url = models.CharField(max_length=200,blank = True,null =True)
phone_number = models.CharField(max_length=200,blank = True, null=True)
email = models.CharField(max_length=200,blank = True, null=True)
gender= models.CharField(max_length=200,blank = True, null=True)
uid = models.CharField(max_length=200,blank=True,null=True)
url = models.CharField(max_length=2000,blank=True,null=True)
edges = models.ManyToManyField('self', through='Edge',blank = True)
task = models.ManyToManyField('Task',blank = True)
location = models.ManyToManyField('Location',blank = True)
case = models.ManyToManyField('case',blank = True)
class Edge(BaseInfo):
source = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_source")
target = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_target")
class Meta:
constraints = [
models.UniqueConstraint(fields=['source','target','label'], name="unique_edge"),
]
def __str__(self):
return '%s' % (self.label)
At this point i query the relations with:
result = Edge.objects.filter(source = User A);
The result will contain a query set with
[Edge(User A , User B , "Friends"),[Edge(User A, User B , "Interacted")]
I want to show the relations (Grouped By ? ) of User A in a template
So it says , Profile A is a friend and has interacted with B. Rather then having to iterate over the queryset and having 2 lines in my template :User A is friends with B , User A interacties with user B.
I already looked in aggregation functions of Django but cant get any further.
Thanks in advance for youre guidance.

How to bulk create or update in Django

I have to process an item report CSV file every 1 hour. The CSV contains 150k+ records for 1 account and there are multiple accounts in my system. I was working previously on rails and there was active record gem to handle this use case very efficiently. I am looking for an alternate to this gem in Django or any built in method that will be helpful to import such large data in bulk.
So far I have tried this code.
class ItemReportService:
def call(self, file_url):
with open(file_url, 'r') as file:
reader = csv.DictReader(file)
products = []
for row in reader:
product = self.process_product(row)
products.append(product)
self.update_products(products)
def process_product(self, row):
print(f'Processing sku: {row["SKU"]}')
product = Product.objects.filter(
sku=row['SKU']).first() or Product(sku=row['SKU'])
product.listing_title = row['Product Name']
product.listed_price = row['Price']
product.buy_box_price = row['Buy Box Item Price'] + \
row['Buy Box Shipping Price']
product.status = row['Lifecycle Status']
return product
def update_products(self, products):
Product.objects.bulk_update(
products,
[
'listing_title',
'listed_price',
'buy_box_price',
'Lifecycle Status'
]
)
It is raising this exception because when there is a new product it doesn't have primary key assigned to it
ValueError: All bulk_update() objects must have a primary key set.
Django 4.1 has new parameters for bulk_create(update_conflicts=bool and update_fields=[])
If your model has a field UNIQUE usually Django would ignore it when creating new data. But if you set the update_conflicts parameter to True, the fields inside update_fields will be updated.
You are not saving the product in the database before applying bulk_update.
I have checked your code for this purpose, you can use bulk_insert with an additional parameter
Model.objects.bulk_create(self.data, ignore_conflicts=True)
or
columns = ['column1', 'column2']
obj = Model.objects.filter(column1="sku").first()
if not obj:
obj = Model.objects.create(column1="sku")
obj.column1 = row["column1"] or obj.column1
obj.column2 = row["column2"] or obj.column2
items_to_be_inserted.append(obj)
In the end, you can do bulk update like
Model.objects.bulk_update(items_to_be_inserted, columns)
This will solve your problem.
I made this class function which can be used on any Django model in a project.
from django.db import models
class BaseModel(models.Model):
#classmethod
def bulk_create_or_update(
cls, uniques: list[str],
defaults: list[str],
data: list[dict]
):
# Get existing object list
data_dict, select = {}, None
for entry in data:
sub_entry, key = {}, ''
for uniq in uniques:
sub_entry[uniq] = entry[uniq]
key += str(entry[uniq])
data_dict[key] = entry
if not select:
select = models.Q(**sub_entry)
continue
select |= models.Q(**sub_entry)
records = cls.objects.filter(select).values('pk', *uniques)
existing = {}
for rec in records:
key = ''
for uniq in uniques:
key += str(rec[uniq])
existing[key] = rec
# Split new objects from existing ones
to_create, to_update = [], []
for key, entry in data_dict.items():
obj = cls(**entry)
if key not in existing:
to_create.append(obj)
continue
obj.pk = existing[key]['pk']
to_update.append(obj)
cls.objects.bulk_create(to_create, batch_size=1000)
cls.objects.bulk_update(to_create, defaults, batch_size=1000)
Let take an usage example
class Product(BaseModel)
price = models.IntegerField()
name = models.CharField(max_length=128, unique=True)
status = models.CharField(max_length=128)
if __name__ == '__main__':
data = [
{'price': 50, 'name': 'p1', 'status': 'New'},
{'price': 33, 'name': 'p2', 'status': 'Old'}
]
Product.bulk_create_or_update(uniques=['name'], defaults=['price', 'status'], data=data)
Any improvement suggestion of the code is welcome.

Assign the same value to object keys from lambda in Python (Django)

When trying to assign the same value to starting_bid and price from the Listing model in my Django project the below approach always yields different values. How can I assign the same value while still having the lambda return a random integer whenever the test runs?
tests.py
SEEDER = Seed.seeder() # from django_seed
AMOUNT = 15
MIN = 5
MAX = 84
PRICE = lambda x: random.randint(MIN, MAX)
SEEDER.add_entity(
Listing,
AMOUNT,
{
"starting_bid": PRICE,
"price": PRICE
})
SEEDER.execute()
Result:
{"starting_bid": 80, "price": 45}
Expected:
{"starting_bid": 80, "price": 80}
models.py
class Listing(models.Model):
CATEGORIES = (("LAP", "Laptop"), ("CON", "Console"), ("GAD", "Gadget"), ("GAM", "Game"), ("TEL", "TV"))
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=64)
description = models.CharField(max_length=256)
starting_bid = models.PositiveSmallIntegerField()
price = models.PositiveSmallIntegerField()
image_url = models.URLField(max_length=200)
category = models.CharField(max_length=8, choices=CATEGORIES)
active = models.BooleanField(default=True)
_price = []
def PRICE(round = 2):
global _price
if not _price:
_price = random.randint(MIN, MAX)
_price = [_price] * round
return _price.pop()

Resources