how to make a form that will affect 2 models? should this use a property method?[SOLVED]

hello if i had model like this

class Customer(models.Model):
    name = models.CharField(max_length=200,null= True)
    phone = models.CharField(max_length=200,null= True)
    email = models.CharField(max_length=200,null= True)
    date_created = models.DateTimeField(auto_now_add= True,null = True)
   
    def __str__(self):
        return self.name

class Tag(models.Model):
    name = models.CharField(max_length=200,null= True)
    
    def __str__(self):
        return self.name

class Item(models.Model):
    CATEGORY = (
        ('Gudang Kering', 'Gudang Kering'),
        ('Gudang Basah','Gudang Basah'),
        )
    name = models.CharField(max_length=200,null= True)
    stock = models.IntegerField(default='0', blank=False, null=True)
    category = models.CharField(max_length=200,null= True,choices=CATEGORY)
    reorderlevel = models.IntegerField(default='0', blank=False, null=True)
    maxreorderlevel = models.IntegerField(default='0', blank=False, null=True)
    description = models.CharField(max_length=200,null= True, blank= True)
    date_created = models.DateTimeField(auto_now_add= True)
    tags = models.ManyToManyField(Tag)
    
    def __str__(self):
        return self.name

class Issue(models.Model):
    STATUS = (
        ('Pending', 'Pending'),
        ('Granted','Granted'),
        ('Denied','Denied'),
        )
    customer = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
    item = models.ForeignKey(Item, null=True, on_delete= models.SET_NULL)
    quantity = models.IntegerField(default='0', blank=False, null=True)
    date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
    status = models.CharField(max_length=200,null= True, choices=STATUS)

    def __str__(self):
        return str(self.quantity)+' '+str(self.item)+' '+self.status + '  ' +str(self.customer)
        #print(def__str__(self)) 
        
class Receive(models.Model):
    STATUS = (
        ('Pending', 'Pending'),
        ('Granted','Granted'),
        ('Denied','Denied'),
        )
    customer = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
    item = models.ForeignKey(Item, null=True, on_delete= models.SET_NULL)
    quantity = models.IntegerField(default='0', blank=False, null=True)
    date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
    status = models.CharField(max_length=200,null= True, choices=STATUS)

    def __str__(self):
        return str(self.quantity)+' '+str(self.item)+' '+self.status + '  ' +str(self.customer)
def updateIssue(request, pk):
    issue = Issue.objects.get(id=pk)
    item = Item.objects.all()
    form = UpdateIssueForm(instance=issue)
    if request.method == 'POST':
        form = UpdateIssueForm(request.POST,instance=issue)
        #print ('printing:',request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            if instance.status == 'Granted':
                item.stock -= instance.quantity
                instance.save()
            else:
               instance.save()

        return redirect('/')

in this case i want to make it so data from model item.stock which is an int will be reduced by
instance.quantity which is an int data from model Issue if status ==“Granted”

What I Tried
I did this succesfully if the model is only 1(stock and quantity_issue in same model)
using instance.stock -= instance.quantity
How do i make this work if it is from 2 different models?
Some Ideas

  1. I read that i can solve this by using properthy method but i have never used it much less with an if else condition
  2. I can get it to update the quantity if it is in the same model instance,if i can somehow get an instance2 the surely i can do a simple plus or minus after save right?

