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ç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ç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çamento -->
{% else %}
<!-- Adicionar Lanç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çamento</button>
<a href="{{ contrato.get_absolute_url }}" role="button" >Cancelar</a>
</p>
</form>
{% endblock content %}
Ну, вот пока и все. Но все еще не могу заставить его работать так, как нужно. Есть ли у кого-нибудь направление для решения?