模型的raw_id_fields

时间:2009-06-11 10:34:09

标签: django django-models django-forms

我有一个modelform,其中有一个字段是一个ForeignKey值,一个模型为40,000行。默认模型尝试创建一个包含40,000个选项的选择框,这至少是不理想的。当这个模型在formset工厂中使用时更是如此!

在管理员中,使用“raw_id_fields”可以轻松避免这种情况,但似乎没有相应的模型形式。我怎么能这样做?

这是我的模型形式:

class OpBaseForm(ModelForm):

    base = forms.CharField()

    class Meta:
        model = OpBase
        exclude = ['operation', 'routes']
        extra = 0
        raw_id_fields = ('base', )   #does nothing

第一个粗体线的工作原理是没有创建庞大的笨重的选择框,但是当我尝试保存此表单的字段集时,我收到错误:“OpBase.base”必须是“Base”实例。< / strong>为了保存modelform,'base'需要是Base实例。显然,Base主键的字符串表示是不够的(至少不是自动的)。我需要某种机制来将表单给出的字符串更改为Base实例。而这种机制必须在一个formset中工作。有任何想法吗?如果只有raw_id_fields可以工作,这就像蛋糕一样简单。但据我所知,它只在管理员中可用。

3 个答案:

答案 0 :(得分:14)

您还可以使用整个raw_id_field管理窗口小部件,以及管理页面具有的方便的js弹出搜索。你甚至不需要模型表格。方法如下:

import string
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django import forms
from models import MyModel

# Have to subclass widget b/c 
# django hardcodes a relative path to Admin Root URL: ../../..
class HardcodedURLForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
    def render(self, *args, **kwargs):
        original_render = super(HardcodedURLForeignKeyRawIdWidget, 
            self).render(*args, **kwargs)
        ADMIN_ROOT_URL = "/admin/"
        return string.replace(original_render,"../../../", ADMIN_ROOT_URL)


class FieldLookupForm(forms.Form):
    my_foreignkey_field = forms.CharField(max_length=10,
        widget=HardcodedURLForeignKeyRawIdWidget(
            MyModel._meta.get_field("foreignkey_field").rel))

将相关的管理员j添加到您的模板和中提琴

{% block header %}
<script type="text/javascript">window.__admin_media_prefix__ = "/static/admin/";</script>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.min.js"></script>
{% endblock %}

答案 1 :(得分:12)

为了扩展Voltaire上面的评论,django 1.4解决方案是:

from django.contrib import admin
admin.autodiscover()

from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django import forms

from .models import Post, Photo

class PostForm(forms.ModelForm):
    photo = forms.ModelChoiceField(
        Photo.objects.all(),
        widget=ForeignKeyRawIdWidget(Post._meta.get_field("photo").rel,admin.site)
    )

您需要的唯一额外的JavaScript是:

<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>

这里重要的是你在管理员上调用自动发现,否则你的RawIdWidget将没有链接。此外,ModelChoiceField还需要一个实际未使用的查询集。 ModelChoiceField比CharField更可取,因为CharField没有正确验证(尝试保存id而不是查找Photo实例)。

答案 2 :(得分:11)

您需要更改base字段的小部件,而不是字段类型。我认为这样可行:

class OpBaseForm(ModelForm):
    base = forms.ModelChoiceField(queryset=Base.objects.all(), 
                                  widget=forms.TextInput)

    class Meta:
        model = OpBase
        ... etc...