【问题标题】:Django: customize inline formset for 'through' model with multiple foreign keysDjango:为具有多个外键的“通过”模型自定义内联表单集
【发布时间】:2014-01-31 14:00:45
【问题描述】:

我想做的是更改前端内联表单集的界面,以便复选框可以与“通过”模型上的两个不同外键字段相关联。基本上,我想将两个下拉菜单折叠成一个复选框。

在models.py中:

class Food(models.Model):
    name = models.CharField(max_length=10)        

class Day(models.Model):
    """Days of the week: Sunday, Monday, Tuesday, etc."""
    name = models.CharField(max_length=10)

class Meal(models.Model):
    """Breakfast, Lunch, Dinner"""
    name = models.CharField(max_length=10)

class Plan(models.Model):
    name = models.CharField()
    foods = models.ManyToManyField('Food',related_name='plans')
    days = models.ManyToManyField('Day',related_name='plans',through='Schedule')
    meals = models.ManyToManyField('Meal',related_name='plans',through='Schedule')

class Schedule(models.Model):
    plan = models.ForeignKey(Plan)
    day = models.ForeignKey(Day)
    meal = models.ForeignKey(Meal)

每个计划都有一组关联的食物,然后通过计划模型与特定日期的特定膳食相关联。

我想要一个模型表单,用户可以在其中创建膳食计划,并在其中说明他们将在哪几天吃什么食物。例如,用户可以先从一组食物中选择苹果和香蕉,然后他们应该能够选择在哪一天吃苹果和香蕉。

我的看法是:

def plan_add(request):
    form = forms.PlanForm();

    ScheduleFormSet = inlineformset_factory(Plan, Schedule)
    formset_schedule = ScheduleFormSet()

    response = render(request, 'template.html', {
        'form' : form,
        'formset_schedule' : formset_schedule,
    })
    return response

这给了我一个可以工作的表单,但是一个不友好的用户界面。对于您要添加的每个时间表,您必须首先从下拉列表中选择日期,然后从第二个下拉列表中选择餐点。我想要的是 7 个表行,每行都有一个用于每餐的复选框,您只需选中要放在日程表上的餐食的复选框。所以,我想让一个表单字段(一个复选框)与 Schedule 模型上的两个外键字段相关联:日和餐。

我想知道是否有“Django”方法来解决这个问题,可能是通过覆盖 inlineformset_factory,或者如果这是一件很奇怪的事情,我应该手动构建这个表单,手动验证它,然后手动解析并保存数据。

【问题讨论】:

  • 我喜欢手动构建表单。我认为django.forms 是 django 框架中最不灵活和不直观的部分。但是,您可能希望查看清晰的表单或 django 表单向导以获得更好的 UI
  • 知道在这种情况下手动构建表单并不是坏习惯,这很有帮助。我知道我可以让它工作,感觉就像我可以通过这样做来解决 Django 拥有的许多强大的内置功能。
  • 你可以把表单域分解成你喜欢的任何形式,然后在客户端使用js来收集数据并发送它,就像你正确使用它一样。这样您就拥有灵活性,但不会失去内置功能

标签: django foreign-keys many-to-many inline-formset


【解决方案1】:

这就是我最终要做的。我没有使用 inlineformset_factory,因为我无法通过内联表单集获得所需的控件。对于食物,我指定了一个带有 CheckboxSelectMultiple 小部件的自定义 ModelMultipleChoiceField,然后通过获取天数并循环遍历它们,每天都做同样的事情。

class PlanForm(ModelForm):
    foods = forms.ModelMultipleChoiceField(
        queryset=Food.objects.all(), 
        required=False, 
        widget=forms.CheckboxSelectMultiple,
        label = "Foods:")

    def __init__(self, *args, **kwargs):
        super(PlanForm, self).__init__(*args, **kwargs)
        days = Day.objects.all()
        for day in days:        
            self.fields[day.name] = forms.ModelMultipleChoiceField(
                queryset=Meal.objects.all(), 
                required=False, 
                widget=forms.CheckboxSelectMultiple,
                label = day.name)

    class Meta:
        model = Plan
        fields = (['name','description','foods']+[day.name for day in Day.objects.all()])

这是获得我想要的表单的基本代码。然后,在您的模板中,您可以执行以下操作,并添加您的自定义 html:

{% for field in form %}
    {{ field.label_tag }}
    {% for choice in field %}
        {{choice}}
    {% endfor %}
{% endfor %}

我最后也使用了这个解决方案:http://schinckel.net/2013/06/14/django-fieldsets/ 来获取字段集,所以我可以为表单上的每个组进行不同的渲染。

【讨论】:

    猜你喜欢
    • 2011-06-12
    • 2019-04-23
    • 2017-03-23
    • 2020-11-07
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    相关资源
    最近更新 更多