Django. Сохранение данных из модальной формы в БД

Учусь работать с Django, и в процессе создания приложения столкнулся со следующей проблемой. Есть три модели (Object, Staff, Work), одна из них (Work) посредством FK связана с двумя другими. Также есть шаблон формы для создания записи в таблице Work. В таком виде всё прекрасно работает. Однако, мне захотелось прикрутить модальное окно для вызова упомянутой формы. Собственно, тут я и сталкиваюсь с тем, что не вполне понимаю, как именно передать данные для FK из шаблона в БД посредством POST и вывести их в таблицу на фронте. Уточнение_1: данные для CharField и т.п. передаются и выводятся корректно, проблема только с FK. Уточнение_2: записывать данные в связанные таблицы не требуется, нужно только вывести их значение, выбранное в шаблоне.

UPD: В данный момент получаю такую ошибку: NOT NULL constraint failed: mip_apps_work.work_office_id

Привожу код.

Модели:

class Object(models.Model):
    object_name = models.CharField(max_length = 50, default = '', 
        verbose_name = 'Объект', unique = True)
    object_address = models.CharField(max_length = 100, default = '', 
        verbose_name = 'Адрес', blank = True)
    object_contacts = models.TextField(verbose_name = 'Контакты', default = '', 
        blank = True)
    class Meta():
        ordering = ['object_name']
    def __str__(self):
        return f'{self.object_name}'


class Staff(models.Model):
    surname = models.CharField(max_length = 30, default = '', 
        verbose_name = 'Фамилия', blank = True, null = True)
    name = models.CharField(max_length = 30, default = '', 
        verbose_name = 'Имя', blank = True, null = True)
    patronymic = models.CharField(max_length = 30, default = '', 
        verbose_name = 'Отчество', blank = True, null = True)
    def __str__(self):
        return self.surname + ' ' + self.name + ' ' + self.patronymic


class Work(models.Model):
    NONE = ''
    STARTED = 'В работе'
    PLANNED = 'Запланировано'
    COMPLETED = 'Выполнено'
    CANCELLED_BY_INITIATOR = 'Отменено инициатором'
    PAUSE = 'Пауза'
    WORK_STATUS_CHOICES = [(NONE, ''), (STARTED, 'В работе'), 
        (PLANNED, 'Запланировано'), (COMPLETED, 'Выполнено'), 
        (CANCELLED_BY_INITIATOR, 'Отменено инициатором'), (PAUSE, 'Пауза')]
    work_office = models.ForeignKey(Object, on_delete = models.CASCADE, 
        default = '', verbose_name = 'Объект')
    work_status = models.CharField(max_length = 22, 
        choices = WORK_STATUS_CHOICES, default = '', verbose_name = 'Статус', 
        blank = True)
    work_start_date = models.DateField(default = datetime.date.today().strftime('%Y-%m-%d'), 
        verbose_name = 'Дата начала', blank = True, null = True)
    work_finish_date = models.DateField(default = datetime.date.today().strftime('%Y-%m-%d'), 
        verbose_name = 'Дата завершения', blank = True, null = True)
    work_description = models.TextField(verbose_name = 'Описание работ', 
        default = '', blank = True, null = True)
    work_executor = models.ForeignKey(Staff, on_delete = models.CASCADE, 
        default = '', verbose_name = 'Исполнитель', blank = True, null = True)
    work_sz_number = models.CharField(max_length = 15, 
        verbose_name = 'Номер СЗ', default = '',blank = True)
    work_sd_number = models.CharField(max_length = 15, 
        verbose_name = 'Номер в SD', default = '',blank = True)
    def __str__(self):
        return f'{self.work_description} '

Представления:

class WorkListView(ListView):
    model = Work
    template_name = 'mip_apps/works_list.html'
    context_object_name = 'works_list'

