Arxas
Arxas

Reputation: 111

How to limit field choices related to another table in database?

I'm working on simple expenses manager. Every user is able to add/remove/edit an Operation, which represents expense or earning. However I have noticed a bug - while adding new Operation it is possible to choose other users Accounts and Categories.

Here is my Operation model:

class Operation(models.Model):

    def get_category_color(self):
        return Category.objects.get(name=self.category).color

    types_tuples = ((-1, 'expense'), (1, 'earning'))

    user = models.ForeignKey(User)
    account = models.ForeignKey(Account)
    category = models.ForeignKey(Category)
    date = models.DateField()
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    type = models.IntegerField(choices=types_tuples)
    currency = models.CharField(max_length=3)

    color = property(get_category_color)

OperationCreate view:

class OperationCreate(CreateView, OperationMixIn):

    model = Operation
    form_class = OperationForm
    success_url = reverse_lazy('manage_operations')

    def form_valid(self, form):
        operation = form.save(commit=False)
        operation.currency = Account.objects.get(pk=form.instance.account_id).currency
        self.update_account_balance(form)
        form.instance.user = self.request.user
        return super(OperationCreate, self).form_valid(form)

and OperationForm:

class OperationForm(ModelForm):
    class Meta:
        model = Operation
        fields = ['account', 'type', 'category', 'date', 'amount']

The question is how can I limit choices for Account and Category that are available during posting new Operation? I would like user to see only those which are related to his/her account.

I tried limit_choices_to as a parameter for models.ForeignKey(Account) and models.ForeignKey(Category) in Operation model but couldn't make it work that way.

I assume I need to use a query which will return only Accounts and Categories related to the current user. However I have no clue how and where should I apply it.

Could you please point me in the right direction?

EDIT:

OperationForm edited due to @efkin suggestion:

class OperationForm(ModelForm):
    class Meta:
        model = Operation
        fields = ['account', 'type', 'category', 'date', 'amount']

    def __ini__(self, user, *args, **kwargs):
        super(OperationForm, self).__init__(*args, **kwargs)    
        self.fields['account'] = ModelChoiceField(queryset=Account.objects.filter(user=user))
        self.fields['category'] = ModelChoiceField(queryset=Category.objects.filter(user=user))

Upvotes: 0

Views: 150

Answers (2)

Arxas
Arxas

Reputation: 111

Okay, so I did some more digging and came up with the solution. OperationForm must be modified using ModelChoiceFiled (as @efkin said) and OperationCreate should contains override for get_form_kwargs method from ModelFormMixIn:

class OperationCreate(CreateView, OperationMixIn):

    model = Operation
    form_class = OperationForm
    success_url = reverse_lazy('manage_operations')

    def get_form_kwargs(self):
        kwargs = super(OperationCreate, self).get_form_kwargs()
        kwargs.update({'user': self.request.user})
        return kwargs

    def form_valid(self, form):
        operation = form.save(commit=False)
        operation.currency = Account.objects.get(pk=form.instance.account_id).currency
        self.update_account_balance(form)
        form.instance.user = self.request.user
        return super(OperationCreate, self).form_valid(form)

Upvotes: 0

user4691348
user4691348

Reputation:

You could use a simple Form with ModelChoiceField fields for foreign keys.

Upvotes: 1

Related Questions