2009-06-11 2 views
13

У меня есть модель, у которой есть одно поле, которое является значением ForeignKey модели, которая составляет 40 000 строк. Модель модели по умолчанию пытается создать поле выбора с 40 000 опциями, что, мягко говоря, не идеально. Тем более, когда эта модельная форма используется на фабрике для изготовления форм!raw_id_fields для modelforms

В администраторе это можно легко избежать, используя «raw_id_fields», но похоже, что эквивалент модели не эквивалентен. Как я могу это сделать?

Вот мой ModelForm:

class OpBaseForm(ModelForm): 

    base = forms.CharField() 

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

Первая полужирная линия работает не создавая огромный громоздкий переключателя, но когда я пытаюсь сохранить Fieldset этой формы, я получаю ошибку: «OpBase. base "должен быть" базовым "экземпляром. Чтобы сохранить форму модели, «базой» должен быть базовый экземпляр. По-видимому, строковое представление базового первичного ключа недостаточно (по крайней мере, не автоматически). Мне нужен какой-то механизм для изменения строки, заданной моей формой, для экземпляра Base. И этот механизм должен работать в форме. Есть идеи? Если бы работало только raw_id_fields, это было бы легко, как торт. Но насколько я могу судить, он доступен только в админке.

ответ

10

Необходимо ввести виджет для поля base, а не тип поля. Я думаю, что это будет работать:

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

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

Чтобы уточнить, атрибут raw_id_field является атрибутом ModelAdmin http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields не атрибутом ModelForm. –

13

Вы можете также использовать весь 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)) 

Добавьте соответствующий администратор JS в шаблон, и альт

{% 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

В Django 1.4 он выглядит так, как конструктор ForeignKeyRawIdWidget изменился - используя этот код, поскольку он вызывает его __init __() принимает ровно 3 аргумента (2 данных). Если вы импортировали файл django.contrib.admin, вы можете передать admin.site в качестве отсутствующего аргумента непосредственно в ForeignKeyRawIdWidget и не переносить его с помощью версии HardcodedUrl. – Voltaire

+0

Вы должны 'admin.autodiscover()' сделать эту работу. Я пишу это в ответ, потому что немного больше не хватает. – chriscauley

+0

Что делать, если вы не хотите перенаправлять администратора? – thumbtackthief

12

расширить свое присутствие на комментарии Вольтера выше, решение Джанго 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 не проверяет правильность (пытается сохранить идентификатор, а не искать экземпляр Photo).

+0

Есть ли способ сделать это без перенаправления на сайт администратора? В идеале что-то вроде функции автозаполнения jquery? – thumbtackthief