2009-06-11 21 views
13

Tengo un modelo que tiene un campo que es un valor ForeignKey para un modelo que tiene 40,000 filas. La forma de modelo predeterminada intenta crear un cuadro de selección con 40,000 opciones, que, por decir lo menos, no es ideal. ¡Aún más cuando este modelo se usa en una fábrica de formset!raw_id_fields for modelforms

En el administrador, esto es fácilmente evitable mediante el uso de "raw_id_fields", pero no parece haber un equivalente de modelo. ¿Cómo puedo hacer esto?

Aquí es mi ModelForm:

class OpBaseForm(ModelForm): 

    base = forms.CharField() 

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

La primera línea en negrita funciona al no crear la enorme caja de selección difícil de manejar, pero cuando trato de guardar un conjunto de campos de este formulario, me sale el error: "OpBase. la base "debe ser una instancia" Base ". Para que se guarde el modelo, 'base' debe ser una instancia Base. Aparentemente, una representación de cadena de una clave primaria Base no es suficiente (al menos no automáticamente). Necesito algún tipo de mecanismo para cambiar la cadena que se le da a la forma, a una instancia de Base. Y este mecanismo tiene que funcionar en un formset. ¿Algunas ideas? Si solo raw_id_fields funciona, esto sería fácil como un pastel. Pero hasta donde puedo decir, solo está disponible en el administrador.

Respuesta

10

Necesita cambiar el widget para el campo base, no el tipo de campo. Creo que esto funcionaría:

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

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

Solo para aclarar, el atributo raw_id_field es un atributo ModelAdmin http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields no es un atributo de ModelForm. –

13

También se puede utilizar todo el widget raw_id_field admin, completo con el útil de búsqueda emergente js que tiene la página de administración. Ni siquiera necesitas un formulario modelo. Así es como:

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

Añadir los js de administración correspondientes a la plantilla, y la viola

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

En Django 1.4 parece que el constructor de ForeignKeyRawIdWidget ha cambiado: usar este código como lo hace lanzar __init __() toma exactamente 3 argumentos (2 dados). Si ha importado django.contrib.admin, puede pasar admin.site como el argumento que falta directamente a ForeignKeyRawIdWidget y no tener que envolverlo con la versión HardcodedUrl. – Voltaire

+0

Tienes que 'admin.autodiscover()' para que esto funcione. Estoy escribiendo esto en una respuesta porque falta un poco más. – chriscauley

+0

¿Qué sucede si no quieres redirigir a administrador? – thumbtackthief

12

Para ampliar el comentario de Voltaire anteriormente, la solución de Django 1.4 es:

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

Y el solo el javascript adicional que debería necesitar es:

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

Lo importante aquí es que llame a autodiscover en el administrador, de lo contrario, su RawIdWidget no tendrá un enlace. Además, ModelChoiceField requiere un conjunto de consulta, que en realidad no se usa. ModelChoiceField es preferible a CharField porque CharField no se valida correctamente (intenta guardar la identificación en lugar de buscar la instancia de Photo).

+0

¿Hay alguna forma de hacerlo sin redireccionar al sitio de administración? ¿Idealmente algo así como la función de autocompletar jquery? – thumbtackthief