I'm creating a Django app, where users can create accounts for them and then add transactions to each account. Its part of a site that will allow users to track investment gains. I'm making use of the generic class views to achieve this.
Users add transactions to a specific account (code not shown) and then they must be able to view the transactions for that specific account using site/transactions/2/, where 2 is the account ID.
The problem is any logged in user can just change the url to get the transactions of another user's account. I can easily fix this by checking that the given account id belongs to the user, but I think there must be a better way. Are there better ways how I can achieve this? Should I perhaps not URL encode this? I'm not that fond of the idea of users seeing their account ids in the DB anyways.
Also, at a later stage I want to make it possible for the users to view some of their accounts' transaction in a single list. For instance, show all transactions for 3 of their 5 accounts. Then this URL method also won't really work. What other options do I have here?
In models.py I have:
class Account(models.Model):
name = models.CharField(max_length=40, unique=True, db_index=True)
user = models.ForeignKey(User)
class Transaction(models.Model):
value = models.DecimalField(max_digits=15, decimal_places=2)
date = models.DateField('transaction date')
account = models.ForeignKey(Account)
In urls.py:
url(r'^(?P<account_id>\d+)/$', views.IndexView.as_view(), name='index'),
In views.py:
class IndexView(LoginRequiredMixin, generic.ListView):
model = Transaction
def get_queryset(self):
account_id = self.kwargs['account_id']
queryset = Transaction.objects.filter(account_id=account_id)
return queryset
And then I have a transaction_list
template
Thanks
You could use the helper function get_object_or_404()
to first get the account object if you want to have a 404 error.
Like this:
def get_queryset(self):
account_id = self.kwargs['account_id']
# raise 404 if no account is found for the current user
account = get_object_or_404(Account, pk=account_id, user=self.request.user)
queryset = Transaction.objects.filter(account=account)
return queryset
For your second thing you mentioned, you could either make a new view, or just check if 'account_id'
was in the url, and reuse your current view. You will need a new url either way.
urls.py:
url(r'^(?P<account_id>\d+)/$', views.IndexView.as_view(), name='index'),
url(r'^$', views.IndexView.as_view(), name='index'),
modify your get_queryset()
again for the case where no account id is in the url:
def get_queryset(self):
# account_id will be None if the second url was the one that matched.
account_id = self.kwargs.get('account_id', None)
if account_id:
# raise 404 if no account is found for the current user
account = get_object_or_404(Account, pk=account_id, user=self.request.user)
queryset = Transaction.objects.filter(account=account)
else:
# we're going to show all transactions for the user, rather than a specific account
queryset = Transaction.objects.filter(account__user=self.request.user)
return queryset