简体   繁体   中英

How to set initial value in a dynamic Django ModelChoiceField?

I'm trying to create a simple page versioning system for a website. I want to be able to pass a page into a form, which points to all of the versions related to the page, and have the form dropdown contain all of the versions for that page, and make the initial page version the ideal version when it is initialized in the view. So far, the form will fill with all of the versions for the page, but not select the ideal version as the initial item.

How do I set the initial item of a field in a form if the field gets populated while the form is initialized in the view?

Research:

I have tried the answers in the following questions, but none of them had the solution:

Example code:

models.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)

You need to reset choices property as it is cached on the field once queryset is set and is not revalidated even if you update queryset property.

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

Can you check if ModelChoiceField in your Django version has a setter method named _set_queryset ? It seems to be fixed in github.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM