Передача args и kwargs в родительский класс с дополнительным содержимым в django CreateView

Я создал форму django, в которой я хочу передать некоторый контекст, который мне нужно отобразить (в основном записи базы данных в тегах, чтобы пользователь мог выбирать из них). Поэтому я сделал get_context_data function, где я добавляю контекст в существующий контекст следующим образом:

def get_context_data(self, **kwargs):
    context = super(UploadView, self).get_context_data(**kwargs)
    context['categories'] = Category.objects.all()
    context['form'] = VideoForm
    return context

однако форма не сохраняет информацию, переданную в базу данных. Почему это не сработало?

Вот часть моего кода!

forms.py:

class VideoForm(forms.ModelForm):
    category = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label=None)
    class Meta:
        model = Video
        fields = [
            'title',
            'description',
            'description_short',
            'category',
            'time',
            'thumbnail',
            'type',
        ]

    def clean_thumbnail(self):
        picture = self.cleaned_data.get("thumbnail")
        if not picture:
            raise forms.ValidationError("No Image")
        else:
            w, h = get_image_dimensions(picture)
            if w/h != (16/9):
                raise forms.ValidationError("Image in wrong aspect ratio (should be 16:9)")
        return picture

upload.html (это довольно долго, поэтому лучше загрузить его в Pastebin)

views.py:

class UploadView(LoginRequiredMixin, CreateView):
   form_class = VideoForm
   template_name= 'upload.html'

   def form_valid(self, form):
       instance = form.save(commit=False)
       instance.uploader=self.request.user
       return super(UploadView, self).form_valid(form)

   def get_context_data(self, **kwargs):
       context = super(UploadView, self).get_context_data(**kwargs)
       context['categories'] = Category.objects.all()
       context['form'] = VideoForm
       return context

Я использую настраиваемую форму, поэтому я могу установить классы, которые я использую для редактирования CSS (вместо того, чтобы просто использовать form.as_p и иметь действительно уродливое оттуда...)

EDIT:

После немного большего количества тестов я обнаружил, что если бы я положил print(instance) внутри функции def form_valid(self, form):, он ничего не распечатывал, предполагая, что экземпляр пуст. Как это может быть? Кроме того, я попытался удалить: context['form'] = VideoForm и ошибок не обнаружено, но если я правильно заполню форму, это все равно не сработает!

ИЗМЕНИТЬ 2:

Я создал функцию form_invalid следующим образом:

def form_invalid(self, form):
    print(form.instance)
    return super(UploadView, self).form_invalid()

который вызывает:

TypeError: form_invalid() missing 1 required positional argument: 'form'

Это заставило меня задуматься, потому что я не получаю никаких ошибок, когда я перенаправляюсь обратно в форму после отправки. Вот форма, которую я написал: https://pastebin.com/3H6VRZR1

Также я попробовал протестировать его только с помощью form.as_p, который вызывает ту же проблему

ИЗМЕНИТЬ 3:

После тестирования некоторых ответов мы выяснили, что форма самой HTML-страницы, вероятно, является причиной, поскольку форма, полученная в form_invalid(), полностью пуста. Я решил, что я попытаюсь использовать form.as_p снова, посмотрю, по-прежнему ли это вызвала ту же проблему, что и моя пользовательская форма. Теперь я получаю новую ошибку:

null value in column "category_id" violates not-null constraint
DETAIL:  Failing row contains (8, test new fom reg, test-new-fom-reg, null, test, test, 2018-01-10 13:44:58.81876+00, 2018-01-10 13:44:58.818789+00, 1, thumbnails/test-new-fom-reg.png, 2, 1, /home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).

с:

USER
admin

GET
No GET data

