How to use TextInput widget with ModelChoiceField in Django forms?
<p>In Django, the <code>ChoiceField</code> or <code>ModelChoiceField</code> represent the data in a <code>Select</code> widget. This is helpful when the list of objects is small. However it is extremely difficult to manage (for the end-user) if the number of objects are in thousands.</p>

<p>To eliminate the said problem I'd like the end-users to manually enter the field value in an input box of type text (i.e, via <code>TextInput</code> widget).</p>

<hr>

<p>So far, I have created the below code. As <code>ModelChoiceField</code> has <code>Select</code> widget by default; It behaves in similar manner as before even after changing the widget to <code>TextInput</code>. It expects a <code>pk</code> or <code>id</code> value of the model object and thus raising an error : </p>

<blockquote>
  <p><em>Select a valid choice. That choice is not one of the available choices.</em></p>
</blockquote>

<p>However, I'd want the end-user to enter <code>sku_number</code> field in the input box rather than the <code>pk</code> or <code>id</code> of the object. What is the correct way to solve this problem?</p>

<p><strong>models.py</strong></p>

<pre><code>class Product(models.Model):
    sku_number = models.CharField(null=False, unique=True)
    product_name = models.CharField(null=Flase)

    def __str__(self):
        return self.sku_number
</code></pre>

<p><strong>forms.py</strong></p>

<pre><code>class SkuForm(forms.Form):
    sku_number = forms.ModelChoiceField(queryset=Product.objects.all(), 
                                        widget=forms.TextInput())
    extra_field = forms.CharField(required=True)
</code></pre>

<hr>

<p><strong>Note</strong> : I did try another approach to solve this problem. By displaying only last 10 objects by slicing the number of objects; this ensures that the select box is not flooded with thousands of items.</p>

<pre><code>queryset=Product.objects.all().order_by('-id')[:10]
</code></pre>

<p>The latter methodology if correctly implemented would work with my particular use-case, however others might be interested in the former method. The above statement further raised errors because of Django's limitation with generating SQL statements.</p>

<blockquote>
  <p>Also note that even though slicing an unevaluated <strong>QuerySet</strong> returns another unevaluated <strong>QuerySet</strong>, modifying it further (e.g., adding more filters, or modifying ordering) is not allowed, since that does not translate well into SQL and it would not have a clear meaning either.</p>
  
  <p>Source : <a href="https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated" rel="nofollow">Django Docs</a></p>
</blockquote>

<p>In my case, I had to change the <code>sku_number</code> field to Product object's <code>id</code>. This had to be done before <code>clean()</code>. As seen in <a href="http://stackoverflow.com/a/11083656/433587">this answer</a>, <code>__init__()</code> should be used to modify data before it reaches <code>clean()</code>.</p>

<pre><code>class SkuForm(forms.Form):
    sku_number = forms.ModelChoiceField(queryset=Product.objects.all(), 
                                        widget=forms.TextInput())
    extra_field = forms.CharField(required=True)

    def __init__(self, data=None, *args, **kwargs):
        if data is not None:
            data = data.copy() # make it mutable
            if data['sku_number']:
                obj = get_object_or_404(Product, batch_name=data['sku_number'])
                data['sku_number'] = obj.id
        super(SkuForm, self).__init__(data=data, *args, **kwargs)
</code></pre>

python django forms