Django. Как показать только связанные варианты в отношениях двух таблиц?

Почему это работает? Это жестко закодировано с contrato_id = 1, но цель динамически представляет связанные варианты, а не полную модель таблицы.

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'].queryset=FaturaBlog.objects.filter(id_contrato=1).all()

Но ни один из них не работает?

a)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'].queryset = FaturaBlog.objects.filter(id_contrato=self.object.pk).all()

Тип исключения: AttributeError Значение исключения: объект 'LancamentoBlogForm' не имеет атрибута 'object'

b)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.id_fatura_choices = [*forms.ModelChoiceField(FaturaBlog.objects.filter(id_contrato=self.object.pk).all().choices)]

Тип исключения: AttributeError Значение исключения: объект 'LancamentoBlogForm' не имеет атрибута 'object'

c)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'] = FaturaBlog.objects.filter(id_contrato=contratoblog__id).all()

Тип исключения: NameError Значение исключения: имя 'contratoblog__id' не определено

d)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'] = super(LancamentoFormset, self).get_context_data()

Тип исключения: AttributeError Значение исключения: объект 'super' не имеет атрибута 'get_context_data'

BUT У меня есть контекст, так как return self.render(context) возвращает данные для base.html

context [{'True': True, 'False': False, 'None': None}, {}, {}, {'object': <ContratoBlog: Terceiro Contrato Blog>, 'contratoblog': <ContratoBlog: Terceiro Contrato Blog>, 'form': <django.forms.formsets.LancamentoBlogFormFormSet object at 0x107cfb0d0>, 'view': <blog.views.ContratoLancamentoEdit object at 0x107cfb0a0>, 'contrato_dados': <ContratoBlog: Terceiro Contrato Blog>, 'fatura_list': <QuerySet [<FaturaBlog: c.3.vct.2022-07-15>, <FaturaBlog: c.3.vct.2022-08-15>, <FaturaBlog: c.3.vct.2022-09-15>]>, 'lancamento_list': <QuerySet [<LancamentoBlog: L7.2022-02-01>, <LancamentoBlog: L8.2022-04-01>, <LancamentoBlog: L9.2022-06-01>]>}] 

e)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'] = FaturaBlog.objects.filter(id_contrato = lancamentoblog__id_contrato).all()

Тип исключения: NameError Значение исключения: имя 'lancamentoblog__id_contrato' не определено

f)

    class LancamentoFormset(forms.ModelForm):
        def __init__(self, *args, **kwargs):
        self.fields['id_fatura'] = FaturaBlog.objects.prefetch_related('id_contrato').all()
        #neither  select_related or prefetch_related works
        #self.fields['id_fatura'] = FaturaBlog.objects.select_related('id_contrato').all()

ОШИБКИ НЕТ. BUT queryset не возвращает никаких результатов, поле в форме пусто.

Я искал и тестировал в течение нескольких дней. Дело вот в чем: поле выбора связано с моделью, которая имеет два ключа, и желаемый результат - показать только варианты выбора из связанного контракта, предварительно определенного. Если параметры не заданы, поле выбора работает нормально, но показывает все записи, а не только связанные записи.

<
    CONTRATO
    ID [ 1,2,3,4,5,6,...]
    
    FATURA
    ID  ID_CONTRATO
    1   1
    2   1
    3   1
    4   2
    5   2
    6   3
    7   3
    
    
    LANCAMENTO
    ID      ID_CONTRATO     ID_FATURA
    1       3               choices should be [6,7]
    2       3               choices should be [6,7]
    3       3               choices should be [6,7]
    4       2               choices should be [4,5]
    5       2               choices should be [4,5]
    6       1               choices should be [1,2,3]
    7       1               choices should be [1,2,3]
<

Модель CONTRATO регистрирует все контракты

Модель FATURA регистрирует все счета-фактуры и имеет ForeignKey с CONTRATO в отношении один-ко-многим (поэтому один контракт имеет много счетов-фактур)

Модель LANCAMENTO регистрирует записи из договоров, которые позже будут присвоены счету-фактуре в FATURA. Таким образом, LANCAMETNO имеет ForeingKey с CONTRATO в отношении on-to-many (поэтому один контракт имеет много записей в LANCAMENTO). И LANCAMENTO также имеет ForeignKey с FATURA, который по умолчанию равен null, поэтому позже FATURA будет присвоена этой записи в LANCAEMTNO.

Цель состоит в том, чтобы иметь эту логику в форме. Таким образом, когда пользователь обращается к LANCAMETNO для назначения FATURA, он может выбрать только FATURA с таким же id контракта, как у LANCAMETNO.

Я здесь много исследовал, но это все, до чего я могу дойти. Я застрял.

Вот код, если кто-то может направить меня в правильном направлении.

Вот мой код для Models:

    from django.db import models
    from django.urls import reverse, reverse_lazy
    
    class ContratoBlog(models.Model):
        nome_contrato = models.CharField(max_length=100)
        
        def __str__(self):
            return f"{self.nome_contrato}"
        def get_absolute_url(self):
            return reverse('blog:detalhe_contrato', kwargs={'pk' : self.pk})
    
    
    class FaturaBlog(models.Model):
        datavencimento = models.DateField(null=True, blank=True)
        status = models.CharField(max_length=50, null=True, blank=True)
        id_contrato = models.ForeignKey(ContratoBlog, on_delete=models.CASCADE)
    
        def __str__(self):
            return f"F.{self.id}.V.{self.datavencimento}"
    
    
    class LancamentoBlog(models.Model):
        id_contrato = models.ForeignKey(ContratoBlog, on_delete=models.CASCADE)
        datalancamento = models.DateField(null=True, blank=True)
        detalhe = models.CharField(max_length=100, null=True, blank=True)
        id_fatura = models.ForeignKey(FaturaBlog, on_delete=models.CASCADE, blank=True, null=True)
       
        def __str__(self):
            return f"L{self.id}.{self.datalancamento}"

