Model Formset и обычный ModelForm в одном шаблоне?

У меня две модели:

Project:

class Project(Model):
    
    name = CharField(max_length=50)
    members = ManyToManyField("accounts.User", through='ProjectUser')
    organization = ForeignKey(Organization, related_name="projects", on_delete=CASCADE)

    def __str__(self):
        return self.name

и Task:

class Task(Model):
    task = CharField(max_length=100)
    project = ForeignKey(Project, on_delete=CASCADE)

    class Meta:
        db_table = 'task'

Я получил класс UpdateView:

class ProjectUpdateView(UpdateView):
    form_class = ProjectUpdateForm
    template_name = 'projects/project_edit.html'
    success_url = reverse_lazy('projects:list')

Как я могу разрешить пользователю добавлять задачи (через встроенный набор форм) на той же странице, на которой они будут редактировать экземпляр Project?

Например, одна объединенная форма, в которой пользователь может редактировать Project name и добавлять/удалять Task экземпляров, все в одном месте.


person erikvm    schedule 08.11.2020    source источник


Ответы (1)


Форма/набор форм:

Сначала создайте форму и набор форм для вашей модели задачи.

class TaskForm(ModelForm):
    class Meta:
        model = Task
        fields = ['task']

    def __init__(self, *args, **kwargs):
        super(TaskForm, self).__init__(*args, **kwargs)



class TaskBaseFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super(TaskBaseFormSet, self).__init__(*args, **kwargs)



TaskFormset = inlineformset_factory(
    Project,  # parent_model
    Task,  # model
    form=TaskForm, 
    formset=TaskBaseFormSet
)

Или, может быть, все, что вам нужно сделать, чтобы создать TaskFormset, если вам не нужен класс TaskForm, это

TaskFormset = inlineformset_factory(Project, Task, fields=('task',))

Просмотр:

Я вижу, вы используете класс UpdateView для своего представления, поэтому вы можете сделать это, чтобы получить TaskFormset в вашем context_data, поэтому теперь вы можете использовать TaskFormset в шаблоне, который вы объявили в свойстве «template_name» вашего класса UpdateView

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)

    if self.request.POST:
        context['task_formset'] = forms.TaskFormset(self.request.POST)
    else:
        context['task_formset'] = forms.TaskFormset()


    return context

# In the form_valid method of your UpdateView class you can validate the data 
# and assign the Project instance to all the tasks that were create by the formsets

def form_valid(self, form):

     task_formset = context['task_formset']

     # you can validate formset data like this
     if not task_formset.is_valid():
        return self.form_invalid(form)


     project = form.save()
     # Here you assign the Project instance to the Tasks
     task_formset.instance = project
     task_formset.save()

     return super().form_valid(form)

Шаблон:

Теперь все, что вам нужно сделать, это распечатать management_form и каждую форму из набора форм, используя цикл, как вы можете видеть в коде ниже.

<form method="post">

    <!-- Your ProjectUpdateForm form here... -->

    {{ task_formset.management_form }}
    <table>
        {% for form in task_formset %}
        {{ form }}
        {% endfor %}
    </table>
</form>

Надеюсь, это может помочь! Есть несколько ссылок на официальную документацию Django, которые могут оказаться полезными:

https://docs.djangoproject.com/en/3.1/topics/forms/formsets/#using-a-formset-in-views-and-templates

https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/#inline-formsets

https://docs.djangoproject.com/en/3.1/ref/forms/models/#inlineformset-factory

person marcelob33    schedule 08.11.2020