class WorkCreateView(CreateView):
    model = Work
    template_name = 'mip_apps/create_works_form.html'
    fields = ['work_office', 'work_status', 'work_start_date', 
    'work_finish_date', 'work_description', 'work_executor', 'work_sz_number', 
    'work_sd_number']
    success_url = reverse_lazy('table')

    def addWork(request):
    if request.method == 'POST':
        work = Work.objects.get_or_create(
            work_office = request.POST.get('work_office.object_name'),
            work_status = request.POST.get('work_status'),
            work_start_date = request.POST.get('work_start_date'),
            work_finish_date = request.POST.get('work_finish_date'),
            work_description = request.POST.get('work_description'),
            work_executor = request.POST.get('work_executor_surname'),
            work_sz_number = request.POST.get('work_sz_number'),
            work_sd_number = request.POST.get('work_sd_number')
        )
        print(work)
        works = Work.objects.all();
    return render(request, 'mip_apps/works_list.html')

URLS:

# Список текущих работ.
path('works_list/', views.WorkListView.as_view(), name = 'works_list'),
# Форма создания работ.
path('create_work/', views.WorkCreateView.as_view(),  name='create_work'),
# Форма редактирования работ.
path('update_work/<pk>', views.WorkUpdateView.as_view(), name='update_work'), 
# Форма удаления работ.
path('delete_work/<pk>', views.WorkDeleteView.as_view(), name='delete_work'),
path('works_list/ajax/table', views.addWork, name='table'),

Кнопка вызовы модальной формы и скрипт:

<a class="contact" href="#" data-form="{% url 'create_work' %}" data-target="{% url 'create_work' %}" title="Edit">edit</a>

<div class="modal hide" id="contactModal">
</div>

<script>
$(".contact").click(function(ev) { // for each edit contact url
    ev.preventDefault(); // prevent navigation
    var url = $(this).data("form"); // get the contact form url
    $("#contactModal").load(url, function() { // load the url into the modal
        $(this).modal('show'); // display the modal on url load
    });
    return false; // prevent the click propagation
});

$('.contact-form').live('submit', function() {
    $.ajax({ 
        type: $(this).attr('method'), 
        url: this.action, 
        data: $(this).serialize(),
        context: this,
        success: function(data, status) {
            $('#contactModal').html(data);
        }
    });
    return false;
});
</script>

Шаблон создания записи:

<div class="align-baseline w-25 mb-3 mx-auto bg-white">
<form method="post" action="ajax/table">

    <div class="modal-header">
        <h3>Добавить задачу</h3>
        <button type="button" class="close" data-dismiss="modal">×</button>
    </div>
    <div class="modal-body">
        {% csrf_token %}
        {{ form.work_start_date|date:'d-m-Y' }}
        <table>{{ form|crispy }}</table>
    </div>
    <div class="modal-footer">
        <input class="btn btn-primary" type="submit" value="Добавить" />
        <input class="btn btn-danger" class="close" name="cancel" type="submit" value="Отменить" />
    </div>
    
</form>
</div>

Если я правильно понимаю концепцию, то мне требуется определить id связанной записи и записать его в БД Work. Однако, как это правильно реализовать - не пойму. Мануалы курил, перегуглил несколько десятков аналогичных проблем. Просветите, пожалуйста, как решить вопрос!

Проблемав том, как вы используете поле work_office из формы в обработчике POST, вот тут:

work_office=request.POST.get('work_office.object_name')

В поле формы хранится идентификатор сущности Object и, чтоб по идентификатору достать сам объект нужно сделать запрос с БД:

work_office=Object.objects.get(id=request.POST.get('work_office'))

Object.objects.get(id=...) достает из БД объект по полю id, а id мы берем из параметра, который пришел в запросе.

как правильно сделать редирект на /works_list

Используйте redirect:

from django.shortcuts import redirect


    if request.method == 'POST':
        work = Work.objects.get_or_create(
            ...
        )
        return redirect("works_list")  # works_list - это name из маппинга в urls.py
Вернуться на верх