Automatically change price when I change inventory in Django? - python

I am creating an invoice form.
I would like to have a function where if I change the inventory (dropdown search), the corresponding price will also change.
You can see an illustration of what I want here:
https://www.dropbox.com/s/4kpyfve3gzxmcls/dj006_ultimate_goal_for_adding_variable_attr.jpg?dl=0
What I know so far is I can I can query the inventory dropdown search through:
inventory = Inventory.objects.get(pk=request.POST['inventory_dropdown_id'])
price = inventory.price
But
how do I propogate such "get" function throughout all the forms (since I'm using query set)
how do I set the price (in the view.py) back to the html file when the html file is already rendered?
Is there away around this?
P.S. Probably been asked before here but I couldn't find any reference. (Hard to find what proper keyword to search) So if you want to post a link. I'd be happy to take a look at it.
================================================================
EDIT: Here is the model structure:
#MODELS.PY
class Invoice_Test_M2M(models.Model):
id = models.BigAutoField(primary_key=True)
ref_num = models.CharField(max_length=100)
def __str__(self):
return self.ref_num
class Inventory_Test_M2M(models.Model):
id = models.BigAutoField(primary_key=True)
inventory_name = models.CharField(blank=True, max_length=100)
invoice = models.ManyToManyField('Invoice_Test_M2M', through= "Invoice_Inventory_Through")
price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
def __str__(self):
return self.inventory_name
class Invoice_Inventory_Through(models.Model):
invoice = models.ForeignKey(Invoice_Test_M2M, on_delete=models.CASCADE)
inventory = models.ForeignKey(Inventory_Test_M2M, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
quantity = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
amount = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
#FORMS.PY
class Invoice_Inventory_ThroughForm(forms.ModelForm):
class Meta:
model = Invoice_Inventory_Through
exclude = ()
Inventory_TestLineItem_M2M_Formset = inlineformset_factory(Invoice_Test_M2M, Invoice_Inventory_Through, form=Invoice_Inventory_ThroughForm, fields = '__all__', exclude = [], can_delete = True)#
#VIEWS.PY
class Invoice_M2M_CreateView(CreateView):
model = Invoice_Test_M2M
success_url=reverse_lazy('music:album_list')
garbage = Invoice_Inventory_ThroughForm
garbage.price_val = 123
form = garbage
fields = '__all__'
def get_context_data(self, **kwargs):
context = super(Invoice_M2M_CreateView, self).get_context_data(**kwargs)
if self.request.POST:
context['track_formset'] = Inventory_TestLineItem_M2M_Formset(self.request.POST)
else:
context['track_formset'] = Inventory_TestLineItem_M2M_Formset()
context['price_val'] = 2
return context
def form_valid(self, form):
context = self.get_context_data(form=form)
formset = context['track_formset']
if formset.is_valid():
response = super().form_valid(form)
formset.instance = self.object
formset.save()
return response
else:
return super().form_invalid(form

This shall be done by Ajax and OnChange handler for the select. You create a view that you send it the id of product and it returns the price to ajax call and on the success of ajax you update the value of the price inputfield with the received price.

Related

Direct assignment to the forward side of a many-to-many set is prohibited. Use product.set() instead

i'm building a small webstore , in the product page i put the order form using FormMixin and TemplateView, when i submit the order i get a "Direct assignment to the forward side of a many-to-many set is prohibited. Use product.set() instead." error
Bellow you can check the code
Models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255, unique=True, )
description = models.TextField(max_length=1500)
class Meta:
verbose_name_plural = "categories"
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
nominal_price = models.PositiveIntegerField(verbose_name='prix normal',)
reduced_price = models.PositiveIntegerField(blank=True, null=True)
quantity = models.PositiveIntegerField(default=10)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
photo = models.ImageField(upload_to="img/products/", default="img/products/user_default.png")
def __str__(self):
return self.name
class Customer(models.Model):
full_name = models.CharField(max_length=150)
address = models.CharField(max_length=1500, null=True)
phone = models.IntegerField()
city = models.CharField(max_length=100)
email = models.EmailField(null=True)
class Order (models.Model):
product = models.ManyToManyField(Product, through='OrderProduct')
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
class OrderProduct(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
Views.py
class ProductDetailView(FormMixin, TemplateView):
model = Product
template_name = 'product.html'
form_class = OrderForm
def get_success_url(self):
return reverse('index')
def post(self, request, *args, **kwargs):
context = self.get_context_data()
form = OrderForm(request.POST)
if context['form'].is_valid():
product = get_object_or_404(Product, name=self.kwargs['product_name'])
customer = form.save()
Order.objects.create(product=product, customer=customer)
return super(TemplateView, self)
def get_context_data(self, **kwargs):
context = super(ProductDetailView, self).get_context_data(**kwargs)
context['product'] = Product.objects.get(name=self.kwargs['product_name'])
context['form'] = self.get_form()
return context
urls.py
path('', views.ProductListView.as_view(), name='index'),
Did i missed something
For handling many-to-many relations, you cannot directly set the product from Order. Also you would need to create the order first before you can set or add a product:
order = Order.objects.create(customer=customer)
order.product.add(product)

Django saving some fields to the DB but not others

I'm trying to save some data through a form and I'm trying to use a
FormSet. The data to be saved is an invoice which contains a Product
and it's Details.
I was able to render everything in one place (this wasn't simple) and
to save the invoice and the detail to the DB. For some reason, the
Detail table is saving the Product ID but not the Invoice ID. This is
my models.py:
class Detail(models.Model):
invoice = models.ForeignKey(Invoice, null=True, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
subtotal = models.DecimalField(max_digits=9 , null=True, decimal_places=2)
class Invoice(models.Model):
date = models.DateField(default=datetime.date.today)
number = models.CharField(max_length=10, default='0000000000')
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
products = models.ManyToManyField(Product, null=True, blank=True)
total = models.DecimalField(max_digits=9 , null=True, decimal_places=2)
And this is my views.py:
def invoice_new(request):
DetailFormSet = formset_factory(DetailForm, extra=2)
if request.method == "POST":
invoice_form = InvoiceForm(request.POST)
detail_formset = DetailFormSet(request.POST)
if invoice_form.is_valid() and detail_formset.is_valid():
invoice = invoice_form.save(commit=False)
invoice.save()
for details in detail_formset:
details.save()
return redirect('invoice_detail', pk=invoice.pk)
else:
return redirect('invoice_error')
else:
invoice_form = InvoiceForm()
detail_formset=formset_factory(DetailForm, extra=2)
return render(request, 'invoice/invoice_edit.html', {'invoice_form': invoice_form, 'detail_form': detail_formset} )
I tried adding this to the body of the for loop:
details.invoice_id = invoice.pk
invoice.id prints OK, but it won't save the number to the DB. I don't see how it picks up product id just fine but not invoice's.
I'm adding forms.py
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
fields = ['date','number','supplier']
total = forms.DecimalField(disabled=True)
class DetailForm(forms.ModelForm):
class Meta:
model = Detail
fields = ['product','quantity']
You can set the invoice id before saving the form.
if invoice_form.is_valid() and detail_formset.is_valid():
invoice = invoice_form.save(commit=False)
invoice.save()
for details in detail_formset:
details.invoice = invoice # Set the invoice
details.save()

Django M2M field in a Model Form?

I have a many to many field ConnectedTo in my model and I want to create the object using a form. However when I list it as a field I just get a listbox with options to highlight and no way of selecting one or more.
Ideally I'd love a multiple selection checkbox with a list of items in a scroll box. But I'd start with just having a selectable item.
Here's my code so far:
models.py:
class Part(models.Model):
PartID = models.AutoField(primary_key=True, unique=True)
SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True)
Comment = models.CharField(max_length=255, blank=True)
Subtype = models.ForeignKey('Subtype', on_delete=models.CASCADE, null=True)
Location = models.CharField(max_length=255, blank=True)
ConnectedTo= models.ManyToManyField('self', blank=True, null=True)
BatchNo = models.CharField(max_length=32, blank=False, null=True)
SerialNo = models.CharField(max_length=32,blank=True)
Manufacturer = models.CharField(max_length=32, blank=False, null=True)
Length = models.CharField(max_length=6, blank=True, null=True)
InspectionPeriod = models.IntegerField(blank=True, null=True)
LastInspected = models.DateField(blank=True, null=True)
InspectionDue = models.CharField(max_length=255, blank=True)
#classmethod
def create(cls, siteid, comment, subtype, location, batchno, serialno, manufacturer, length, inspectionperiod, lastinspected, inspectiondue):
part = cls(SiteID = siteid, Comment = comment, Subtype = subtype, Location = location, BatchNo = batchno, SerialNo = serialno, Manufacturer = manufacturer, Length = length, InspectionPeriod = inspectionperiod, LastInspected = lastinspected, InspectionDue = inspectiondue)
return part
def __str__(self):
return str(self.PartID)
forms.py:
class PartForm(forms.ModelForm):
class Meta:
model = Part
fields = ('Comment', 'Subtype', 'Location', 'ConnectedTo', 'BatchNo', 'SerialNo', 'Manufacturer', 'Length', 'InspectionPeriod', 'LastInspected')
views.py:
#login_required(login_url='/accounts/login/')
def addPartForm_Create(request, site, subtype):
siteselected = site
subtypeselected = Subtype.objects.get(SubtypeID = subtype)
if request.method == 'POST':
form = addPartForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.SiteID = Site.objects.get(SiteID = siteselected)
obj.Subtype = subtypeselected
obj.save()
return redirect('/sites/'+str(site))
else:
form = addPartForm()
return render(request, 'myproj/addPart.html', {'form': form, 'SiteNo': Site.objects.get(SiteID = siteselected).SiteID, 'subtype': subtypeselected})
EDIT: had the wrong view, sorry.
EDIT 2: example of what I mean by the highlighted box:
UPDATE:
Jey_Jen's answer has helped me get the style I want. I now have a multiple selection checkbox. But the ConnectedTo attributes do not save. Everything else in the model is saved and a new part is created. But no many to many links.
I would suggest looking into django form widgets. you can override the default widget to be a whatever you want. you can view them here.
heres a small example the django docs give:
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)

Perform update query after delete in django cbv

I'm trying to update other table after successful delete of of data. Below is my
views.py
class AttendeeDeleteView(DeleteView):
model = Attendee
success_url = reverse_lazy('index')
def get_form_kwargs(self):
id = self.kwargs['id'] # get value of enr
Payment.objects.filter(pk=id).update(registered=0)
In my urls.py
url(r'^delete/(?P<pk>\d+)$', AttendeeDeleteView.as_view(template_name="includes/attendee_delete.html"), name='attendee_delete',),
My Current code successfully delete the item but failed to update the other table.
my model
class Payment(models.Model):
id = models.AutoField(primary_key=True)
payee = models.CharField(max_length=200, blank=True, null=True)
contact = models.CharField(max_length=200,blank=True, null=True)
batch = models.CharField(max_length=200, null=True, blank=True)
ticketNumber = models.CharField(max_length=200)
ticketType = models.CharField(max_length=200, choices=TICKET_CHOICES, default='paid')
date = models.DateField('Date Paid (MM/DD/YYYY)', max_length=200, null=True, blank=True)
remarks = models.CharField(max_length=200 ,blank=True, null=True)
registered = models.BooleanField(default=False)
def __str__(self):
return self.payee
class Attendee(models.Model):
id = models.AutoField(primary_key=True)
payment = models.OneToOneField(Payment, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200)
batch = models.CharField(max_length=200, blank=True, null=True)
department = models.CharField(max_length=200, choices=DEPT_CHOICES, default='')
remarks = models.CharField(max_length=200, blank=True, null=True)
UPDATE 1:
I followed this came up with
def get(self, request, *args, **kwargs):
id = kwargs['pk'] # get value of enr
context = Payment.objects.filter(pk=id).update(registered=0)
return self.render_to_response(context)
but it returned context must be a dict rather than int.
I think def get_form_kwargs(self): doesn't executed, because it used for FormMixin. But, you can also handle it inside get() function.
def get(self, request, *args, **kwargs):
attendee = self.get_object()
Payment.objects.filter(pk=attendee.payment.pk).update(registered=0)
context = self.get_context_data(object=attendee)
return self.render_to_response(context)
See this https://ccbv.co.uk/projects/Django/1.11/django.views.generic.edit/DeleteView/ for more..
Edit:
This solution below if still getting a problem when using get() method.
from django.http import HttpResponseRedirect
....
def delete(self, request, *args, **kwargs):
"""
Calls the delete() method on the fetched object and then
redirects to the success URL.
"""
self.object = self.get_object()
success_url = self.get_success_url()
Payment.objects.filter(pk=self.object.payment.pk).update(registered=0)
self.object.delete()
return HttpResponseRedirect(success_url)

Showing form within another one

I want to show a form within another form.
For example I have these models:
class Measure(models.Model):
length = models.ForeignKey(Statistics, related_name='Length', null=True, blank=True)
surface_area = models.ForeignKey(Statistics, related_name='Surface area+', null=True, blank=True)
section_area = models.ForeignKey(Statistics, related_name='Section area+', null=True, blank=True)
volume = models.ForeignKey(Statistics, related_name='Volume', null=True, blank=True)
diameter = models.ForeignKey(Statistics, related_name='Diameter', null=True, blank=True)
class Statistics(models.Model):
total = models.FloatField('Total', null=True, blank=True)
avg = models.FloatField('Average', null=True, blank=True)
min = models.FloatField('Minimum', null=True, blank=True)
max = models.FloatField('Maximum', null=True, blank=True)
standard_deviation = models.FloatField('Standard deviation', null=True, blank=True)
and then I have these forms corresponding to the previous models:
class StatisticsForm(forms.ModelForm):
class Meta:
model = Statistics
fields = '__all__'
class MeasureForm(forms.ModelForm):
class Meta:
model = Measure
fields = '__all__'
# Here I have to say that the right form for each field is the StatisticForm
The ForeignKey in the forms is rendered as a combo box includes all the objects in the other table (in my case the Statistics table), I want to replace the combo box with an object of StatisticsForm so I can control the way I render the Statistics objects
Thank you very much.
Your database scheme and models are incorrectly designed to solve the problem at hand. You are defining a "has many" relationship in the wrong direction. One Measurement is supposed to have several Statistics, however one Statistics is not supposed to have many Measurement.
As your models are set up right now the ForeignKey is on the wrong side of the relationship. You should do this instead:
class Measure(models.Model):
def save(self,*args,**kwargs):
result = super(Measure, self).save(*args,**kwargs)
Statistics.objects.create(name='length', measurement=self)
Statistics.objects.create(name='section', measurement=self)
Statistics.objects.create(name='surface', measurement=self)
Statistics.objects.create(name='volume', measurement=self)
Statistics.objects.create(name='diameter', measurement=self)
return result
To provide the same comfort in accessing the Statisticss for one Measurement as in your current code you can add a couple of #property shortcuts:
class Measure(models.Model):
#property
def length(self):
return self.statistics_set.get(name='length')
#property
def section(self):
return self.statistics_set.get(name='section')
#property
def surface(self):
return self.statistics_set.get(name='surface')
#property
def volume(self):
return self.statistics_set.get(name='volume')
#property
def diameter(self):
return self.statistics_set.get(name='diameter')
class Statistics(models.Model):
name = models.CharField(max_length=10)
measurement = models.ForeignKey('Measurement')
total = models.FloatField('Total', null=True, blank=True)
avg = models.FloatField('Average', null=True, blank=True)
min = models.FloatField('Minimum', null=True, blank=True)
max = models.FloatField('Maximum', null=True, blank=True)
standard_deviation = models.FloatField('Standard deviation', null=True, blank=True)
Once you fix the relationship between the objects the problem becomes much easier to solve. Instead of being ForeignKeyFields in the form, the Statisticss become proper related objects, which are routinely handled by django.
As #solarisssmoke mentioned in the comments you are looking for formsets. Here is an example from the django documentation showing how to achieve what you need:
The models in question:
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
birth_date = models.DateField(blank=True, null=True)
def __str__(self): # __unicode__ on Python 2
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
And a view using inlineformset_factory to create the needed formset:
from django.forms import inlineformset_factory
def manage_books(request, author_id):
author = Author.objects.get(pk=author_id)
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
if request.method == "POST":
formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
if formset.is_valid():
formset.save()
# Do something. Should generally end with a redirect. For example:
return HttpResponseRedirect(author.get_absolute_url())
else:
formset = BookInlineFormSet(instance=author)
return render(request, 'manage_books.html', {'formset': formset})
If performance becomes an issue also have a look at prefetch_related for boosting performance.

Categories

Resources