【问题标题】:Django - In a ModelForm, display data coming from a model linked with M2M relationshipDjango - 在 ModelForm 中,显示来自与 M2M 关系链接的模型的数据
【发布时间】:2022-01-04 01:18:54
【问题描述】:

我想在表单中添加信息,这些信息来自与我正在更新的模型建立 M2M 关系的模型。表单可以正常工作,但我无法添加任何信息。
这是我得到的:

这是预期的结果:

我的解决方案:最后,我更新了UserGroup 模型中的__str__() mthod 以显示我所期望的(但是,在这个阶段,I lost the dynamic part 和我的视图不再起作用了:- /)

主模型是Event,由于这种关系,它链接到Groups;在表单中,所有组都被列出并显示为复选框,但我只能显示组的名称,没有其他信息。

看起来我错过了一些数据/信息:组名仅因为我使用{{grp}}}(见下文)而显示,但它没有可用的属性/字段,即使它是使用来自组模型的查询进行初始化的.
我设想了一种解决方法(见下文),因为我的第一次尝试让我考虑了这种解决方案,但我无法重现我所做的:-/
知道我做错了什么,或者有什么建议来解决这个问题吗?提前致谢。

这里是相关的代码部分。
型号:

class UserGroup(models.Model):
    company = models.ForeignKey(
        Company, on_delete=models.CASCADE, verbose_name="société"
    )
    users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
    group_name = models.CharField("nom", max_length=100)
    weight = models.IntegerField("poids", default=0)
    hidden = models.BooleanField(default=False)

    def __str__(self):
        return self.group_name

class Event(models.Model):
    company = models.ForeignKey(
        Company, on_delete=models.CASCADE, verbose_name="société"
    )
    groups = models.ManyToManyField(UserGroup, verbose_name="groupes", blank=True)
    rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
    event_name = models.CharField("nom", max_length=200)
    event_date = models.DateField("date de l'événement")
    slug = models.SlugField()
    current = models.BooleanField("en cours", default=False)
    quorum = models.IntegerField(default=33)
    rule = models.CharField(
        "mode de scrutin", max_length=5, choices=rules, default="MAJ"
    )

    class Meta:
        verbose_name = "Evénement"
        constraints = [
            models.UniqueConstraint(fields=["company_id", "slug"], name="unique_event_slug")
        ]

    def __str__(self):
        return self.event_name

表格:

class EventDetail(forms.ModelForm):
    groups = forms.ModelMultipleChoiceField(
        label = "Liste des groupes",
        queryset = None,
        widget = forms.CheckboxSelectMultiple,
        required = False
        )

    class Meta:
        model = Event
        fields = ['event_name', 'event_date', 'quorum', 'rule', 'groups']


    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        instance = kwargs.get('instance', None)
        self.fields['groups'].queryset= UserGroup.objects.\
                                                filter(company=instance.company).\
                                                order_by('group_name')

查看:

@user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
    '''
        Manage events creation and options
    '''
    company = Company.get_company(request.session['comp_slug'])
    # all_groups = list(UserGroup.objects.filter(company=company, hidden=False).order_by('group_name').values())

    if evt_id > 0:
        current_event = Event.objects.get(id=evt_id)
        event_form = EventDetail(request.POST or None, instance=current_event)

    else:
        event_form = EventDetail(request.POST or None)

    event_form.fields['groups'].queryset = UserGroup.objects.\
                                                        filter(company=company, hidden=False).\
                                                        order_by('group_name')
    
    if request.method == 'POST':
        if event_form.is_valid():
            if evt_id == 0:
                # Create new event
                event_data = {
                    "company": company,
                    "groups": event_form.cleaned_data["groups"],
                    "event_name": event_form.cleaned_data["event_name"],
                    "event_date": event_form.cleaned_data["event_date"],
                    "quorum": event_form.cleaned_data["quorum"],
                    "rule":event_form.cleaned_data["rule"]
                }
                new_event = Event.create_event(event_data)
            else:
                new_event = event_form.save()

        else:
            print("****** FORMULAIRE NON VALIDE *******")
            print(event_form.errors)

    return render(request, "polls/adm_event_detail.html", locals())

HTML(我没有把'手风琴'小部件的每个部分,我认为它们与问题无关):

