Calculating the difference between two fields within a table in Django - python

I have a model which stores the opening and closing balances of a petrol station. I have an additional third column which is basically the difference between the opening and reading values.
class DailySales(models.Model):
PUMP_NAMES = (
...
)
Pump_name = models.CharField(max_length = 5, choices = PUMP_NAMES)
Opening_reading = models.IntegerField()
Closing_reading = models.IntegerField()
Gross_sales = models.IntegerField(null = True)
Date = models.DateField(auto_now_add= True)
def Calc_gross(self):
self.Gross_sales = self.Opening_reading - self.Closing_reading
super(DailySales,self).save()
the Calc_gross method works when i'm saving only one instance at a time.
But i want to add multiple opening and closing values to the model at once for which i'm using formsets.
SalesForm = modelformset_factory(DailySales, extra = 7, exclude=('Date','Gross_sales'))
This is where the logic falters. Django isnt able to calculate the gross values for each row.
Exception Value:
'list' object has no attribute 'Calc_gross'
I was thinking since its a list , I could iterate over each and invoke the Calc_gross method. What will be the name of the list?
Is there a better way to do this? Would it be logical to use Prefixes (how would u assign a prefix to each form?)

Turns out i could just loop over the formset.
if request.method == 'POST':
sale_Form = SalesForm(data = request.POST)
for Form in sale_Form:
if Form.is_valid():
gross = Form.save()
gross.Calc_gross()
gross.save()
else:
print(sale_Form.errors)

Related

How to insert ManyToMany field in django

I want to insert a ManyToMany fields in my db using django.I select some customers using checkboxes.
This is my models.py :
class Campaign(models.Model):
title = models.CharField(max_length=255)
channel = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
target_prospect = models.ManyToManyField(ProspectClient,related_name='campaigns_prospect')
target_partner = models.ManyToManyField(PartnerClient,related_name='campaigns_partners')
I try the code below in my views.py but didn't work :
def campaigns_page(request):
if request.user.is_authenticated:
if request.user.profile == 'D' or request.user.profile == 'E' or request.user.is_superuser:
campaigns = Campaign.objects.all()
prospects = ProspectClient.objects.all()
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
context = {
'campaigns':campaigns,
'prospects':prospects,
'partners':partners
}
if request.method == 'POST':
title = request.POST['title']
channel = request.POST['channel']
start_date = request.POST['start_date']
end_date = request.POST['end_date']
descriptions = request.POST['goals'].split(",")
targets = request.POST['targets']
campaign = Campaign.objects.create(title=title,channel=channel,start_date=start_date,end_date=end_date)
for description in descriptions:
goal = Goal.objects.create(description=description)
goal.campaign.add(campaign)
for target in targets:
prospects.campaign.add(campaign)
partners.campaign.add(campaign)
return render(request,'CampaignManagement/campaigns_page.html',context)
return render(request, 'Login/logout.html')
If I delete the part of tergets it works.
But with this part it gives me This error : 'QuerySet' object has no attribute 'campaign'
How I can solve this ?
I see a couple of errors. Perhaps one or more are leading to the problem.
One
Try printing this:
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
print(partners)
I suspect it will print None since you are excluding all id's in PartnerClient.objects.values('id'). On another note you don't need the all() since exclude() will return all the results you are looking for.
Two
In the line for target in targets: what exactly are you iterating through? targets = request.POST['targets'] is just giving you a string, so it would iterate through each letter. Perhaps you meant:
targets = request.POST['targets'].split(", ")
like you did for descriptions? Or perhaps you are getting a list of items from your form, in which case you can use:
targets = request.POST.getlist('targets')

How to create multiple objects with different values at a time in django?

