0

we are currently building a cryptocurrency market (university project)

Each user has different cryptocurrencies in his "Wallet"

The wallet should show the sum (+ and - transactions) for each of the different cryptocurrencies.

For the moment we are only able to render a list which doesn't group the different currencies (those who have the same name)

We tried the sum and annotate functions. By doing so we aren't able to display the currency name. We get only the currency id

Here is our models file

class Currency(models.Model):
    currency_name = models.CharField(max_length=40, unique=True)
    symbol = models.ImageField(blank=True)
    ticker = models.CharField(max_length=10)
    created_at = models.DateTimeField(auto_now_add = True, null=True)

class Transaction(models.Model):
    listing_id = models.ForeignKey(Listing, on_delete=models.CASCADE, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add = True)
    exchange_amount = models.IntegerField(null=True)
    user_debit = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_id_debit', null=True)
    user_credit = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_id_credit', null=True)
    currency_id = models.ForeignKey(Currency, on_delete=models.CASCADE, null=True)

And our views.py

#profile page
@login_required
def profile(request):
    wallet = Transaction.objects.filter(Q(user_credit=request.user) | Q(user_debit=request.user)).order_by('-created_at')
    transactions = Transaction.objects.filter(Q(user_credit=request.user) | Q(user_debit=request.user)).order_by('-created_at')[:10]
    listings = Listing.objects.filter(user=request.user).order_by('-created_at')[:10]

    args = {'wallets': wallet, 'transactions': transactions, 'listings': listings}
    return render(request, 'profile.html', args)

<!--This is our custom head title for the different urls. The base.html is loaded after this.-->
<head>
    <title>SolvayCoin - Profile</title>
</head>

{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div class="jumbotron">
        <div class="container">
          <h1 class="display-4">{{ user.first_name }} {{ user.last_name }}'s profile</h1>
          <p>Username is <pan style="font-style: italic">{{ user.username }}</pan> and joined SolvayCoin {{ user.date_joined|timesince }} ago</p>
        </div>
      </div>

      <div class="container extra-padding">

        <hr>

        <div class="row">
          <div class="col-md-4">
            <h2>My wallet</h2>
            <ul class=list-unstyled >
              {% for wallet in wallets %}
              <li>{{ wallet.currency_id }}: {{ wallet.exchange_amount__sum|floatformat:"2" }}</li>
              {% endfor %}
            </ul>
          </div>
          <div class="col-md-4">
            <h2>My transactions</h2>
              {% if transactions %}
              <ul class=list-unstyled >
              {% for transactions in transactions %}
                <li>
                  {% if user == transactions.user_credit %}
                    (+)
                  {% elif user == transactions.user_debit %}
                    (-)
                  {% endif %}
                  {{ transactions.currency_name_fixed }}{{ transactions.exchange_amount|floatformat:"2" }}
                  with {% if user == transactions.user_credit %}{{ transactions.user_debit }}{% elif user == transactions.user_debit %}{{ transactions.user_credit }}{% endif%}
                </li>
              {% endfor %}
              </ul>
              {% else %}
                <p>No transactions are available.</p>
              {% endif %}
          </div>
          <div class="col-md-4">
            <h2>My listings</h2>
              {% if listings %}
              <ul class=list-unstyled >
              {% for listings in listings %}
                <li>Listing <!--{{ listings.id }}--> created {{ listings.created_at|date:'d/m/Y' }} at {{ listings.created_at|date:'H:i' }}</li>
              {% endfor %}
              </ul>
              {% else %}
                <p>No listings are available.</p>
              {% endif %}
          </div>
        </div>

      {% endif %}

        <hr>

      </div> <!-- /container -->
{% endblock %}

1 Answers1

0

A django queryset solution seems tricky; since debit/credit resolution is also required here.

If performance and doing it in DB are not a major concern; I would do it in python as follows:

wallet_transactions = Transaction.objects.filter(Q(user_credit=request.user) | Q(user_debit=request.user)).order_by('-created_at').select_related('currency_id')
wallet = get_wallet(request.user, wallet_transactions)


def get_wallet(user, wallet_transactions):
    wallet = defaultdict(int)
    for txn in wallet_transactions:
        if user == txn.user_credit:
            wallet[txn.crrency_id.currency_name] += txn.exchange_amount
        elif user == txn.user_debit:
            wallet[txn.crrency_id.currency_name] -= txn.exchange_amount
        else:
            # Error; invalid transaction
            pass
    return wallet
    # This will return something like this: {'SBS': 300, 'BTC': 121000}

I'd also think that there will be more business logic that gets added to get_wallet which definitely makes sense to be kept in the business layer of your application (python).

In your html; you can iterate over the wallet contents and print as follows:

<ul>
{% for currency_name, amount in wallet.items %} 
  <li>{{currency_name}} - {{amount}}</li>
 {% endfor %}
</ul>

In your specific case; change this snippet:

    <ul class=list-unstyled >
      {% for currency_name, amount in wallet.items %}
      <li>{{ currency_name }}: {{ amount|floatformat:"2" }}</li>
      {% endfor %}
    </ul>
rtindru
  • 5,107
  • 9
  • 41
  • 59
  • thank you for your answer. We are completely to django and are not sure where/how to implement this code. We have already the "def profile" (see views.py) linked with our url.py. I've pasted the updated views.py file in my question above. Thx for your help –  May 15 '18 at 18:09
  • Just create a `helpers.py` or `utils.py` file in your app; and define the `get_wallet` function there. – rtindru May 15 '18 at 18:10
  • Your template code will also have to change to support the new `wallet` being a dict instead of a django queryset. – rtindru May 15 '18 at 18:10
  • ok great, we created a helpers.py and linked it with the views.py. No errors but as you said we have to change the html template to display the elements. But we don't know how to change the new wallet to being a dict instead of a queryset. thx a lot –  May 15 '18 at 18:26
  • there you go. I posted the html code of the profile page –  May 15 '18 at 18:31
  • Updated; also I'd encourage you to spend some time trying to solve this on your own. A quick search on stackoverflow or django docs can surface this info for you: https://stackoverflow.com/questions/1275735/how-to-access-dictionary-element-in-django-template – rtindru May 15 '18 at 18:34