简体   繁体   中英

Restricting access to Django generic views

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM