Как у вас есть единый выбор для выбора двух разных слабо связанных моделей в форме django

#django #django-templates #django-forms

#django #django-templates #django-forms

Вопрос:

У меня есть эти модели:

 OrderLine
StockItem
WaybillItem
Waybill
  

Waybill Состоит из нескольких WaybillItem , связанных с одним номером заказа.

WaybillItem Состоит из OrderLine amp; a StockItem Отношение между StockItem amp; Orderline является свободным многие ко многим, зависящим от запроса из данных OrderLine :

  StockItem.objects.filter( wh_code = OrderLine.origin_wh_code ).filter( si_code = OrderLine.si_code ).filter( commodity_code = OrderLine.commodity_code )
  

В форме я хотел бы, чтобы у пользователя был единый выбор для выбора возможных вариантов, где для каждого совпадающего StockItem / OrderLine есть один элемент.

Пример: В OrderLines есть следующее OrderLines :

 Line_ID, OrderNumber, commodity_code, si_code, origin_wh_code
1001,1, Wheat, 2222,Rome
1002,1, Oat, 2222, Rome
1003,2, Oat, 2222, Rome
  

В StockItem у вас есть:

 Stock_ID, commodity_code, si_code, wh_code, reference_code
10, Wheat, 2222, Rome, 222201
12, Oat, 2222, Rome, 222202
13, Wheat, 2222, Rome, 222203
14, Wheat, 2222, Paris, 222203
  

Это должно привести к выпадающему списку для waybillline для waybill с номером 1 с:

 Line_ID, Stock_ID, (commodity_code, si_code, reference_code)
