A
A
Andrey Kovalchuk2017-04-19 08:55:08
Django
Andrey Kovalchuk, 2017-04-19 08:55:08

How is a FormSet made for a ManyToMany relationship?

Good day.
I'm stuck, I can't figure it out without you. You need to display multiple forms (the main object and related objects of the same type) on the same page. The object is tied to itself by a ManyToMany relationship (there will be a model below).
The view on which I want to see several forms is created using UpdateView and currently displays one form:
c4404badf8.jpg
I would like the completed form of the related object to be displayed next to (and below, respectively):
3574a7bf4a.jpg
After reading various materials on the network, I came to the conclusion that the following should help construction in forms.py :

PostFormSet = inlineformset_factory(Post, Post.depend_posts.through, fk_name='from_post', form=PostForm, can_delete=False)

I get not quite what I would like:
84a8c15ab5.jpg
Template Forms:
<form class="form-horizontal form-label-left" method="post" enctype="multipart/form-data">
                    {% csrf_token %}
                    {{ form_vk.management_form }}
                    {% for form in form_vk %}
                        {% for field in form.visible_fields %}
                            {% if field.auto_id == 'id_date_publish' %}
                                <div class="control-group">
                                    <div class="controls">
                                        <div class="input-prepend input-group">
                                                <span class="add-on input-group-addon"><i
                                                        class="glyphicon glyphicon-calendar fa fa-calendar"></i></span>
                                            {{ field }}
                                        </div>
                                    </div>
                                </div>
                            {% endif %}
                            <div class="form-group">
                                <label class="control-label col-md-3 col-sm-3 col-xs-12"
                                       for="{{ field.auto_id }}">{{ field.label_tag }}</label>
                                <div class="col-md-9 col-sm-9 col-xs-12">
                                    {{ field }}
                                </div>
                                {% if field.errors %}
                                    <div class="control-label">
                                        {{ field.errors.as_text }}
                                    </div>
                                {% endif %}
                            </div>
                        {% endfor %}
                    {% endfor %}
                    <div class="ln_solid"></div>
                    <div class="form-group">
                        <div class="col-md-9 col-sm-9 col-xs-12 col-md-offset-3">
                            <button type="button" class="btn btn-primary">Cancel</button>
                            <button type="reset" class="btn btn-primary">Reset</button>
                            <input type="submit" class="btn btn-success" value="Save"/>
                            <button type="submit" class="btn btn-success"
                                    formaction="{% url 'mainApp:to_vk' object.id %}">Send
                            </button>
                        </div>
                    </div>
                </form>

models.py
class Post(models.Model):
    title = models.CharField(verbose_name='Заголовок', max_length=100, null=False, unique=True)
    content = models.TextField(verbose_name='Текст')
    min_age = models.IntegerField(verbose_name='Возрастной рейтинг', default=0)
    image = models.ImageField(verbose_name='Основное изображене', upload_to='news_image')
    site_source = models.URLField(verbose_name='Сайт источник', max_length=110)
    date_publish = models.DateTimeField('Дата публикации', null=True)
    category = models.ForeignKey(Category, verbose_name='Категория', on_delete=models.CASCADE, default=None)
    publish_status = models.BooleanField(verbose_name='Опубликован', default=False)
    tags = models.ManyToManyField(Tag)
    depend_posts = models.ManyToManyField('self')

    def __str__(self):
        return self.title

views.py
class UpdatePostView(UpdateView):
    model = Post
    form_class = PostForm
    template_name = 'mainApp/post_edit_create.html'
    success_url = '/'

    def get_context_data(self, **kwargs):
        context = super(UpdatePostView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['form_vk'] = PostFormSet(self.request.POST, self.model)
        else:
            context['form_vk'] = PostFormSet()
        return context

And here is the table that Django made for MTM
0df70a1f34.jpg

Answer the question

In order to leave comments, you need to log in

1 answer(s)
T
to_east, 2018-06-17
@to_east

If I understand correctly - you need to display several instances of forms with the ability to edit, then maybe this will help (if the question is still relevant :)):

# forms.py

class EditThumbsForm(forms.ModelForm):
    class Meta:
        model = Thumb
        fields = ('src', )
    src = forms.CharField()


EditThumbsFormSet = forms.inlineformset_factory(Video, Thumb, form=EditThumbsForm, extra=0)


class EditVideosForm(forms.ModelForm):
    class Meta:
        model = Video
        fields = ('id', 'video_id', 'title', )

    id = forms.IntegerField()
    video_id = forms.IntegerField()
    title = forms.CharField()


class EditVideosModelFormSet(BaseModelFormSet):
    def add_fields(self, form, index):
        super(__class__, self).add_fields(form, index)
        form.thumb_formset = EditThumbsFormSet(
            instance=form.instance,
            data=form.is_bound and form.data or None,
            files=form.is_bound and form.files or None,
            prefix='thumb-%s-%s' % (
                form.prefix,
                EditThumbsFormSet.get_default_prefix()
            )
        )

EditVideosFormSet = forms.modelformset_factory(Video, EditVideosForm, formset=EditVideosModelFormSet, extra=0)

In the view like this:
class EditVideosView(View):
    template_name = 'admin/edit_videos.html'

    def get(self, request):
        instance = Video.objects.all()
        formset = EditVideosFormSet(queryset=instance)
        return render(request, self.template_name, context={'formset': formset})

And in the template like this:
{% for form in formset %}
    <li>
        {{ form }}
        {% if form.thumb_formset %}
            {{ form.thumb_formset.management_form }}
            <ul>
            {% for thumb in form.thumb_formset %}
                <li>
                {{ thumb.src }}
                </li>
            {% endfor %}
            </ul>
        {% endif %}
    </li>
{% endfor %}

In short, you must first create a formset from the main model using modelformset_factory(), and then add related tables to the main one using inlineformset_factory()

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question