Вот мой код для Views:

Вот мой код для Fomrs.py

    from importlib.metadata import MetadataPathFinder
    from django.forms import BaseInlineFormSet
    from django import forms
    from django.forms.models import inlineformset_factory
    from blog.models import ContratoBlog, FaturaBlog, LancamentoBlog
    
    class FooModelChoiceField(forms.Form):
        foo_select = forms.ModelChoiceField(queryset=None)
    
        def __init__(self, *args, **kwargs):
            super(FooModelChoiceField, self).__init__(*args, **kwargs)
            qs = FaturaBlog.objects.filter(fatura__id_contrato=self.id_contrato)
            self.fields['foo_select'].queryset = qs
    
    ContratoBlogFaturaBlogFormset = inlineformset_factory(
        ContratoBlog, FaturaBlog, 
        fields=('datavencimento','status', 'id_contrato')
        )
    
    ContratoBlogLancamentoBlogFormset = inlineformset_factory(
        ContratoBlog, LancamentoBlog, 
        fields=('datalancamento', 'detalhe', 'id_fatura')
        )

Вот мой код для urls.py

    from django.urls import path, re_path
    from blog.views import HomeView, ContratoBlogDetailView, ContratoBlogFaturaBlogEditView, ContratoBlogLancamentoBlogEditView
    
    app_name = 'blog'
    
    urlpatterns = [
        path('', HomeView.as_view(), name='home'),
        #re_path(r'^contrato/(?P<pk>\d+)$', ContratoBlogDetailView.as_view(), name='detalhe_contrato'),
        path('contrato/<int:pk>', ContratoBlogDetailView.as_view(), name='detalhe_contrato'),
        path('contrato/<int:pk>/fatura/edit/', ContratoBlogFaturaBlogEditView.as_view(), name='contrato_fatura_edit'),
        path('contrato/<int:pk>/lancamento/edit/', ContratoBlogLancamentoBlogEditView.as_view(), name='contrato_lancamento_edit'),

Шаблон 1 - Показывает все FATURA и все LANCAMETNO, связанные с CONTRATO:

{% extends 'base.html' %}

{% block content %}
{% include 'blog/contrato_nav.html' %}

<p>
    Contrato ({{contratoblog}}). {{contratoblog.nome_contrato}}.  <BR>
</p>

<p><hr>
    Faturas: <a href="{{ contratoblog.get_absolute_url }}/fatura/edit/">[Editar]</a> <BR>
    <table>
    {% for fatura in fatura_list %}
    <tr>
        <td>[{{ fatura.id }}]</td> <td>Vct. {{ fatura.datavencimento|date:"d M y" }} </td><td>{{ fatura }}</td>  
    </tr>
    {% endfor %}
    </table>
</p>
<p><hr>
    Lan&ccedil;amentos: <a href="{{ contratoblog.get_absolute_url }}/lancamento/edit/">[Editar]</a> <BR>
    <table>
    {% for lancamento in lancamento_list %}
    <tr>
        <td> {{ lancamento.datalancamento|date:"d-M-y" }} </td> <td>{{ lancamento.detalhe }}. </td> <td>{{ lancamento.id_fatura }}</td>
    </tr>
    {% endfor %}
    </table>
</p>
{% endblock %}

Шаблон 2 - показывает все LANCAMETNO, связанные с CONTRATO, но ДОЛЖЕН показывать только FATURA, вместо того, чтобы показывать все записи в fatura.

{% extends 'base.html' %}

{% block content %}

<h2>Lan&ccedil;amentos:</h2>

<hr>
  <form action="" method="post" >
    {% csrf_token %}
    {{ form.non_form_errors }}

    {% for hidden_field in form.hidden_fields %}
      {{ hidden_field.errors }}
      {{ hidden_field }}
      {{ hidden_field.field }}
      {{ hidden_field.help_text }}
    {% endfor %}
     {{ form.management_form }}

    <table>
      <h3>
        {% for hidden_field in form.forms %}
          {{ hidden_field.errors }}
        {% endfor %}
      </h3>
      {% for lancamento_form in form.forms %}
      <h5>
        {% if lancamento_form.instance.id %}
        {% else %}
          {% if form.forms|length > 1 %}
            <!-- Adicionar outro Lan&ccedil;amento -->
          {% else %}
            <!-- Adicionar Lan&ccedil;amento-->
          {% endif %}
        {% endif %}
      </h5>
      <tr><th>Data Lancaamento</th><th>Contrato</th><th>Detalhe</th><th>Fatura</th><th>Deletar</th> </tr>    
        <tr> 
          <td>{{lancamento_form.id}}{{ lancamento_form.datalancamento }}</td> 
          <td>{{ lancamento_form.id_contrato }}</td> 
           <td>{{ lancamento_form.detalhe }}</td> 
           <td>{{ lancamento_form.id_fatura }}</td> 
           <td>{{lancamento_form.DELETE}} Deletar.</td>
        </tr>
      {% endfor %}

    </table>
    <hr>
    <p>
      <button type="submit" value="Update Fatura" >Atualizar Lan&ccedil;amento</button>
      <a href="{{ contrato.get_absolute_url  }}" role="button" >Cancelar</a>
    </p>
  </form>
{% endblock content %}

Ну, вот пока и все. Но все еще не могу заставить его работать так, как нужно. Есть ли у кого-нибудь направление для решения?

Вернуться на верх