Django 表单与基于类模型的类元数据模型

52 阅读2分钟

在 Django 中,使用基于类模型的类元数据模型生成表单时,在添加或编辑记录时,表单中某些字段的值可能不会被正确地填充或保存。例如,在以下场景中,Neighborhood 表单中的 "state" 字段无法正确地填充和保存:

  • 模型:
class City(models.Model):
    name = models.CharField("City", max_length=100, blank=False, null=False)
    state = models.CharField("State", max_length=2, blank=False, null=False)

class Neighborhood(models.Model):
    name = models.CharField("Name", max_length=100, blank=False, null=False)
    city = models.ForeignKey(City, blank=False, null=False)
  • 表单:
class NeighborhoodForm(forms.ModelForm):
    class Meta:
        model = Neighborhood
    state = forms.CharField("State", max_length=2, required=True)
  • 视图:
def index(request):
    if "submit" in request.POST:
        form = NeighborhoodForm(request.POST, request.FILES)
        if form.is_valid():
            form.save(commit=True)

    elif "cancel" in request.POST:
        return HttpResponseRedirect("/")

    else:
        form = NeighborhoodForm()

    neighborhoods = Neighborhood.objects.all()
    cities = City.objects.all()
    data = {
        "form": form,
        "states": STATES,
        "cities": cities,
        "neighborhoods": neighborhoods
    }
    return render_to_response("neighborhood/inserir.html", data, context_instance=RequestContext(request))
  • 模板:
{% extends "base.html" %}

{% block content %}
    <form action="" method="post" id="neighborhoodForm" name="neighborhoodForm">
        {% csrf_token %}
        <div>
            <label>State:</label>
            <select id="state" name="state" autofocus="autofocus">
                <option value=""></option>
                {% for item in states %}
                    <option value="{{ item }}"
                            {% if item == form.state.value %}
                            selected="selected"
                            {% endif %}>{{ item }}</option>
                {% endfor %}
            </select>
        </div>

        <div>
            <label>City:</label>
            <select id="city" name="city">
                <option value=""></option>
                {% for item in cities %}
                    <option value="{{ item.id }}"
                            {% if item.id == form.city.value|add:0 %}
                            selected="selected"
                            {% endif %}>{{ item.name }}</option>
                {% endfor %}
            </select>
        </div>
        <div>
            <label>Neighborhood Name:</label>
            <input type="text" id="name" name="name" value="{{ form.name.value|default_if_none:"" }}"/>
        </div>
        <div>
            <button type="submit" id="submit" name="submit" value="submit">Submit</button>
            <button type="submit" id="cancel" name="cancel" value="cancel">Cancel</button>
        </div>
    </form>

    <br/>

    <table border="1">
        <tr>
            <th>Neighborhood Name</th>
            <th>City</th>
            <th>State</th>
        </tr>
        {% for item in neighborhoods %}
        <tr>
            <td>{{ item.name }}</td>
            <td>{{ item.city.name }}</td>
            <td>{{ item.city.state }}</td>
        </tr>
        {% endfor %}
    </table>
{% endblock %}

在上述场景中,当用户在表单中选择一个城市时,"state" 字段的值不会被正确地填充。这是因为 "state" 字段不是 Neighborhood 模型的一部分,因此它不会被自动初始化。

  1. 解决方案

要解决上述问题,可以使用以下方法:

  • 在表单的 init 方法中初始化 "state" 字段的值。
class NeighborhoodForm(forms.ModelForm):
    class Meta:
        model = Neighborhood

    def __init__(self, *args, **kwargs):
        super(NeighborhoodForm, self).__init__(*args, **kwargs)
        if 'instance' in kwargs:
            state = self.instance.city.state
            self.fields['state'].initial = state
  • 在视图中使用 NeighborhoodForm 的 instance 参数来初始化表单。
def index(request):
    if "submit" in request.POST:
        form = NeighborhoodForm(request.POST, request.FILES, instance=neighborhood)
        if form.is_valid():
            form.save(commit=True)

    elif "cancel" in request.POST:
        return HttpResponseRedirect("/")

    else:
        form = NeighborhoodForm(instance=neighborhood)

    neighborhoods = Neighborhood.objects.all()
    cities = City.objects.all()
    data = {
        "form": form,
        "states": STATES,
        "cities": cities,
        "neighborhoods": neighborhoods
    }
    return render_to_response("neighborhood/inserir.html", data, context_instance=RequestContext(request))
  • 在模板中使用 form.state.value 来填充 "state" 字段的值。
{% extends "base.html" %}

{% block content %}
    <form action="" method="post" id="neighborhoodForm" name="neighborhoodForm">
        {% csrf_token %}
        <div>
            <label>State:</label>
            <select id="state" name="state" autofocus="autofocus">
                <option value=""></option>
                {% for item in states %}
                    <option value="{{ item }}"
                            {% if item == form.state.value %}
                            selected="selected"
                            {% endif %}>{{ item }}</option>
                {% endfor %}
            </select>
        </div>

        <div>
            <label>City:</label>
            <select id="city" name="city">
                <option value=""></option>
                {% for item in cities %}
                    <option value="{{ item.id }}"
                            {% if item.id == form.city.value|add:0 %}
                            selected="selected"
                            {% endif %}>{{ item.name }}</option>
                {% endfor %}
            </select>
        </div>
        <div>
            <label>Neighborhood Name:</label>
            <input type="text" id="name" name="name" value="{{ form.name.value|default_if_none:"" }}"/>
        </div>
        <div>
            <button type="submit" id="submit" name="submit" value="submit">Submit</button>
            <button type="submit" id="cancel" name="cancel" value="cancel">Cancel</button>
        </div>
    </form>

    <br/>

    <table border="1">
        <tr>
            <th>Neighborhood Name</th>
            <th>City</th>
            <th>State</th>
        </tr>
        {% for item in neighborhoods %}
        <tr>
            <td>{{ item.name }}</td>
            <td>{{ item.city.name }}</td>
            <td>{{ item.city.state }}</td>
        </tr>
        {% endfor %}
    </table>
{% endblock %}

通过上述修改,"state" 字段的值将被正确地初始化和保存,从而解决了在添加或编辑记录时出现的问题。