Django Admin外键选择的自动完成

来源:互联网 发布:java写数据到txt 编辑:程序博客网 时间:2024/05/02 00:31

原创声明:本文除了标明引用的内容外,都为本人原创,请尊重本人的知识产权,不能用于商业用途。欢迎转载,转载请在文章开头处插入以下内容:

本文转载自"狂龙ing”的Blog,地址为“http://blog.csdn.net/kuanglong2016/article/details/17173939"

问题

Django Admin使用select来作为Model的外键数据输入,但是如果外键记录数目很大(比如>1000),我们就很难快速地选择指定的外键。最多能使用首字母来快速定位,但首字母相同的选项也很多,所以这种办法有效性有限,而且如果外键显示项为中文时就完全无效了,因此我们需要一种可以帮我们快速定位指定外键的办法。

背景知识

Django是使用人数最多的Python Web开发框架,其中Django Admin模块更是其中的精华,它可以帮助开发人员快速地建立一个模型数据的“增删改查”系统。而且在最新的1.6版中,该模块已经默认开启。(推荐大家使用)

思路和方法

由于Django Admin中默认使用select,本人一度考虑不使用Django Admin,而是自己做一个数据的“增删改查”后台系统,但是实在不舍得Django Admin的便捷性,于是祭出神Google大法,经过search and search,发现大多数人都推荐django-extensions和django-autocomplete-light这两个关于自动完成的包,于是决定使用拿来主义从中间挑一个。
以下就是我的先郁闷后雀跃的拿来之旅(参照文档http://django-extensions.readthedocs.org/en/latest/installation_instructions.html 和 http://djangosaur.tumblr.com/post/358055866/install-django-extensions):

我首先使用的是django-extensions(因为看到别人用起来好简单的)
1. 安装&测试:
>>>  pip install django-extensions>>>  python -c "import django_extensions; print django_extensions.VERSION"
2. 加入Django项目: 
INSTALLED_APPS = (    ...    'django_extensions',)
3. 在代码中使用django-extensions:在app/admin.py中添加django_extension
from django.contrib import adminfrom django_extensions.admin import ForeignKeyAutocompleteAdminimport models# Register your models here.class LessonAdmin(ForeignKeyAutocompleteAdmin):    related_search_fields = {        'xyClass': ('name',)    }admin.site.register(models.XYClass)admin.site.register(models.Lesson, LessonAdmin)
4. 完成后测试可以正常访问/admin/,但是页面还是没有任何变化,select也没变,通过Firebug查看可以看到错误如下

从错误提示可以看出,是没有正确的引入jQuery库,但是在Firebug的“脚本”标签栏中,明明白白可以看到有jQuery库。
Google之:从Django Admin文档和Mark van Lent的博客中发现,原来因为Django Admin本身需要jQuery库,但是为了不污染全局命名空间,使用django.jQuery作为jQuery库的名字空间(注:JavaScript中实际上是没有名字空间这个说法的,但是我不知道该用其它什么词语)。因此$就要用django.jQuery来代替,但是在使用其它的一些jQuery Plugin时,就会发生$ is undefined 错误,比如jQuery Autocomplete(如上图)。
Mark van Lent介绍了解决该问题的三种方法:
1. 再引入一份jQuery,这样就可以在全局命名空间中使用$;
2. 修改Plugin代码,用django.jQuery取代jQuery以及$;
3. 重新定义变量$或者jQuery。
因为其它方法2需要耗费很多时间,方法3不知道在哪里定义,所以我使用方法1。
在使用Django Admin时,如果需要添加其它JavaScript库,在Admin子类中添加Media类,如下:
from django.contrib import adminfrom django_extensions.admin import ForeignKeyAutocompleteAdminimport models# Register your models here.class LessonAdmin(ForeignKeyAutocompleteAdmin):    related_search_fields = {        'xyClass': ('name',)    }    class Media:        js = ("//code.jquery.com/jquery-1.10.2.min.js",) #在这里添加自己的jQuery库admin.site.register(models.XYClass)admin.site.register(models.Lesson, LessonAdmin)
使用Firebug测试,$已经可以使用,但是还是出现下面错误:


又Google了好一通,但是能力有限,无法解决。遂决定放弃,改用django-autocomplete-light

试用django-autocomplete-light
首先根据django-autocomplete-light在github上的链接,找到了它的文档http://django-autocomplete-light.readthedocs.org/en/v2/demo.html,并根据该文档指南一步一步往下操作,最后成功启动服务器./manage.py runserver 0.0.0.0:8001,文档中使用的是./manage.py runserver,但是因为这样的话就只能在本机上用浏览器访问,但是我是通过ssh在远程开发机上做的开发,所以需要使用地址和端口0.0.0.0:8001,这样才能在其它机器上使用浏览器访问(参考doc)。浏览器显示“A server error occurred. Please contact the administrator.” 服务器上显示错误AttributeError: 'module' object has no attribute 'GenericModelForm'   。因为指南说明有两个示例,test_project和test_remote_project,所以我想既然不是本地访问,可能应该使用test_remote_project才行。于是我打开两个console,一个在test_project目录中运行./manage.py runserver,另一个在test_remote_project目录中运行./manage.py runserver 0.0.0.0:8001。测试可以正常访问/admin/并使用test/test登录成功,但是在测试自动完成时,当我在添加Address的Form中的city项时,输入Hous时(我准备输入Houston的),弹出框里并没有我想要的城市名称提示,而是ValueError
检查服务器发现:
test_project显示错误
test_remote_project显示错误

然后又是一顿Google,但是本人水平实在有限,最后还是没有搞定,真的是准备放弃了,弄了这么久,居然还是一点成效都没有。但是转念一想,既然大家都推荐它,它应该还是很好用的,干脆也不用这个指南中的示例来测试了,直接放到自己的项目中试试看。
于是参照了另一篇文档安装文档http://django-autocomplete-light.readthedocs.org/en/latest/install.html,实施过程如下:


1. 安装:
>>>   pip install django-autocomplete-light
2. 加入Django项目:
INSTALLED_APPS = [    ...    'autocomplete_light',]
3. 在urls.py里的admin.autodiscover()前添加autocomplete_light.autodiscover()添加代码后的样子一般如下
import autocomplete_light# import every app/autocomplete_light_registry.pyautocomplete_light.autodiscover()import adminadmin.autodiscover()
4. 添加autocomplete_light.urls:在使用自动完成的时候,admin会使用ajax的方式与autocomplete_light.urls通信
from django.conf.urls import patterns, url, includeurlpatterns = patterns('',    ...    url(r'^autocomplete/', include('autocomplete_light.urls')),)
5. 修改admin/base_site.html,该文件一般都在/usr/local/lib/python[版本号]/dist-packages/django/contrib/admin/templates/admin里。先备份base_site.html,再用以下内容覆盖
{% extends "admin/base.html" %}{% block extrahead %}    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.js" type="text/javascript"></script>    {% include 'autocomplete_light/static.html' %}{% endblock %}
6. 注册Autocomplete类:添加app/autocomplete_light_register.py,内容一般如下
import autocomplete_lightfrom models import Person# This will generate a PersonAutocomplete classautocomplete_light.register(Person,    # Just like in ModelAdmin.search_fields    search_fields=['^first_name', 'last_name'],    # This will actually html attribute data-placeholder which will set    # javascript attribute widget.autocomplete.placeholder.    autocomplete_js_attributes={'placeholder': 'Other model name ?',},)
7. 为admin提供具有自动完成功能的ModelForms
from django.contrib import adminimport autocomplete_lightfrom models import Orderclass OrderAdmin(admin.ModelAdmin):    # This will generate a ModelForm    form = autocomplete_light.modelform_factory(Order)admin.site.register(Order)
按照指南中的说法,到这里应该已经可以实现外键的自动完成功能了。于是我便满怀期待地使用Admin去添加Order记录,但是还是没有任何变化,打开Firebug也没有看到任何错误。郁闷...真的出了郁闷还是郁闷....郁闷...真的太郁闷了...6个小时居然啥也没干成,我都快要崩溃了。不过真是应了这句“山穷水复疑无路,柳暗花明又一村”,就在我快要放弃的时候,我突然发现我打开的一个页面里的内容与上面有一点点不一样,大喜!!!(为什么还没有使就会大喜呢,嘿嘿嘿,说来惭愧,因为之前看到admin.site.register(Order)时就觉得纳闷,为什么不是Admin常用的admin.site.register(Order, OrderAdmin)呢,但是也没有深究,后来不成功也没有往这方面想。现在居然看到了我原来想的那种用法,当然很开心,感觉很可能问题就是它了)
from django.contrib import adminimport autocomplete_lightfrom models import Orderclass OrderAdmin(admin.ModelAdmin):    # This will generate a ModelForm    form = autocomplete_light.modelform_factory(Order)#admin.site.register(Order) #erroradmin.site.register(Order, OrderAdmin)
再次打开..:8000/admin页面,测试,成功!!!(截图如下)开心,狂喜!!!哈哈哈哈哈大笑大笑大笑


总结

这种通过自动完成外键的方法可以大大提高数据输入的效率,而且django-autocomplete-light不仅仅只用于Django Admin,在自定义页面中也可以使用。
(这篇博文是我第一次用这样叙事的方式来写,写得很乱,但是因为中途实在是太郁闷了,不写不快,希望各位看客见谅)

主要参考文献

 1. Installation instruction of django-extensions
 2. Install django-extensions!!!
 3. Mark van Lent's weblog
 3. Quick start of django-autocomplete-light