Django. Как заставить ModelChoiceField работать с SingleObjectMixin, FormView и inlineformset_factory?
Итак, я пытался заставить его функционировать так, как планировалось, но, возможно, есть какие-то недоработки, о которых я не знаю.
Задача - показать только связанные варианты в форме с полем выбора.
<Модель 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
# Create your models here.
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}"
Код для представлений:
Код для форм:
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')
)
** Код для урлов:**
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 %}
Цель - показать только связанные варианты в форме с полем выбора.
Я здесь много исследовал, но это все, до чего я могу дойти. Я застрял.