Search code examples
pythondjangodjango-formsdjango-widget

Change model form field to text input widget


I want to change the 'trans_recipient' field widget from being a dropdown, into being a text-input field. In the case of a large

I've tried the following :

class SafeTransactionForm(forms.ModelForm):
''' SafeTranSactionForm '''
trans_recipient = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
class Meta:
    model = SafeTransaction
    fields = [ 'trans_recipient',
                'subject',
                'arbitrator_name',
                'payment_condition',
                'amount_to_pay']

That produces this :

enter image description here

and although the widget itself has changed, actually trying to use it produces a value error as such : enter image description here

without my incorrect one-line attempt at changing the widget :

class SafeTransactionForm(forms.ModelForm):
''' SafeTranSactionForm '''
class Meta:
    model = SafeTransaction
    fields = [ 'trans_recipient',
                'subject',
                'arbitrator_name',
                'payment_condition',
                'amount_to_pay']

the dropdown:

enter image description here

I've tried playing around with syntax, trying to update the widget within the meta class, I've mostly ended up with syntax errors and I've not been able to find examples of this specific case after some Google searches.

Insight and explanations are very much welcome and appreciated.

EDIT:

Now I am getting an error : ValueError: invalid literal for int() with base 10: 'inbox'

Stack Trace : enter image description here

UpdateView class :

class SafeTransUpdateView(UpdateView):
'''
    This view lets the user Update a SafeTransaction receipt
    then send an automatic email to the email address
'''
form_class = SafeTransactionForm
model = SafeTransaction
template_name = "myInbox/safeTrans_update.html"

def __init__(self, *args, **kwargs):
    self.request = kwargs.pop('request', None)
    super(SafeTransUpdateView, self).__init__(*args, **kwargs)

def form_valid(self, form):
    trans = form.save(commit=False)
    trans.save()
   ### Send an email to the user upon transaction update.
    usr_obj = User.objects.get(customuser=trans.trans_recipient)
    user_mail = usr_obj.email
    from_email = 'timi.ogunkeye@gmail.com'
    contents = "This transaction [ " +trans.payment_condition+" ] has been updated !"
    email_subject = 'Transaction Update !'
    try:
        send_mail(email_subject, contents, from_email, [user_mail], fail_silently=False)
        pass
    except:
        pass
    else:
        pass
    return HttpResponseRedirect('inbox')

my updated form :

class SafeTransactionForm(forms.ModelForm):
''' SafeTranSactionForm '''
# trans_recipient = forms.CharField(widget=forms.TextInput(attrs={'class':'special'}))
trans_recipient = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
widget=forms.TextInput(attrs={'value':"username"}), to_field_name="username")

def clean_trans_recipient(self):
    data = self.cleaned_data['trans_recipient']
    try:
        return CustomUser.objects.get(username=data)
    except CustomUser.DoesNotExist:
        raise forms.ValidationError("No user with this username exists")

class Meta:
    model = SafeTransaction
    fields = [  'trans_recipient',
                'trans_recipient_email',
                'subject',
                'arbitrator_name',
                'payment_condition',
                'amount_to_pay']

safeTrans_update.html :

        <h1>TRANSACTION UPDATE: </h1>
        <form action="{% url 'ST_update' object.pk %}" method="post">{% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="Confirm Update" />   
        </form>

urls.py:

 path('ST_update/<pk>',SafeTransUpdateView.as_view(),name='ST_update'),

I'd like to know why this error is now occuring : ValueError: invalid literal for int() with base 10: 'inbox' . Any info is very much appreciated.


Solution

  • Well the problem is that Django does not know how to convert the data from the form field back into a trans_recipient attribute for the model (which should be a CustomUser instance).

    This is however not problematic: you can perform form cleaning, and thus write a custom function to transform the string back into a CustomUser object, for example:

    class SafeTransactionForm(forms.ModelForm):
        ''' SafeTranSactionForm '''
        trans_recipient = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    
        def clean_trans_recipient(self):
            data = self.cleaned_data['recipients']
            try:
                return CustomUser.objects.get(username=data)
            except CustomerUser.DoesNotExist:
                raise forms.ValidationError("No user with this username exists")
    
        class Meta:
            model = SafeTransaction
            fields = [ 'trans_recipient', 'subject', 'arbitrator_name',
                       'payment_condition', 'amount_to_pay' ]

    Although you will probably need to do some tweaking for the CustomUser fetch. We thus aim to fetch such CustomUser and return it. In case no such CustomUser exists, then we raise a ValidationError.