Можно ли изменять неотображаемые поля модели Django во время валидации пользовательской формы, но до фиксации данных модели?

Последние несколько недель я работаю над своим первым проектом на Django: базой данных для регистрации всех входящих заказов от клиентов для общего предприятия. Предположим, что каждый из торговых представителей может быть закреплен за определенным клиентом на целый натуральный год, причем назначения могут меняться от года к году (например, агент A закреплен за клиентом A в 2024 году, тогда как в 2025 году за клиента A может отвечать агент B).

Для начала я решил использовать FK внутри модели Orders, чтобы ссылаться на связанные с ней Product и Customer. В то же время модель Assignments также будет содержать пару FK к таблицам Customer и Agent, так что все будет связано, доступно и циклы данных в моей реляционной диаграмме будут исключены. Тем не менее, это решение было очень болезненным для получения некоторых данных, особенно когда мне нужно было получить отфильтрованные данные для финальных шаблонов... поэтому я решил перевернуть все с ног на голову и использовать другой подход:

class Order (models.Model):
    assignment = models.ForeignKey("Assignment", on_delete=models.RESTRICT)
    product = models.ForeignKey("Product", on_delete=models.RESTRICT)
    quantity = models.PositiveIntegerField(default=1)
    order_date = models.DateField()
    price = models.DecimalField(max_digits=10, decimal_places=2)

class Assignment (models.Model):
    assignment_year = models.PositiveSmallIntegerField()
    customer = models.ForeignKey("Customer", on_delete=models.CASCADE)
    agent = models.ForeignKey("Agent", on_delete=models.CASCADE)
        
    class Meta:
        constraints = [
            UniqueConstraint(
                fields=['year', 'customer'], name='Primary_Key_Assignment'
            )
        ]
        
class Customer (models.Model):
    name = models.CharField(max_length=64)
    address = models.CharField(max_length=64)
    city = models.CharField(max_length=32)
    working_time_start = models.TimeField(blank=True, null=True)
    working_time_end = models.TimeField(blank=True, null=True)
    
    class Meta:
        verbose_name_plural = "Customers"
        constraints = [
            CheckConstraint(
                check = Q(working_time_start__isnull=True, working_time_end__isnull=True)|Q(working_time_start__isnull=False, working_time_end__isnull=False, working_time_start__lte=F('working_time_end')),
                name = 'Wrong working time limits',
            ),
        ]

Теперь приказы напрямую ссылаются на конкретные задания, что, признаюсь, имеет свои плюсы и минусы, и меня не совсем устраивает тот факт, что год задания и дата приказа будут храниться в одной таблице из-за избыточности и потенциальных несоответствий.

После этого я предпочитаю скрывать все эти хитрые детали от пользователя и хотел бы иметь вид администратора для заказов, который мог бы скрывать поле назначения FK, но при этом позволял бы пользователю выбирать идентификатор клиента. После этого я бы выбрал идентификатор клиента, год, основанный на дате заказа, и создал действительное назначение FK, прежде чем реестр мог бы быть сохранен в базе данных.

Что-то вроде следующего:

from django.contrib import admin
from django.db.models import F, CharField
from django import forms
from django.db import models
from .models import Customer, Agent, Product, Order, Assignment

class OrderForm(forms.ModelForm):
    
    customers_list = forms.ModelChoiceField(queryset = Customer.objects.all())
    
    def clean(self):
        cleaned_data = self.cleaned_data
        
        form_date = cleaned_data['order_date']
        customer_form = cleaned_data['customers_list']
        cleaned_data['assignment'] = Assignment.objects.get(assignment_year=form_date.year, customer=customer_form.id)
        
        super(OrderForm, self).clean()

        return cleaned_data
    
    class Meta:
        model = Order
        fields = '__all__'

class OrderAdmin(admin.ModelAdmin):
    form = OrderForm
    fieldsets = (
      ('None', {
          'fields': ( 'customers_list', 'product', 'quantity', 'order_date', 'price', )
      }),
    )

admin.site.register(Order, OrderAdmin)

В представленном выше методе очистки возникает ошибка, поскольку я не включаю FK назначения в форму, поэтому не могу изменить его до запуска валидации на уровне модели (например, CheckConstraints, настраиваемый метод очистки и сохранения модели, если таковой имеется, и т. д.).

Итак, мой вопрос заключается в следующем: есть ли способ, которым я могу построить действительную модель заказа на основе данных, полученных из пользовательской формы заказа, и добавить мое ожидающее назначение в качестве действительного FK до того, как реестр будет вставлен в базу данных, даже если пользователю не будет показано назначение?

как это, может быть?

def clean(self):
  cleaned_data = super().clean()
  form_date = cleaned_data['order_date']
  customer_form = cleaned_data['customers_list']
  cleaned_data['assignment'] = Assignment.objects.get(assignment_year=form_date.year, customer=customer_form.id)

  cleaned_data[{field_name}] = self._meta.model.{some_fk_field}
  return cleaned_data
Вернуться на верх