Bank ATM ledger in Django – – deposit, withdrawal, balance

Issue

I’m trying to build a rudimentary interactive ATM machine ledger prototype demo in Python with Django.

Here is what my web app currently looks like.

The intended basic functionality I am trying to create right now is to have the web visitor (banking client) enter the amount of cash in the input field and then click: “Deposit” or ”Withdraw”. Say for example the client deposits $20.00, the amount should appear in the HTML table under the “Credits” column and the “Running Balance” column should reflect that as well, like anyone would expect a typical ATM and accounting ledger to behave. The end goal is to have unique timestamps and transaction IDs to generate with each transaction, but for now I am focused on just getting the ‘running balance’ variable (rightmost column) to update when the web visitor inputs an amount and clicks “Deposit”. Currently, when a web visitor enters $20.00 as the amount in the box and then clicks “Deposit”, since I’ve got a print statement inside my views.py, the Django terminal shows: <QueryDict: {'csrfmiddlewaretoken': ‘<redacted>'], 'amount': ['20.00'], 'transaction': ['Deposit']}>. So an amount is being accepted and processed. But the Running Balance remains $0.00, which is what I am trying to solve.

forms.py:

from django import forms
 
class AmountForm(forms.Form):
   amount = forms.DecimalField(label='Amount', max_digits=10, decimal_places=2)
   # deposit = forms.DecimalField(label='Deposit', max_digits=10, decimal_places=2)
   # withdraw = forms.DecimalField(label='Withdraw', max_digits=10, decimal_places=2)

views.py:

from django.shortcuts import render
from django.http import HttpResponseRedirect,HttpResponse
from .models import Account, Transactions
# Create your views here.
from .forms import AmountForm
 
def index(request):
   # Starting balance variable initialization:
   balance = 0
   context = {'balance': balance}
   # Import `Account` model data:
   data = Account.objects.all().order_by('-inception_date')
   # If this is a POST request we need to process the form data:
   print(request.POST)
   if request.method == 'POST':
       # Create a form instance and populate it with data from the request:
       form = Transactions(request.POST)
       # Check whether it's valid:
       if form.is_valid():
           # Process the data in form.cleaned_data as required:
           amount = form.cleaned_data['amount']
           if request.POST['transaction'] == 'Deposit':
               balance = balance + amount
               context.update({'balance': balance,})
           if request.POST['transaction'] == 'Withdraw':
               balance = balance - amount
               context.update({'balance': balance,})
           # Redirect to a new URL:
           return render(request, 'telagents/home.html', {'form': form, 'data':data, 'context': context,})
 
   # If a GET (or any other method) we'll create a blank form:
   else:
       form = AmountForm()
 
   return render(request, 'telagents/home.html', {'form': form, 'data':data, })

models.py:

from django.db import models
from datetime import datetime
# from pytz import timezone
import decimal
from decimal import Decimal
from random import randint
from django.forms import ModelForm
from telagents.forms import AmountForm
 
class Account(models.Model):
   #### Static data elements :
   interest = models.DecimalField(max_digits=6, decimal_places=3) # Percent
   inception_date = models.DateTimeField('Client since (inception date)')
   first_name = models.CharField(max_length=30)
   last_name = models.CharField(max_length=30)
   account_number = models.BigIntegerField()
   #### Interactive and dynamic data points :
   debit = models.DecimalField(max_digits=12, decimal_places=2)
   credit = models.DecimalField(max_digits=12, decimal_places=2)
   balance = models.DecimalField(max_digits=12, decimal_places=2)
   amount = models.DecimalField(max_digits=12, decimal_places=2)
   trans_timestamp = models.DateTimeField(auto_now=False, auto_now_add=False)
   trans_id = models.BigIntegerField()
 
   def client_since(self):
       # a = self.pub_date.timezone.now("US")
       # b = pytz.timezone("US")
       return self.inception_date.strftime("%A %d %B %Y @ %I:%M:%S %p")
  
   def __str__(self):
       return f"{self.first_name} {self.last_name}'s bank account."
 
class Transactions(ModelForm):
   class Meta:
       model = Account
       fields = ['amount',]

As you can see at the bottom of the models.py above, I attempt to use a ModelForm. The only field is amount. For the model class variable, I use Account which in turn inherits the properties of the Account class defined above it through polymorphism. But this isn’t really what I am trying to do. Instead I wish to actually inherit the amount property defined in the AmountForm class as defined in forms.py. But when I change Account to AmountForm, Django throws an error saying : AttributeError: type object 'AmountForm' has no attribute '_meta'

templates/home.html:

 <body>
  {% block content %}

    <br><br>

{% for data_obj in data %}
    <center>
    Client Name : {{data_obj.first_name}} {{data_obj.last_name}}
        <br>
    Bank Account Number : {{data_obj.account_number}}
        <br>
    Client Since : {{ data_obj.inception_date }}
        <br>        
    Interest Rate : {{data_obj.interest}} %
    </center>
{% endfor%}

<br><br>
<center>
<form action="{% url 'index' %}" method="post">
   {% csrf_token %}
   {{ form }}
 <input type="submit" value="Deposit" name="transaction" >
 <input type="submit" value="Withdraw" name="transaction">
</form>
</center>
 
<center>
<table class="GeneratedTable">
  <thead>
   <tr>
     <th>Type</th>
     <th>Timestamp</th>
     <th>Trans ID #</th>
     <th>Merchant</th>
     <th>Debits</th>
     <th>Credits</th>
     <th>Running Balance</th>
   </tr>
 </thead>
  <tbody>
   <tr>
   {% for trans_objs in data %}
     <td>(null)</td>           
     <td>{{ trans_objs.trans_timestamp }}</td>     
     <td>{{ trans_objs.trans_id }}</td>     
     <td>Big Bank ATM</td>
     <td>{{ trans_objs.debit }}</td>
     <td>{{ trans_objs.credit }}</td>     
     <td>{{ trans_objs.balance }} </td>
   {% endfor %}    
   </tr>
 </tbody>
  <tbody>
   <tr>
   {% for trans_objs in context %}
     <td>(null)</td>           
     <td>(null)</td>     
     <td>(null)</td>     
     <td>ATM</td>
     <td>(null)</td>
     <td>(null)</td>     
     <td>{{ trans_objs.amount }} </td>
   {% endfor %}    
   </tr>
 </tbody>
 
</table>
</center>

{% endblock %}

</body>

As you can see here, I’ve got two rows within Jinja for loops. The first one just positions static information. Those are mostly just placeholders. All that data showing is entered in the Admin Dashboard. It’s static. In the second row is my best attempt at extrapolating the balance variable from the context dictionary in the views.py dynamically. But it doesn’t work. It’s empty as you can see in the screenshot I shared way at the top of this question.

What do I need to change in my views.py to get Django to accept user form input in my template to complete the simple mathematical operation of adding the amount to the balance entered by the user and then having it updated in the HTML table?

Resources I’ve used so far:

Solution

Here is my latest views.py function:

def index(request):
   balance = 0
   context = {'balance': balance}
   data = Account.objects.all() 
   myaccount = Account.objects.get(id=1)
   print(request.POST)
   if request.method == 'POST':
       form = Transactions(request.POST)
       if form.is_valid():
           print(form)
           amount = form.cleaned_data['amount']
           if request.POST['transaction'] == 'Deposit':
               balance = balance + amount
               context.update({'balance': balance,})
           if request.POST['transaction'] == 'Withdraw':
               balance = balance - amount
               context.update({'balance': balance,})
           myaccount.balance = balance
           myaccount.save()
           return render(request, 'telagents/home.html', {'form': form, 'data':data, 'context': context,})
 
   else:
       form = AmountForm()
 
   return render(request, 'telagents/home.html', {'form': form, 'data':data, })

The crucial changes above is the instantiation of the Account class object during initialization with the .get query set to the only id row entry in the db. I then refer to this object towards the end of the algorithm where I apply the balance input collected from the POST request to the balance attribute and then save it to myaccount.

To quote my original question:

Instead I was intending to have the balance change dynamically every time a web visitor makes a deposit or withdrawal.

So I accomplished what I intended to in my question above. By that I mean, when a web visitor lands on the web page now and they enter a dollar amount and click “Deposit”, the balance field in the ledger updates to reflect the amount entered.

The problem to solve next is that my views.py function algorithm doesn’t track a running tally with subsequent deposits. When a web visitor enters a second deposit amount, it just overwrites the former balance. The next step for me to take is to fix this.

I had help from a friend to come up with my revised views.py but I refined it further based on a free mini crash course which teaches how to first use a GUI sqlite browser utility and then proceeds to cover querying db.sqlite3 from Python REPL integrated with a basic Django test project. The course refers to Django docs:

Answered By – Angeles89

Answer Checked By – Senaida (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.