I’m not quite following what exactly you’re trying to accomplish here - or what the particular issue is - so all I can do at the moment is make some guesses.

  • There’s no reason to do an item = Item.objects.all() I don’t think you want to do this for all items, just the item related to the instance.

  • You’re creating the form twice. form = UpdateIssueForm(instance=issue) followed by form = UpdateIssueForm(request.POST, instance=issue). You only need to do the former if you’re not handling a POST. (And you don’t need to add the instance parameter if the original form was created from the instance.

  • You don’t need to use the commit=False pattern - regardless of instance.status, you’re still saving instance.

  • I think you want to retrieve just the one Item related to the Issue object being updated, so you might want to try something like this. (Off-the-cuff, very possibly a syntax error or two here.)

if request.method == 'POST':
    form = UpdateIssueForm(request.POST)
    if form.is_valid():
        instance = form.save()
        if instance.status == 'Granted':
            instance.item.stock -= instance.quantity
            instance.item.save()

Hi sorry,i just learned django so i don’t know how to put this into word
what i want to do is that to be able to affect the value of stock in tabel item


class Item(models.Model):
    CATEGORY = (
        ('Gudang Kering', 'Gudang Kering'),
        ('Gudang Basah','Gudang Basah'),
        )
    name = models.CharField(max_length=200,null= True)
    stock = models.IntegerField(default='0', blank=False, null=True)
    category = models.CharField(max_length=200,null= True,choices=CATEGORY)
    reorderlevel = models.IntegerField(default='0', blank=False, null=True)
    maxreorderlevel = models.IntegerField(default='0', blank=False, null=True)
    description = models.CharField(max_length=200,null= True, blank= True)
    date_created = models.DateTimeField(auto_now_add= True)
    tags = models.ManyToManyField(Tag)

and tabel Issue

class Issue(models.Model):
    STATUS = (
        ('Pending', 'Pending'),
        ('Granted','Granted'),
        ('Denied','Denied'),
        )
    customer = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
    item = models.ForeignKey(Item, null=True, on_delete= models.SET_NULL)
    quantity = models.IntegerField(default='0', blank=False, null=True)
    date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
    status = models.CharField(max_length=200,null= True, choices=STATUS)

in this case My Goal is to be able to affect the stock value in tabel Item with minus quantity value from table issue(IF status–‘granted’ and plus quantity value from tabel receive (IF status=‘granted’ status from the model tabel

before this i was able to affect a tabel but this is only with one model where
stock,quantity_issue,quantity_receive was in the same model in model Stock
(unlike now in different model connected with foreign key)

def acceptstock(request, pk):
	queryset = Stock.objects.get(id=pk)
	form = PengeluaranForm(request.POST or None, instance=queryset)
	if form.is_valid():
		instance = form.save(commit=False)
		instance.stock-= instance.quantitas_issue += instance.quantitas_receive
		instance.created_by = str(request.user)		

		instance.save()

		return redirect('/detail_stok/'+str(instance.id))
		# return HttpResponseRedirect(instance.get_absolute_url())

	context = {
		"title": 'Accept ' + str(queryset.item_name),
		"queryset": queryset,
		"form": form,
		"username": 'granted_by: ' + str(request.user),
	}
	return render(request, "add_item.html", context)

what i want to know how do i go about this goal? im sorry
i also read about a property method being used to automatically get the real time value for this kind of thing,but i have never used it nor i understand when i have read the documentation for python of @property
I am sorry my english is not the best

Does the example I posted in my previous response do what you’re looking for? If not, what is it not doing that you need?

(Side note - @property is almost never needed for anything in Django. Some people like how it lets them organize their code, but that’s a personal code-style preference and not a requirement.)

ah yes,

if request.method == 'POST':
    form = UpdateIssueForm(request.POST)
    if form.is_valid():
        instance = form.save()
        if instance.status == 'Granted':
            instance.item.stock -= instance.quantity
            instance.item.save()

in this case i thought instance is from a queryset yes? how do i set it so, that like you do the instance called model from model item and issue
(an answer from a comment someone said(in other website that i should use instance 2 - instance1?? how do i get this??)
or this instance.item.stock will automatically call stock from item?,sorry i’m on the road and can’t try it on my laptop and thank you for answering

if request.method == 'POST':
    form = UpdateIssueForm(request.POST)
    if form.is_valid():
        instance = form.save()
        if instance.status == 'Granted':
            instance.item.stock -= instance.quantity
            instance.item.save()

No, instance is from your form.save() in the previous line. When you save a ModelForm, the form returns the instance of the model saved.

Assuming UpdateIssueForm is a ModelForm for type Issue, then instance is an Issue - specifically the one saved by the form.

Issue.item is an FK to Item. So instance.item is a reference to the specific Item referenced by that Issue. Therefore, instance.item.stock is a reference to the value in Item for the Item referenced by that Issue.

1 Like

Thank you and Forgive me if i understand wrong,so this is my understanding of your answer,
BEFORE
,i thought that since i add item and add issue with different 1 from formadditem form
with item model and 1 from formissueitem form.py with model issue
I should have to have 2 instances,
AFTER
so with this it should be ok if i get that instance from issue.id right(the instance value will be from form issue item and since it is a child of item and had foreign key to item i should be just fine)

Well, that depends upon whether you’re talking about one page with both forms on that page and both forms being submitted at the same time, or two different pages where you’re submitting one and then the other.
You’ve only shown where you’re only submitting one form from your view.

Yes, your reference would be correct.