如何为 Django 中的每个外键创建表单字段?

How to create form field for every foreign key in django?

我正在尝试构建一个表单,但不确定如何正确完成。这些是我的模型:

class Country(models.Model):
    name = models.CharField(max_length=250, null=False, blank=False, unique=True)
    twocode = models.CharField(max_length=5, null=False, blank=False, unique=True)

class GeoBonus(models.Model):
    name = models.CharField(max_length=250)
    country = models.ForeignKey(Country, related_name='geo_bonuses')
    bookmaker = models.ForeignKey(Bookmaker, related_name='geo_bonuses')

Bookmaker 有奖金,每个国家不同。例如:

Bookmaker has bonuses:
    Slovakia: "eligible for 100% up to 0"
    Afghanistan: "eligible for 100% up to €100!"
    USA: "restricted country"
    ...

我想在 GeoBonus 中将引号中的文本保存为 name。当然,我可以使用简单的模型表单编写,但我会提交 248 次表单(针对每个国家/地区)。我想显示每个国家/地区的所有字段。


我尝试为国家/地区手动创建新字段:

<form method="post" action="" class="wide">
        {% csrf_token %}
        {%bootstrap_form form %}
        <div class="form-group">
        {%for country in countries%}
        <label class="control-label" for="{{country.twocode}}">{{country}}</label>
            <input class="form-control" id="{{country.twocode}}" maxlength="250" type="text" />
        {%endfor%}
        </div>
        <input type="submit"  class="btn btn-default" name="submit" value="Save">
    </form>

使用这个 forms.py class:

class GeoBonusForm(forms.ModelForm):
    class Meta:
        model = GeoBonus
        fields = ['bookmaker']

但是 request.POST 只包含博彩公司字段。

编辑 1:Views.py

@staff_member_required
def geo_bonus_edit(request, bookmaker=None):
    template = loader.get_template('geobonus/edit.html')
    if request.method == 'POST':
        form = GeoBonusForm(request.POST)
        print request.POST
    else:
        form = GeoBonusForm()
    context = RequestContext(request, {
        'form': GeoBonusForm,
        'countries': Country.objects.all(),
    })
    return HttpResponse(template.render(context))

您可以使用 Django 的 inlinemodelformset for that, refer to using a formset in views and templates 作为示例代码。

我建议您动态生成表单中的字段。它可能看起来像这样:

class GeoBonusForm(forms.ModelForm):
    countries = Country.objects.all()

    def __init__(self, *args, **kwargs):
        super(GeoBonusForm, self).__init__(*args, **kwargs)
        for country in self.countries:
            self.fields[country.name] = forms.CharField()

这允许您为您拥有的每个国家/地区生成一个 CharField。 因此,节省与普通 ModelForm 所期望的有点不同我建议重写 save 方法:

def save(self, commit=True):
    super(GeoBonusForm, self).save(commit=False)
    bookmaker = Bookmaker.objects.get(id=self.cleaned_data['bookmaker'].id)
    for field in self.cleaned_data:
        if field != 'bookmaker':
            country = Country.objects.get(name=field)
            geo_bonus, created = GeoBonus.objects.get_or_create(bookmaker=bookmaker, country=country)
            geo_bonus.name = self.cleaned_data[field]
            geo_bonus.save()

首先我们尝试获取选定的博彩公司。之后,我们遍历清理过的字段(有关表单清理的更多信息,请查看 here)并尝试 get_or_create GeoBonus 对象。现在我们只要填入对应的country字段的值并保存即可。

我稍微修改了你的视图代码:

def index(request, bookmaker=None):
    template = loader.get_template('geobonus/edit.html')
    if request.method == 'POST':
        form = GeoBonusForm(request.POST)
        if form.is_valid():
            form.save()
    else:
        form = GeoBonusForm()
    context = RequestContext(request, {
        'form': GeoBonusForm,
    })
    return HttpResponse(template.render(context))

您不再需要将国家/地区传递给上下文,因为我们会在表单中生成字段。在 POST 我们检查表单是否有效并保存它,如果有效。

对于模板,您现在只需要这个:

<form action="." method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit">
</form>

希望我做对了,这能解决您的问题。

编辑:请注意,这是一个简单的快速示例。当然,您应该清理数据并检查输入是否有效,并可能为更简单的保存过程做准备。