1001,  10, (Wheat, 2222, 222201 
1001,  13, (Wheat, 2222, 222203)
1002,  12, (Oat,2222, 222202)
  

При выборе в Waybill строке должны быть сохранены оба Line_ID amp; Stock_ID , но пользователь использует только один выпадающий список.
Я использую inlineformset_factory для создания списка строк wabill.

Кто-нибудь может помочь?

Отредактировано добавление представления моделей и шаблона

Модели: накладная

 class Waybill( models.Model ):
    ltiNumber = models.CharField( max_length = 20 )
    waybillNumber = models.CharField( max_length = 20 )
    dateOfLoading = models.DateField( null = True, blank = True )
    recipientLocation = models.CharField( max_length = 100, blank = True )
    destinationWarehouse = models.ForeignKey( Places, blank = True )
  

Порядок

 class OrderLine( models.Model ):
    lti_pk = models.CharField( max_length = 50, primary_key = True, db_column = 'LTI_PK' )
    lti_id = models.CharField( max_length = 40, db_column = 'LTI_ID' )
    code = models.CharField( max_length = 40, db_column = 'CODE' )
    origin_wh_code = models.CharField( max_length = 13, blank = True, db_column = 'ORIGIN_WH_CODE' )
    destination_location_code = models.CharField( max_length = 10, db_column = 'DESTINATION_LOCATION_CODE' )
    si_code = models.CharField( max_length = 8, db_column = 'SI_CODE' )
    commodity_code = models.CharField( max_length = 18, db_column = 'COMMODITY_CODE' )
    number_of_units = models.DecimalField( max_digits = 7, decimal_places = 0, db_column = 'NUMBER_OF_UNITS' )
  

Это стандартная модель

 class StockItem( models.Model ):
        wh_pk = models.CharField( max_length = 90, blank = True, primary_key = True )
        wh_code = models.CharField( max_length = 13 )
        wh_name = models.CharField( max_length = 50, blank = True )
        si_code = models.CharField( max_length = 8 )
        origin_id = models.CharField( max_length = 23 )
        commodity_code = models.CharField( max_length = 18 )
        number_of_units = models.IntegerField()
  

Это WaybillItem

 class WaybillItem( models.Model ):
        wbNumber = models.ForeignKey( Waybill )
        siNo = models.ForeignKey( OrderLine )
        coi_code = models.ForeignKey( StockItem )
        numberUnitsLoaded = models.DecimalField( default = 0, blank = False, null = False, max_digits = 10, decimal_places = 3 )
  

Вид:

 def waybillCreate( request, lti_code ):
   #Retrive the LTIs for this Waybill
   current_lti = OrderLine.objects.filter( code = lti_code )
    for lti in current_lti:
        c_sis.append( lti.si_code )

    current_stock = StockItem.in_stock_objects.filter( si_code__in = c_sis ).filter( wh_code = current_lti[0].origin_wh_code )

    class LoadingDetailDispatchForm( ModelForm ):
        #Here are the order lines
        siNo = ModelChoiceField( queryset = current_lti, label = 'Commodity' )
        #This is from the Stock
        coi_code = ModelChoiceField( queryset = current_stock )
        overload = forms.BooleanField( required = False )
        class Meta:
            model = LoadingDetail
            fields = ( 'siNo', 'numberUnitsLoaded', 'wbNumber', 'overloadedUnits', 'overOffloadUnits' , 'coi_code' )

    LDFormSet = inlineformset_factory( Waybill, LoadingDetail, form = LoadingDetailDispatchForm, fk_name = "wbNumber", formset = BaseLoadingDetailFormFormSet, extra = 5, max_num = 5 )
    current_wh = ''
    if request.method == 'POST':
        form = WaybillForm( request.POST )
        form.fields["destinationWarehouse"].queryset = Places.objects.filter( geo_name = current_lti[0].destination_loc_name )
        formset = LDFormSet( request.POST )
        if form.is_valid() and formset.is_valid():
            wb_new = form.save()
            instances = formset.save( commit = False )
            wb_new.waybillNumber = new_waybill_no( wb_new )
            for subform in instances:
                subform.wbNumber = wb_new
                subform.save()
            wb_new.save()
            return HttpResponseRedirect( '../viewwb/'   str( wb_new.id ) )
        else:
            loggit( formset.errors )
            loggit( form.errors )
            loggit( formset.non_form_errors )
    else:
        qs = Places.objects.filter( geo_name = current_lti[0].destination_loc_name ).filter( organization_id = current_lti[0].consegnee_code )
        if len( qs ) == 0:
            qs = Places.objects.filter( geo_name = current_lti[0].destination_loc_name )
        else:
            current_wh = qs[0]
        form = WaybillForm( 
            initial = {
                    'dispatcherName':      request.user.profile.compasUser.person_pk,
                    'dispatcherTitle':      request.user.profile.compasUser.title,
                    'ltiNumber':         current_lti[0].code,
                    'dateOfLoading':     datetime.date.today(),
                    'dateOfDispatch':    datetime.date.today(),
                    'recipientLocation': current_lti[0].destination_loc_name,
                    'recipientConsingee':current_lti[0].consegnee_name,
                    'transportContractor': current_lti[0].transport_name,
                    'invalidated':'False',
                    'destinationWarehouse':current_wh,
                    'waybillNumber':'N/A'
                }
        )
        form.fields["destinationWarehouse"].queryset = qs

        formset = LDFormSet()
    return render_to_response( 'waybill/createWaybill.html', {'form': form, 'lti_list':current_lti, 'formset':formset}, context_instance = RequestContext( request ) )
  

Шаблон (часть):

 <table cellpadding="1px" cellspacing="1px" id="example" style="border:1px;">
    <thead>
        <tr><th></th><th>Commodity</th><th>{%if lti_list.0.is_bulk%}Metric Tons{%else%}Number of Units{%endif%}</th><th>Overload</th></tr>
    </thead>
    {% for sform in formset.forms %}
    <tr>
        <td>{{ sform.id }}{{forloop.counter}}</td>
        <td>{{ sform.siNo }} {{sform.coi_code}}</td>
        <td>{{ sform.numberUnitsLoaded }} {%for error in sform.numberUnitsLoaded.errors%}<span class='error'> {{error|escape}}</span>{%endfor%}</td>
        <td>{{ sform.overloadedUnits}}</td>
    </tr>
    {% endfor %}
</table>
  

Комментарии:

1. Как правило, более полезно включать упрощенную версию кода, который вы используете для своих моделей, вместо перечисления их взаимосвязей.

2. там, где выпадающие списки являются динамическими, я использую jQuery для управления списками. На мой взгляд, это быстрее и понятнее. Вот ссылка для начала: webcloud.se/log/AJAX-in-Django-with-jQuery Также начал использовать шаблоны jQuery для форматирования, а не делать это в django.

Ответ №1:

Прежде всего, вам следует провести рефакторинг ваших моделей. Создать хранилище моделей:

 class WareHouse(models.Model): 
     code = models.CharField...
  

Свяжите OrderItem и StockItem со складом с помощью внешних ключей вместо ‘origin_wh_code’ и ‘wh_code’. Только тогда вы сможете выбирать данные с помощью объединений.

Используйте поле выбора с пользовательскими вариантами или даже лучше — создайте унаследованный от форм.Поле TypedChoiceField, которое инкапсулирует логику сериализации (товар на складе, элемент заказа) pks.

 choices = StockItem.objects.filter(commodity_code=F("warehouse__orders__commodity_code"))
        .values('pk', 'warehouse__orders__pk')# or , <other required fields>)

item = CustomChoiceField(choices= choices, required=True)
  

После публикации вы могли бы получить пару:

 stock_item, order_item = form.cleaned_data['item']
#Save'em here