Джанго. Как заставить ModelChoiceField работать с SingleObjectMixin, FormView и inlineformset_factory?
Это не должно быть сложно
Как показать в форме модели Django только связанные записи из Model.B для выбора пользователем. Учитывая, что при условии, что пользователь данных Model.A и Model.C должен выбрать один из возможных вариантов Model.B?
Визуализация проблемы - сквематика
Итак, я пытался заставить его работать, как планировалось, но, насколько мне известно, может быть подстройка.
Цель состоит в том, чтобы показать только связанные параметры в форме с полем выбора.
Модель CONTRATO регистрация всех договоров
Модель FATURA регистрирует все счета-фактуры и имеет внешний ключ с CONTRATO в отношении «один ко многим» (поэтому у одного контракта много счетов-фактур).
Модель LANCAMENTO регистрирует записи из договоров, которые впоследствии будут присвоены счету-фактуре в FATURA. Таким образом, LANCAMETNO имеет ForeingKey с CONTRATO в отношении «на-ко-многим» (таким образом, один контракт имеет много записей в LANCAMENTO). И у LANCAMENTO также есть внешний ключ с FATURA, который по умолчанию равен нулю, поэтому позже для этой записи в LANCAEMTNO будет назначен FATURA.
Цель состоит в том, чтобы иметь эту логику в форме. Таким образом, когда пользователь переходит на LANCAMETNO, чтобы назначить FATURA, он может выбрать только FATURA с тем же идентификатором contract_id, что и у LANCAMETNO.
Я добрался сюда, много исследуя, но это все, что я могу сделать. Я застрял.
Вот код, если кто-то может указать мне правильное направление.
Вот мой код для моделей:
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 django.forms.models import inlineformset_factory
from multiprocessing import context
from urllib import request
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse, reverse_lazy
from blog.forms import ContratoBlogLancamentoBlogFormset, ContratoBlogFaturaBlogFormset
from blog.models import ContratoBlog, FaturaBlog, LancamentoBlog
from django.views.generic import TemplateView, FormView, CreateView, ListView, DetailView, UpdateView, DeleteView
from django.views.generic.detail import SingleObjectMixin
# Create your views here.
# Views Just Home.
class HomeView(TemplateView):
template_name = 'blog/home.html'
# Views for Model Contrato.
class ContratoBlogDetailView(DetailView):
model = ContratoBlog
template_name = "blog/contratoblog_detail.html"
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet Complementary Data
context['contrato_dados'] = ContratoBlog.objects.get(id=self.object.pk)
context['fatura_list'] = FaturaBlog.objects.filter(id_contrato=self.object.pk).all()
context['lancamento_list'] = LancamentoBlog.objects.filter(id_contrato=self.object.pk).all()
return context
class ContratoBlogFormView():
pass
#View para Faturas do Contrato
class ContratoBlogFaturaBlogEditView(SingleObjectMixin, FormView):
model = ContratoBlog
template_name = 'blog/contrato_fatura_edit.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['fatura_list'] = FaturaBlog.objects.filter(id_contrato=self.object.pk).all()
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ContratoBlog.objects.all())
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ContratoBlog.objects.all())
return super().post(request, *args, **kwargs)
def get_form(self, form_class=None):
return ContratoBlogFaturaBlogFormset(**self.get_form_kwargs(), instance=self.object)
def form_valid(self, form):
form.save()
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('blog:detalhe_contrato', kwargs={'pk': self.object.pk})
#---------------------------------
#View para Lancamentos do Contrato
class ContratoBlogLancamentoBlogEditView(SingleObjectMixin, FormView):
model = ContratoBlog
template_name = 'blog/contrato_lancamento_edit.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ContratoBlog.objects.all())
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ContratoBlog.objects.all())
return super().post(request, *args, **kwargs)
def get_form(self, form_class=None):
return ContratoBlogLancamentoBlogFormset(**self.get_form_kwargs(), instance=self.object)
def form_valid(self, form):
form.save()
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('blog:detalhe_contrato', kwargs={'pk': self.object.pk})
Код для форм:
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')
)
** Код для URL: **
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 вместо отображения всех записей в фатуре.
{% 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 %}
Цель состоит в том, чтобы показать только связанные параметры в форме с полем выбора.
Я добрался сюда, много исследуя, но это все, что я могу сделать. Я застрял.