POST
Variable Value
title 'test new fom reg'
category '8'
type '1'
time '1'
description 'test'
csrfmiddlewaretoken `BeizxWHU5KDbixit9vpxKoxEeBxgU9MNITaNlkM1qtI0Aq6kIThHrtjfUsQXjxON'
description_short 'test'

FILES
Variable Value
thumbnail <TemporaryUploadedFile: sixteentonineratio.png (image/jpeg)>
videoFile <TemporaryUploadedFile: test 3.mp4 (video/mp4)>

в качестве данных данные отправляются из формы, которые предлагают (на основе this), что category_id не входит в мою модель для формы. который он (поле называется просто category), но почему он думает, что он должен быть там?

Я просто проверил phppgadmin, чтобы посмотреть, как выглядит база данных, и там поле называется id_category, а в моей модели оно называется категорией, почему?

EDIT 4: Я никогда не добавлял Traceback для ошибки выше:

Внутренняя ошибка сервера:/upload/ Traceback (последний последний вызов): Файл "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", строка 85, в _execute   return self.cursor.execute(sql, params) psycopg2.IntegrityError: значение null в столбце "category_id" нарушает непустое ограничение ДЕТАЛИ: Неудачная строка содержит (12, тест, тест, нуль, тест, тест, 2018-01-16 18: 18: 25.907513 + 00, 2018-01-16 18: 18: 25.907538 + 00, 6, thumbnails/test_d1MHjMX. png, 2, 1,/home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).

Вышеупомянутое исключение было прямой причиной следующего исключения:

Traceback (most recent call last):
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/exception.py", line 35, in inner
    response = get_response(request)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 128, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 69, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/contrib/auth/mixins.py", line 52, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 89, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/edit.py", line 172, in post
    return super().post(request, *args, **kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/edit.py", line 142, in post
    return self.form_valid(form)
  File "/home/trie/Desktop/django/vidmiotest/upload/views.py", line 21, in form_valid
    instance.save()
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 729, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 759, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 842, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 880, in _do_insert
    using=using, raw=raw)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/query.py", line 1125, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 1280, in execute_sql
    cursor.execute(sql, params)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column "category_id" violates not-null constraint
DETAIL:  Failing row contains (12, test, test, null, test, test, 2018-01-16 18:18:25.907513+00, 2018-01-16 18:18:25.907538+00, 6, thumbnails/test_d1MHjMX.png, 2, 1, /home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).

Ответ 1

Проблема в вопросе заключается в том, что super(). form_valid() вызывается и перезаписывает построенную форму с помощью контекста ['form'] = VideoForm. Пусть каркас устанавливает форму при использовании CreateView.

Унаследованный CreateView предоставляет функции для настройки формы, сохранения формы, установки self.object в представлении и перенаправления на URL-адрес успеха.

I.e CreateView.form_valid обеспечивает:

self.object = form.save()

Правильное решение для установки загрузчика в UploadView, но вызов super.form_valid попытается снова сохранить форму.

Как я понимаю, желаемое поведение:

  • установить загрузчик
  • сохранить объект
  • перенаправление на URL успеха.

в коде:

instance = form.save(commit=False)
instance.uploader = self.request.user
instance.save()
return redirect(self.get_success_url()) # Skip the call to super

Также, как указано в другом ответе, контекст ['form'] = VideoForm перезапишет настройку формы с помощью CreateView.

Я предлагаю посмотреть, как поток выполнения работает для CreateView, он установит форму для контекста шаблона как в ситуации GET, так и в POST.

Еще один способ решить эту проблему - разрешить VideoForm принимать загрузчик в init:

class VideoForm(forms.ModelForm):
    def __init__(self, uploader, *args, **kwargs):
        self.uploader = uploader
        super().__init__(*args, **kwargs)

    def save(self, commit=True):
        instance = super().save(commit=False)
        instance.uploader = self.uploader
        if commit:
           instance.save()
        return instance

и предоставить загрузчику форму

 class UploadView(..., CreateView):
     def get_form_kwargs(self):
         kwargs= super().get_form_kwargs()
         kwargs['uploader'] = self.request.user
         return kwargs

Надеюсь, что это поможет.

Глядя на РЕДАКТИРОВАТЬ 3:

Причина, по которой он говорит "category_id", заключается в том, что внешние ключи в модели django автоматически будут иметь суффикс "_id" в качестве имени столбца в базе данных. Документация.

В модели Video, вероятно, есть внешний ключ для категории.

Если вы нашли id_category в базе данных, возможно, вы потеряли синхронизацию с вашими миграциями/моделями? Вероятно, вам следует попробовать очистить базу данных и/или повторно запустить makemigrations/migrate

Ответ 2

Вам, конечно, не нужна строка context['form'] = VideoForm. Помимо всего прочего, он переписывает существующую форму, чтобы ваш шаблон никогда не видел никаких ошибок.