1

This is a follow up question to this question: How to pass user object to forms in Django

Here is my form:

class SellForm(forms.Form):
    symbol = forms.ModelChoiceField(queryset=None, widget=forms.Select(attrs={
        'class': 'form-control',
        'placeholder': 'Symbol',
        'autofocus': 'autofocus',
        }))
    
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(SellForm, self).__init__(*args, **kwargs)
        self.fields['symbol'].queryset = Holdings.objects.filter(student=self.request.user.student)

This form is providing me with this dropdown: the example of the dropdown that this form creates

I have extra information in the model which is the purchase price of each share, is there a way to get this onto the form also? It would have to be non editable though as its the price that the share was purchased at.

Here is the model that the form is getting the data from:

class Holdings(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    symbol = models.CharField(max_length=10)
    purchase_price = models.DecimalField(max_digits=10, decimal_places=2)
    
    class Meta:
        verbose_name_plural = "Holdings"
        ordering = ['symbol']
    
    def __str__(self):
        return self.symbol

I would very much appreciate any help if possible! Thank you!

2 Answers2

2

The simplest way is just overriding the __str__ method and include the purchase price:

class Holdings(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    symbol = models.CharField(max_length=10)
    purchase_price = models.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        verbose_name_plural = "Holdings"
        ordering = ['symbol']

    def __str__(self):
        return f'{self.symbol} ({self.purchase_price})'

Filtering can be done more efficiently though with:

class SellForm(forms.Form):
    symbol = forms.ModelChoiceField(
        queryset=None,
        widget=forms.Select(
            attrs={
                'class': 'form-control',
                'placeholder': 'Symbol',
                'autofocus': 'autofocus',
            }
        ),
    )

    def __init__(self, *args, request, **kwargs):
        super(SellForm, self).__init__(*args, **kwargs)
        self.fields['symbol'].queryset = Holdings.objects.filter(
            student__user=self.request.user
        )

since this will prevent a query to get the Student object for the user, or we can work with:

class SellForm(forms.Form):
    symbol = forms.ModelChoiceField(
        queryset=None,
        widget=forms.Select(
            attrs={
                'class': 'form-control',
                'placeholder': 'Symbol',
                'autofocus': 'autofocus',
            }
        ),
    )

    def __init__(self, *args, request, **kwargs):
        super(SellForm, self).__init__(*args, **kwargs)
        self.fields['symbol'].queryset = Holdings.objects.filter(
            student_id=self.request.user.student_id
        )
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • This looks like probably a better and more readable way of doing it, thanks! Also thank you for the tip on filtering! – Jordan Dovey Jul 08 '23 at 19:48
0

I have managed to get it to work with some help from copilot.

it is one extra line of code so the form now looks like this:

class SellForm(forms.Form):
    symbol = forms.ModelChoiceField(queryset=None, widget=forms.Select(attrs={
        'class': 'form-control',
        'placeholder': 'Symbol',
        'autofocus': 'autofocus',
        }))
    
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(SellForm, self).__init__(*args, **kwargs)
        self.fields['symbol'].queryset = Holdings.objects.filter(student=self.request.user.student)
        self.fields['symbol'].label_from_instance = lambda obj: "%s ($%s)" % (obj.symbol, Holdings.objects.filter(student=self.request.user.student, symbol=obj.symbol).values_list('purchase_price', flat=True)[0])

Its a pretty hideous and long lambda function that I don't really understand, but I figured out how to access the purchase price from the view and then just tacked that part (the .values_list...) on to the lambda function and it worked. Below is what I've managed, which is pretty much what I wanted.

enter image description here

Thanks for taking a look and helping, hopefully this helps some others that may be stuck on this too!