skip to Main Content

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!

3

Answers


  1. Chosen as BEST ANSWER

    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!


  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
            )
    Login or Signup to reply.
  3. You can add an extra field to a modelForm and make it uneditable.

    class SellForm(forms.ModelForm):
        purchase_price = forms.IntegerField()
    
        class Meta:
            model = Holdings
    
        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['purchase_history'].disabled= True
        
    

    A more elegant way, in my opinion, is to include the data in the context that’s sent to the form , and render that field alone separately in the form’s template.html page. i.e. send purchase_history as part of the context data to the view that renders the form .

    Check the answers here:

    1. In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?

    2. Django add extra field to a ModelForm generated from a Model

    3. https://docs.djangoproject.com/en/4.2/ref/forms/fields/#disabled

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search