I need to create two models from a single template. Creating Product model is fine. The Product model has the ManyToOne relation with ProductVariant. But I got problem while creating ProductVariant model.
request.POST.getlist('names') this gives me the result like this ['name1','name2] and the same goes for all.
I want to create ProductVariant object with each values. How can I do this ? Also I think there is a problem while stroing a HStoreField. request.POST.getlist('attributes') gives the value like this ['a:b','x:z'] so I converted it into dictionary(but not sure it works).
UPDATE:
What I want is
attributes, names ... all will have the same number of items in the list.
For example if the name is ['a','b','c'] then weight will also have 3 values in the list [12,15,23] like this.
I want to create ProductVariant object 3 times since every list will have 3 items in the list. The first object will have field values from the list first item which is name=a,weight=12.. and for the second object values will be name=b, weight=15 like this.
How will it be possible? Or I should change the logic ? Any suggestions ?
models
class ProductVariant(models.Model):
name = models.CharField(max_length=255, blank=False, null=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attributes = HStoreField()
price = models.FloatField(blank=False, null=False, default=0.0)
views
product = product_form.save()
attributes = request.POST.getlist('attributes')
names = request.POST.getlist('name')
up = request.POST.getlist('price')
weight = request.POST.getlist('weight')
print(names, 'names')
# converting attributes into the dictionary for the HStore field
for attribute in attributes:
attributes_dict = {}
key, value = attribute.split(':')
attributes_dict[key] = value
ProductVariant.objects.create(name=name,...) # for each value I want to create this.
Answer for update:
names = ['a', 'b', 'c']
weights = [12, 15, 23]
params = zip(names, weights)
products = [ProductVariant(name=param[0], weight=param[1]) for param in params]
ProductVariant.objects.bulk_create(products)
I disagree with this approach, but if you really want to do it this way, ziping would be the way as #forkcs pointed out.
I would use Django to help me as much as possible, before i get there, please make this change. float != money
class ProductVariant(models.Model):
name = models.CharField(max_length=255, blank=False, null=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attributes = HStoreField()
price = models.DecimalField(blank=False, null=False, default=0, max_digits=6, decimal_places=2)
Once thats done, the form should look like this:
class ProductVariantForm(forms.ModelForm):
class Meta:
fields = ('name', 'product', 'attributes', 'price')
model = ProductVariant
ProductVariantFormSet = formset_factory(ProductVariantForm)
Note that I don't have to parse/clean/format attributes? Thats because Django did it for me ;)
And you can use it as follow IF you raname your fields and not use the same name multiple times: (instead of all your fields being called "attributes", you call them "form-X-attributes" where X is the number 0-infinity, example)
product = product_form.save()
formset = ProductVariantFormSet(data=request.POST)
if formset.is_valid():
instances = []
for form in formset:
if form.is_valid(): # this could probably be removed
instances.append(form.save())
For extra credit you can also do: (it shouldn't really matter)
product = product_form.save()
formset = ProductVariantFormSet(data=request.POST)
if formset.is_valid():
instances = []
for form in formset:
if form.is_valid(): # this could probably be removed
instances.append(form.save(save=False))
ProductVariant.objects.bulk_create(instances)
What do you gain? STANDARDS!!! AND compartmentalization! Everyone that knows Django knows what you did. All your clean logic will be placed in the right place (the form), and you'll be less error prone.
Ps. i wrote tests for you. https://gist.github.com/kingbuzzman/937a9d207bd937d1b2bb22249ae6bdb2#file-formset_example-py-L142
If you want more information on my approach, see the docs https://docs.djangoproject.com/en/3.1/topics/forms/formsets/
As for attributes, it could be reduced to one line like this:
attributes_dict = dict(map(lambda x: x.split(':'), attributes))
To create multiple objects you should either iterate and create one object at a time or use bulk_create:
for name in names:
ProductVariant.objects.create(name=name,...)
Or
ProductVariant.objects.bulk_create([ProductVariant(name=name) for name in names])
Best practice for this is using bulk_create method.
product_variants = [ProductVariant(name=name) for name in names]
ProductVariant.objects.bulk_create(product_variants)

Django for loop save instance

I have a form that has two input fields which are returning the id values that will have to be parsed and added into the database.
This is the code
if form_stage_1.is_valid() and form_stage_2.is_valid():
# GET the new TP to pass to the next instance
form_stage_1.instance.created_by = self.request.user
new_tp = form_stage_1.save()
# Parse and add suppliers to the TP
for supplier_id in request.POST["supplier"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=supplier_id)
form_stage_2.instance.side = 1
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
# Parse and add clients to the TP
for client_id in request.POST["client"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=client_id)
form_stage_2.instance.side = 2
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
messages.success(request, "TP created successfully".format())
return redirect("add-tp")
Unfortunately is only adding one client.. why the form_stage_2.instance.save() is only working once?
What would be the most appropriate way to
First of all, do you have 2 forms to fill in ?
Anyway,
for supplier_id in request.POST["supplier"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=supplier_id)
form_stage_2.instance.side = 1
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
This won't work. You can't save a form instance multiple times I believe.
if form_stage_1.is_valid() and form_stage_2.is_valid():
form_stage_1.instance.created_by = self.request.user
new_tp = form_stage_1.save()
# Parse and add suppliers to the TP
for supplier_id in request.POST["supplier"]:
data = {
'counterpart':Counterpart.objects.get(pk=supplier_id),
'side':1
'transaction_proposal':[new_tp]
}
stage2_instance = Stage2ModelClass.objects.create(**data)
# Parse and add clients to the TP
for client_id in request.POST["client"]:
data = {
'counterpart':Counterpart.objects.get(pk=client_id),
'side':2
'transaction_proposal':[new_tp]
}
stage2_instance = Stage2ModelClass.objects.create(**data)
messages.success(request, "TP created successfully".format())
return redirect("add-tp")
Solution can be somewhat like this.
You can use,
for client_id in request.POST["client"]:
data = {
'counterpart':Counterpart.objects.get(pk=client_id),
'side':2,
'transaction_proposal':[new_tp]
}
form = FormClassStage2(data)
if form.is_valid():
form.save()
But I don't think you need to initialize a form here.

django get_or_create method always results in a new record

Model
class projects(models.Model):
"""Table that holds the details of the projects."""
toiName = models.CharField(max_length=100)
toiOwner = models.CharField(max_length=50)
receiver = models.CharField(max_length=50)
manager = models.CharField(max_length=50)
toiOwnerEmail = models.EmailField(max_length=70)
receiverEmail = models.EmailField(max_length=70)
managerEmail = models.EmailField(max_length=70)
dateUpdated= models.DateTimeField(default=datetime.today())
dateCreated = models.DateTimeField(default=datetime.today())
class Meta:
db_table="projects"
View, the original code to save the model works fine, when I go ahead and edit the form in the view, I always end up with a new record.
data = model_to_dict(projects.objects.filter(toiName=pid, managerEmail=request.user)[0])
if request.method == 'POST':
form = projectsForm(request.POST)
if form.is_valid():
#form = projectsForm(request.POST, instance=projects.objects.get(toiName=pid))
#obj = projects\
obj, created = projects.objects.get_or_create\
(toiName=request.POST['toiName'],
toiOwnerEmail=request.POST['toiOwnerEmail'],
toiOwner=request.POST['toiOwner'],
manager=request.POST['manager'],
receiver=request.POST['receiver'],
receiverEmail=request.POST['receiverEmail'],
dateUpdated=datetime.now(),
dateCreated=data['dateCreated'],
managerEmail=request.user,)
Here created always results in True.
At least this dateUpdated=datetime.now() causes get_or_create to always create new record, because each time datetime.now() is different.
I believe I was using the get_or_create incorrectly, since I was only trying to update the entry.
I fixed the code in the view with:
data = model_to_dict(projects.objects.filter(toiName=pid, managerEmail=request.user)[0])
proj = projects.objects.get(toiName=pid, managerEmail=request.user)
if request.method == 'POST':
form = projectsForm(request.POST)
if form.is_valid():
proj.toiName=form.cleaned_data['toiName']
proj.toiOwnerEmail=form.cleaned_data['toiOwnerEmail']
proj.toiOwner=form.cleaned_data['toiOwner']
proj.manager=form.cleaned_data['manager']
proj.receiver=form.cleaned_data['receiver']
proj.receiverEmail=form.cleaned_data['receiverEmail']
proj.dateUpdated=datetime.now()
#proj.dateCreated=data['dateCreated']
proj.save()
additional to #user1865366 answer, projects.objects.get should be enclose it with try ... except ... like so
try:
proj = Projects.objects.get(toiName=pid,manageEmail=request.user)
except Projects.DoesNotExist :
# do something create new proj and do something with the form
...
otherwise there will be big error screen when django cannot get the object

counting/filtering database-entries over multiple foreign key-relations

These are my DB-Models:
class Category(models.Model):
name = models.CharField(max_length = 20, unique = True)
...
class Feed(models.Model):
title = models.CharField(max_length = 100)
category = models.ForeignKey(Category)
...
class Article(models.Model):
title = models.CharField(max_length = 100)
read = models.BooleanField(default = False)
feed = models.ForeignKey(Feed)
...
Every Article belongs to one Feed (source) and each Feed is in a Category.
Now, i want to create a view to display all categories with some meta-information,
e.g. how many unread articles are in category x.
I tried things like this, but nothing worked:
categories = Category.objects.filter(feed__article__read=False)\
.annotate(Count('feed__article'))
What is the proper way to extract those information?
Especially if i want to add further information like: number of feeds in category and
number of favored articles in one QuerySet (If possible)...
Any ideas?
Thanks.
EDIT: Since i had no idea how to 'solve' this problem, i've written an ugly workaround:
result = categories.values_list('name',
'feed__title',
'feed__article__title',
'feed__article__read')
for i in range(0, len(result)):
#if pointer changed to a new category
#dump current dict to list and clear dict for the new values
if last != result[i][0]:
category_list.append(category_info.copy())
category_info.clear()
last = result[i][0]
if some values None:
insert values
elif some other values None:
insert values
else:
category_info['name'] = result[i][0]
category_info['feed_count'] = category_info.get('feed_count', 0) + 1
category_info['all_article_count'] = category_info.get('all_article_count', 0) + 1
#if a article has not been read yet
if result[i][3] == False:
category_info['unread_article_count'] = category_info.get('unread_article_count', 0) + 1
#if this category is the last in the result-list
if i+1 == len(result):
category_list.append(category_info.copy())
i += 1
I am pretty sure there is a quicker and nicer way to get those information, but at least i can work with it for the moment :/
You must label the information. You should be able to use category.article_count for the items in the queryset if you use the query below.
categories = Category.objects.filter(feed__article__read=False)\
.annotate(article_count=Count('feed__article'))

Categories

Resources