0

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.

Azer
  • 1,200
  • 13
  • 23
timi95
  • 368
  • 6
  • 23

1 Answers1

2

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Hey, this exact thing now throws this error before updating : `invalid literal for int() with base 10: 'inbox' ` Am I supposed to be casting something in the Form or UpdateView in order to solve this. Any help at all is greatly appreciated. – timi95 Jan 19 '19 at 16:14
  • 1
    I'm quite sure this has nothing to do with this form. I propose you ask a new question with the relevant models, forms, views, etc. and the *full* traceback. Just the error message does not say much, since that error could originate from everywhere. – Willem Van Onsem Jan 19 '19 at 16:16
  • I recently asked a question on this, but I suppose it is a bit broad. Will try to narrow it down and see if anyone would answer. I've searched around, this error seem fairly generic as you say, I'm not too sure where it could be from considering this. – timi95 Jan 19 '19 at 16:18
  • 1
    @timi95: typically this is an error that is generated when you filter a queryset or create an object, where you pass a string for a `ForeignKey`. So something like `Group.objects.filter(user='foo')`, which makes no sense, since `user` should be a `User`, and `'foo'` is a string, not a user. – Willem Van Onsem Jan 19 '19 at 16:20
  • Yes technically I am doing this I suppose because I am converting the default dropdown widget into a TextInput widget. My clean method looks as this though : `CustomUser.objects.get(username=data)` where "data" is : `data = self.cleaned_data['trans_recipient']` Just as you answered to me that one time. – timi95 Jan 19 '19 at 17:06
  • 1
    But `username` is not a `CharField` here? – Willem Van Onsem Jan 19 '19 at 17:08
  • I guess it probably isn't and I've been over looking it maybe . Is there a way to cast it as such, so I can check if this is the issue ? – timi95 Jan 19 '19 at 17:12
  • will this work : `CustomUser.objects.get(username=str(data))` ? edit : didn't work. – timi95 Jan 19 '19 at 17:13
  • 1
    @timi95: no, what exactly is `username` here, that is the key. If it is a `ForeignKey`, than we need to query with a `JOIN`. – Willem Van Onsem Jan 19 '19 at 17:15
  • I see. CustomUser inherits that username field from django.contrib.auth.models' `User`. I looked in the definition and that username seems to be a string . – timi95 Jan 19 '19 at 17:18
  • @timi95: yes, then I guess the error is *not* located here. But you can update the question with the *full* traceback, such that we can verify that? – Willem Van Onsem Jan 19 '19 at 17:19
  • Okay, I will get that done in a few minutes probably. I'll try to be verbose about it. – timi95 Jan 19 '19 at 17:22
  • Hi, I've amended my original question. Would you mind at all taking a quick look ? – timi95 Jan 19 '19 at 17:58
  • @timi95: it looks like you defined a class-based view, for an URL with a `pk` attribute, but that `pk` attribute is not an int-pattern (it is here `inbox`). Now some views (like an `UpdateView`, etc.), see `pk` as a special URL variable: the primary key of the object to update, and since a `pk` is in most cases an `int`, here it does not "add up". So I guess something is wrong with that URL pattern (or at least how you handle this). – Willem Van Onsem Jan 19 '19 at 18:11
  • So this is the line in url.py : `path('ST_update/',SafeTransUpdateView.as_view(),name='ST_update'),`, and This is the update button that leads me to the update form : ` ` – timi95 Jan 19 '19 at 18:19
  • 1
    Well the `pk` can only have an `int`, so you can "restrict" the path, by writing `path('ST_update/', ...)`, and then look where exactly the invalid URL is generated. The `int:` part will ensure that from now on, this view will only fire with a `pk` that is of shape `[0-9]+`. – Willem Van Onsem Jan 19 '19 at 18:20
  • I **seem** to have fixed my issue simply by adding ` \ ` to my `HttpResponseRedirect(' \inbox ')` in the form_valid() method. I will still include `` in order to be more explicit. – timi95 Jan 19 '19 at 18:58