pythondjangodatabaseformsrelation

How display categories in forms only for user who created this?


I am creating app for control transaction (expense, income, budget). I want each user to be able to create their own spending categories and their own expenses. All expenses and categories created by a user are to be visible only to that user.

If user A creates the category "Food123" then user B cannot see it. He can create his own such category.

I've created two models - Category and Expense.

class Category(models.Model):
    name = models.CharField(max_length=100)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="category")

class Expense(models.Model):
    name = models.CharField(max_length=100)
    amount = models.DecimalField(max_digits=8, decimal_places=2)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    date = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="expense")

I use a generic view when creating expenses and categories.

class ExpenseListView(LoginRequiredMixin, ListView):
model = Expense
context_object_name = 'expense'
template_name = 'expense/expense_list.html'

def get_queryset(self):
    return self.request.user.expense.all()

class ExpenseCreateView(CreateView):
    model = Expense
    success_url = '/record/expense'
    form_class = ExpenseForm

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.object.user = self.request.user
    self.object.save()
    return HttpResponseRedirect(self.get_success_url())

class CategoryCreateView(CreateView):
    model = Category
    success_url = '/record/expense'
    form_class = CategoryForm

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

What is more, I've used forms.py.

class ExpenseForm(forms.ModelForm):
    class Meta:
        model = Expense
        fields = ('name', 'amount', 'category')

class CategoryForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ('name',)

Unfortunetly, when user A create category "IT" it automatically goes to user B. When user B creates his expense he also sees this "IT" category that A created, not B.

How can I limit the display of categories in the form to only those created by a particular user?


Solution

  • The problem is because you're not filtering the model fields by your user in the ExpenseForm (like you are doing in the ExpenseListView.get_queryset).

    To do it, you will need to change a bit your logic. you can try something like this:

    # forms.py
    class ExpenseForm(forms.ModelForm):
        def __init__(self, user, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # limit the category field queryset
            self.fields['category'] = forms.ModelChoiceField(
                queryset=user.category.all())
            )
        class Meta:
            model = Expense
            fields = ('name', 'amount', 'category')
    
    # views.py
    class ExpenseCreateView(CreateView):
        ...
        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
            # inject the user in the form instantiation
            kwargs['user'] = self.request.user
            return kwargs