0

I have an Item object that has a manytomany relation to another object Option. I create a modelform from the Item object like so;

class Item(models.Model):
    category = models.ForeignKey(Category)
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
    options = models.ManyToManyField(Option)
class OptionForm(ModelForm):
    options = forms.ChoiceField(widget=forms.RadioSelect())
    class Meta:
        model = Item
        fields = ( 'options', )

When i render the form in the template it renders all available options for the Item object(the expected behavior) even those not created by a specific item. I want to be able to load options defined by the specific Item that will be chosen by the user. How do i override the form to achieve such behavior.for example without a form i can render an Items own Options through its id. item = Item.objects.get(pk=id)

Suziemac Tani
  • 455
  • 9
  • 23
  • Can you clarify your question? Do you want to dynamically implement a form as defined by the options a user wants? or only put a particular choice set in the model as defined by the user? – agconti Aug 26 '13 at 12:54
  • I think the question is fine @agconti,when creating an Item, i also specify the `options` that belong to that Item. – Suziemac Tani Aug 26 '13 at 13:05
  • Why do you want to limit the options? – agconti Aug 26 '13 at 13:12
  • an example an item like a coffee may have options such as big or small and item like burrito may have options like steak or chicken. When i render the form it renders all the options, meaning coffee will have options like big,small,steak and chicken which is not right. – Suziemac Tani Aug 26 '13 at 13:17
  • 2
    Have a look here: http://stackoverflow.com/questions/9743103/django-how-to-limit-field-choices-in-formset. Maybe this leads into the right direction. – Jingo Aug 26 '13 at 13:19
  • @jingo, that link led to the right direction.. – Suziemac Tani Aug 26 '13 at 18:35

3 Answers3

0

Its tough to make ModelForm's defined on the fly because they are intimately tied to the structure in your model. Nevertheless you could use some clever template control flow and view rendering to get your desired effect. This is untested so you millage with this might vary.

<form method="post" action="">
    {{ formset.management_form }}
    {% for form in formset %}
        {{ form.id }}
        <ul>
     {% if user_option.category %}
            <li>{{ form.caregory }}</li>
     {% endif %}
     {% if user_option.name %}
            <li>{{ form.name }}</li>
     {% endif %}
     {% if user_option.p_opt %}
            <li>{{ form.price }}</li>
            <li>{{ form.options }}</li>
     {% endif %}
        </ul>
    {% endfor %}
</form> 

From the Djano docs here.

agconti
  • 17,780
  • 15
  • 80
  • 114
0

Try overriding the form's init method and passing in the Item pk as an additional argument. The trick here is to pop the argument before calling the parent init.

class ItemOptionsForm(forms.ModelForm):
    class Meta:
        model = Item

    def __init__(self, *args, **kwargs):
        # pop 'item_id' as parent's init is not expecting it
        item_id = kwargs.pop('item_id', None)

        # now it's safe to call the parent init
        super(ItemOptionsForm, self).__init__(*args, **kwargs)

        # Limit options to only the item's options
        if item_id:
            try:
                item = Item.objects.get(id=item_id)
            except:
                raise ValidationError('No item found!')

            self.fields['options'] = forms.ChoiceField(item.options)

Then, in your view, create the form like:

form = ItemOptionsForm(item_id=item_id)

The nice thing about this is that you can raise ValidationErrors that will show up in the form.

Be aware that this doesn't prevent someone from POSTing option IDs to your form which do not belong to the Item, so you'll likely want to override the ModelForm.clean() to validate the options as well.

Fiver
  • 9,909
  • 9
  • 43
  • 63
0

learning from link django: How to limit field choices in formset? provided by @jingo, i solved the problem by first of all creating dynamic form like so;

def partial_order_item_form(item):
    """dynamic form limiting optional_items to their items"""
    class PartialOrderItemform(forms.Form):
        quantity = forms.IntegerField(widget=forms.TextInput(attrs={'size':'2', 'class':'quantity','maxlength':'5'}))
        option = forms.ModelChoiceField(queryset=OptionalItems.objects.filter(item=item),widget= forms.RadioSelect())

    return PartialOrderItemform

then validating form like so;

def show_item(request,id):
    option = get_object_or_404(Item,pk=id)
    if request.method == 'POST':
        form = partial_order_item_form(option)
        #bound form to POST data, 
        final_form = form(request.POST)
        # check validation of posted data
        if final_form.is_valid():
            order.add_to_order(request)
            url =urlresolvers.reverse('order_index',kwargs={'id':a.id})
            # redirect to order page
            return HttpResponseRedirect(url)
    else:
        form = partial_order_item_form(item=id)
    context={
        'form':form,
    }
    return render_to_response('item.html',context,context_instance=RequestContext(request))
Community
  • 1
  • 1
Suziemac Tani
  • 455
  • 9
  • 23