解决vim对django中的models类字段自动补全失效的问题

来源:互联网 发布:medibang paint mac 编辑:程序博客网 时间:2024/05/16 18:12

    首先我使用了pythoncomplete的最新的0.9版,在环境变量里面添加了DJANGO_SETTINGS_MODULE=settings,设置export PYTHONPATH=~/workspace/my_project/src。这时候我编写一个model类比如:

# file: ~/workspace/my_project/src/main/models.py

class Book(models.Model):

    name = models.CharField(max_length=100)

    title = 'book title'

 

然后在同一目录里面创建一个tt.py文件在里面创建这个类的实例

from main.models import *

 

b = Book()

b.<C-x><C-o>

 

这时候只能提示title这个字段而不会出现name字段,这个问题我反复实验了多次,发现和PYTHONPATH,tags都设置都没有关系。而是pythoncomplete这个插件自身编写有问题,如果你不让Book继承models.Model而是普通的object是可以提示出name字段,看了它的代码不是很明白所以我只是用一种补丁的方式来解决这个问题。打开$VIM/vim72/autoload/pythoncomplete.vim文件编辑其中的get_completions函数在

 

match = stmt[ridx+1:]

stmt = _sanitize(stmt[:ridx])

result = eval(stmt, self.compldict)

all = dir(result)

 

 

后面加入:

 

if result.__class__.__name__ == 'ModelBase':

                    module_name = result.__module__

                    class_name = result.__name__

                    module_instance = __import__(module_name, {}, {}, [class_name])

                    class_instance = getattr(module_instance, class_name)

                    instance = class_instance()

                    result = instance

                    all = dir(instance)

 

 

这里面的result就是b这个对象实例,all是它的所有属性。在上面的代码中我首先判断result的基类是否为django.db.models.base.ModelBase然后根据Book的__module__再重新动态创建一个实例出来,这样就可以得到所有的字段了。你可以把亲手修改以下看看是不是真的有效果了,如果起作用了你也不要太高兴,因为这只完成了第一步,不信你把b = Book()放到一个函数里面比如我们views.py中经常定义的函数中去你就发现改了上面的代码也没有什么作用,经过反复使用print绝技,终于定位到问题了,在evalsource函数的这段代码身上:

 

 for l in sc.locals:

            try:               

                exec(l) in self.compldict

            except:

                print "locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)

                dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))

 

 

print这句是我加的,方便查看具体错误。应该报错的错误是NameError,使用了未定义的Book。经过实验发现在一个函数里面如果加入import就会正常,比如:

def index(request):

 

    from main.models import *

    b = Book()

这是因为exec在执行的时候是要结合上下文的,如果你不在index函数中导入需要的module,Book肯定是未定义,所以解决这个问题其实很简单,将上面这段改为:

 

namespace = []

 

 

def evalsource(self,text,line=0):

        sc = self.parser.parse(text,line)

        src = sc.get_code()

        dbg("source: %s" % src)

        try: exec(src) in self.compldict

        except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))

        # patch for Model SubClass in fuction

        if not namespace:

            for item in src.split('/n'):

                if item.startswith('from') or item.startswith('import'):

                    namespace.append(item)

 

        for l in sc.locals:

            try: 

                namespace.append(l)    

                l = '/n'.join(namespace)

                exec(l) in self.compldict

            except:

                print "locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)

                dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))

 

 

 

因为在src这个变量已经包含了前面的源码,所以可以简单的过滤出来你源文件中的所有import,from语句,然后再在每次exec的时候把namespace加到它的前面就大功告成了。

 

说起来简单,做起来难。就这两个小问题折腾了我两天还好是搞定了,看上面的代码应该是有性能问题,好在neocomplcache插件有缓存不会让vim反应过慢。最后还要说个问题就是现在的index函数是在太简单了,因为正常的编码都会从数据库中取值比如:

b = Book.object.get(id = 1)

这时候是肯定没有任何提示了,当然有一个弱点的解决方法就是为了提示在定义一个b = Book() 先把代码写完了,然后在把这句删除了。不过更好的方发应该是根据django的源码想办法让pythoncomplete可以动态得到query后的实例对象,这个问题就等我以后有热情和精力再解决把。现在的这个提示功能我已经很满足了。

 

 

原创粉丝点击