Search code examples
djangodjango-filters

How to limit choices on the Django filter based on the request.user


I have a multi company setup. The user is logged into to a specific company. Every company has its own set of ledger accounts and transactions. On the transaction list I created filter options so that the user can filter based on "Description", "Transaction date" and "Ledger account". The problem with the Ledger Account is that the choices available is the accounts from all the companies. I ant to limit the choices to only show the Ledger Accounts from the current company that the user is logged into.

User model:

class custom_user(AbstractBaseUser):
    email                   = models.EmailField(verbose_name="email", max_length=60, unique=True)
    username                = models.CharField(max_length=30, unique=True)
    date_joined             = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
    last_login              = models.DateTimeField(verbose_name='last login', auto_now=True)
    is_admin                = models.BooleanField(default=False)
    is_active               = models.BooleanField(default=True)
    is_staff                = models.BooleanField(default=False)
    is_superuser            = models.BooleanField(default=False)
    company_group           = models.CharField(max_length=6, null=True)
    current_company         = models.ForeignKey(tcompany, on_delete=models.CASCADE, null=True)

Note the current_company as a ForeignKey.

Leger Account Model:

class tledger_account(models.Model):
    id = models.AutoField(primary_key=True)
    slug = models.SlugField(max_length=30, null=True)
    description = models.CharField(max_length=30, unique=False)
    gl_category = models.CharField(max_length=30, choices=category_choices, verbose_name='category', db_index=True)
    note = models.CharField(max_length=25, blank=True, default=None)
    active = models.BooleanField(default=True)
    company = models.ForeignKey(tcompany, on_delete=models.PROTECT)
    class Meta:
        indexes =[
            models.Index(fields=['company', 'description'])
        ]
        unique_together = ['company', 'description']

Note the company as a ForeignKey.

Views.py:

class TransactionlistView(ListView):
    model = ttransactions
    template_name = 'accounting/transaction_list.html'

    def get_form_kwargs(self):
        kwargs = super(TransactionlistView, self).get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs

    def get_context_data(self, **kwargs):
        context = super(TransactionlistView, self).get_context_data(**kwargs)

        line_list = []
        context = {}
        balance = 0
        transactions = ttransactions.objects.order_by('transaction_date'). \
            filter(company=self.request.user.current_company)
        company = self.request.user.current_company

        transaction_count = transactions.count()
        myFilter = transactionFilter(self.request.GET, queryset=transactions, **kwargs)
        transactions = myFilter.qs
        transaction_lines_count = 0

        for transaction in transactions:
            transaction_lines = ttransaction_lines.objects.filter(transaction=transaction.id)

            transaction_lines_count = transaction_lines.count()
            mylineFilter = lineFilter(self.request.GET, queryset=transaction_lines)
            transaction_lines = mylineFilter.qs

            for transaction_line in transaction_lines:
                if transaction_line.transaction_type == 'Debit':
                    balance += transaction_line.amount
                else:
                    balance -= transaction_line.amount

                line = {
                    'transaction': transaction.id,
                    'description': transaction.description,
                    'transaction_date': transaction.transaction_date,
                    'sequence': transaction_line.sequence,
                    'transaction_type': transaction_line.transaction_type,
                    'ledger_account': transaction_line.ledger_account,
                    'amount': transaction_line.amount,
                    'balance': balance,
                }
                line_list.append(line)

        paginator = Paginator(line_list, 20)
        page = self.request.GET.get('page')
        try:
            line_list = paginator.page(page)
        except PageNotAnInteger:
            line_list = paginator.page(1)
        except EmptyPage:
            line_list = paginator.page(paginator.num_page)

        context = {'line_list': line_list, 'myFilter': myFilter, 'transactions': transactions,
                   'transaction_count': transaction_count, 'transaction_lines_count': transaction_lines_count,
                   'mylineFilter': mylineFilter, 'transaction_lines': transaction_lines, 'page': page}

        return context

Filter.py:

class lineFilter(django_filters.FilterSet):
    legder_account = django_filters.ModelChoiceFilter(queryset=tledger_account.objects.filter(
        company=request.user.current_company))

    class Meta:
        model = ttransaction_lines
        fields = '__all__'
        exclude = ['transaction', 'sequence', 'quantity', 'posted', 'transaction_type', 'company', 'ledger_account']

This setup gives me an error: "name 'request' is not defined" The question is how do I get the request.user to the filter.py


Solution

  • You can try like this(based on example from documentation):

    def accounts(request):
        if request is None:
            return tledger_account.objects.none()
        return tledger_account.objects.filter(company=request.user.current_company))
    
    class lineFilter(filters.FilterSet):
        legder_account = filters.ModelChoiceFilter(queryset= accounts)
    
    # usage
    my_filter = transactionFilter(self.request.GET, queryset=transactions, request=self.request)
    

    FYI, class names should be CamelCase and method names should be snake_case as per PEP-8 style guide.