{% if evt_id %}
<form action="{% url 'polls:adm_event_detail' company.comp_slug evt_id %}" method="post">
{% else %}
<form action="{% url 'polls:adm_create_event' company.comp_slug %}" method="post">
{% endif %}
    {% csrf_token %}

    <!-- Hidden field where the referer is identified to go back to the related page after validation -->
    <input type="hidden" name="url_dest" value="{{ url_dest }}" />

    <br>

    <!-- Accordion -->
    <div id="eventDetails" class="accordion shadow">

        <div class="card">
            <div class="card-header bg-white shadow-sm border-0">
                <h6 class="mb-0 font-weight-bold">
                    Evénement
                </h6>
            </div>
            <div class="card-body p-5">
                <p>Nom : {{event_form.event_name}} </p>
                <p>Date : {{event_form.event_date}} </p>
            </div>
        </div>

        <!-- Accordion item 2 - Event's groups -->
        <div class="card">
        <div id="headingTwo" class="card-header bg-white shadow-sm border-0">
            <h6 class="mb-0 font-weight-bold">
                <a href="#" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo" class="d-block position-relative collapsed text-dark collapsible-link py-2">
                    Groupes d'utilisateurs
                </a>
            </h6>
        </div>
        <div id="collapseTwo" aria-labelledby="headingTwo" data-parent="#eventDetails" class="collapse show">
            <div class="card-body p-5">
                <p>Sélectionnez le(s) groupe(s) d'utilisateurs participants à l'événement :</p>
                <ul>
                {% for grp in event_form.groups %}
                    <li>{{ grp }}
                        {{ grp.weight }}
                        {{ grp.hidden }}
                        {{ grp.nb_users }}
                    </li>
                {% endfor %}
                </ul>

                <p></p>
            </div>
        </div>
        </div>

    </div> <!-- Accordion end -->


    <button class="btn btn-success mt-5" type="submit">{% if evt_id %}Mettre à jour{% else %}Créer{% endif %}</button>
    &nbsp &nbsp &nbsp
    <a class="btn btn-secondary back_btn mt-5" href="*">Annuler</a>
    <div class="row">
        <div hidden>
            <!-- List of groups in event -->
            {{ event_form.group_list }}
        </div>
    </div>
</form>

解决方法
如果无法直接实现这一点,我想了一个解决方法,可以分几个部分实现:

  • 创建一个几乎类似于查询集的列表:group_list = UserGroup.objects.filter(company=instance.company).order_by('group_name').values()
    我已经知道我可以显示每个组及其详细信息和一个复选框
  • 在客户端 (javascript) 上,我管理一个隐藏列表,该列表将成为表单的一部分,其中包含每个选定组的 ID。这意味着当在未选中的
  • 上检查一个框时,将动态更新列表
  • 在 POST 请求中,读取列表以更新更新事件的组属性。

我更希望用户操作直接对表单产生影响,但我知道这可行

【问题讨论】:

    标签: django forms many-to-many


    【解决方案1】:

    您正在访问表单的组字段,而不是模型实例。表单字段与其他模型没有任何关系,它只是一个字段。您可以使用form.instance 访问模型表单的底层实例。

    另请注意,您在查询相关模型时会获得一个关系管理器对象。因此,使用.all 查询所有组。

    试试

         <ul>
             {% for grp in event_form.instance.groups.all %}
                 <li>{{ grp }}
                     {{ grp.weight }}
                     {{ grp.hidden }}
                     {{ grp.nb_users }}
                    </li>
             {% endfor %}
         </ul>
    

    【讨论】:

    • 是的,它正确显示属性!我不知道这种语法。但我不再有复选框了:-/
    • @Christophe,你想在组属性前面有复选框吗?如果您想将这些数据作为表单的一部分提交,您需要设置一个不同的表单。这些是 UserGroup 模型的属性,但您的表单是 Event 模型表单。
    • 是的,我希望这个列表成为表单的一部分,但除了名称之外还显示属性以提供信息以方便选择。实际上,由于列表,我设法显示了复选框+详细信息,但是我不能在表单中使用它,并且我希望尽可能避免两者兼有,根据用户对表单的操作使用 js 管理表单字段列表...
    • 最后我更新了UserGroup模型中的__str__()方法
    猜你喜欢
    • 1970-01-01
    • 2012-07-28
    • 2012-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 2021-12-01
    • 2010-10-22
    相关资源
    最近更新 更多