Skip to main content
 首页 » 编程设计

python之如何在动态 Django ModelChoiceField 中设置初始值

2025年05月04日38insus

我正在尝试为网站创建一个简单的页面版本控制系统。我希望能够将一个页面传递到一个表单中,该表单指向与该页面相关的所有版本,并让表单下拉列表包含该页面的所有版本,并使初始页面版本成为理想版本在 View 中初始化。到目前为止,表单将填写页面的所有版本,但不会选择理想的版本作为初始项。

如果在 View 中初始化表单时字段被填充,我如何设置表单中字段的初始项?

研究:

我试过以下问题的答案,但都没有解决方案:

示例代码:

模型.py

from django.db import models 
 
from autoslug import AutoSlugField 
 
from django_enumfield import enum 
 
from ..users.models import User 
 
 
def write_page(name, submitter, content=''): 
    page = Page.objects.get_or_create(name=name)[0] 
    page_version = PageVersion.create(page, submitter, content) 
 
    return page_version 
 
 
class Status(enum.Enum): 
    DISABLED = 0 
    ACTIVE = 1 
 
 
class Page(models.Model): 
    name = models.CharField(max_length=100) 
    slug = AutoSlugField(populate_from='name', unique=True) 
    status = enum.EnumField(Status, default=Status.ACTIVE) 
    current_version = models.IntegerField(default=0) 
 
    def __unicode__(self): 
        name_str = "(name='{}', slug='{}')" 
        return name_str.format(self.name, self.slug) 
 
    @property 
    def versions(self): 
        status = ("status = {}".format(Status.ACTIVE),) 
        order_by = ("-version",) 
 
        return self.pageversion_set.extra(where=status, order_by=order_by) 
 
    @property 
    def all_versions(self): 
        order_by = ("-version",) 
 
        return self.pageversion_set.extra(order_by=order_by) 
 
    @property 
    def ideal(self): 
        try: 
            return PageVersion.objects.get(id=self.current_version) 
        except self.DoesNotExist: 
            return None 
 
    @property 
    def version(self): 
        if self.ideal: 
            return self.ideal.version 
 
        return 0 
 
    @property 
    def content(self): 
        if self.ideal: 
            return self.ideal.content 
 
        return '' 
 
    @property 
    def submitter(self): 
        if self.ideal: 
            return self.ideal.submitter 
 
        return None 
 
    @classmethod 
    def create(cls, name): 
        page_object = cls.objects.create(name=name) 
 
        return page_object 
 
 
class PageVersion(models.Model): 
    parent = models.ForeignKey(Page) 
    version = models.IntegerField(default=0) 
    content = models.TextField() 
    date = models.DateTimeField(auto_now_add=True) 
    status = enum.EnumField(Status, default=Status.ACTIVE) 
    submitter = models.ForeignKey(User) 
 
    def __unicode__(self): 
        name_str = "(name='{}', slug='{}', version={})" 
        return name_str.format(self.name, self.slug, self.version) 
 
    @property 
    def name(self): 
        return self.parent.name 
 
    @property 
    def slug(self): 
        return self.parent.slug 
 
    @classmethod 
    def create(cls, page, submitter, content='', make_current=True): 
        if page.all_versions: 
            current_max_version = max([i.version for i in page.all_versions]) 
        else: 
            current_max_version = 0 
 
        page_version_object = cls(parent=page, submitter=submitter, content=content) 
        page_version_object.version = current_max_version + 1 
        page_version_object.save() 
 
        if make_current: 
            page.current_version = page_version_object.id 
            page.save() 
 
        return page_version_object 

forms.py

from django import forms 
 
from .models import PageVersion 
 
 
class PageVersionForm(forms.Form): 
    version = forms.ModelChoiceField(queryset=None) 
 
    def __init__(self, *args, **kwargs): 
        page = kwargs.pop("page") 
        super(PageVersionForm, self).__init__(*args, **kwargs) 
        self.fields["version"].queryset = page.versions 

views.py

from django.shortcuts import get_object_or_404, render 
from django.http import HttpResponseRedirect, HttpResponseForbidden 
from django.core.urlresolvers import reverse 
 
from .models import Page, write_page 
from .forms import PageForm, PageVersionForm 
from ..helpers import get_pages 
 
 
def update_page(request, slug): 
    if not request.user.has_perm("can_edit_page"): 
        return HttpResponseForbidden() 
 
    page = get_object_or_404(Page, slug=slug) 
 
    if request.method == "POST": 
        form = PageForm(request.POST) 
 
        if form.is_valid(): 
            content = form.cleaned_data["content"] 
            user = request.user 
 
            write_page(name=page.name, submitter=user, content=content) 
 
            kwargs = {} 
            kwargs["slug"] = slug 
 
            return HttpResponseRedirect(reverse("pages:detail", kwargs=kwargs)) 
 
    else: 
        data = {} 
        data["content"] = page.content 
 
        form = PageForm(data) 
        context = {} 
 
        page_version_form = PageVersionForm(page=page) 
 
        context["form"] = form 
        context["page_version_form"] = page_version_form 
        context["page"] = page 
        context["user"] = request.user 
        context.update(get_pages()) 
 
        return render(request, "pages/page_update.html", context) 

请您参考如下方法:

您需要重置 choices 属性,因为一旦设置了查询集,它就会缓存在字段上,并且即使您更新 queryset 属性也不会重新生效。

def __init__(self, *args, **kwargs): 
    ... 
    self.fields['version'].queryset = page.versions 
    self.fields['version'].widget.choices = self.fields['version'].choices 

你能检查一下你的 Django 版本中的 ModelChoiceField 是否有一个名为 _set_queryset 的 setter 方法吗? ?它似乎在 github 中已修复。