django疑难杂症

为什么80%的码农都做不了架构师?>>>   hot3.png

1.这个问题折磨我很久了(用models中定义的class访问其他业务创建的没有主键的table都有这问题)

查看文本打印?

  1. def testRawSql(uid):  

  2.     from boosencms.sinaweibo.analysis.models import WomAccount  

  3.     accounts = WomAccount.objects.raw("select userId as uid, token as access_token, secret as expire_in from account_ProviderUserToken where userId = %s" % uid)  

  4.     print accounts  

  5.     for account in accounts:  

  6.         print account  

查看文本打印?

  1. <RawQuerySet: 'select userId as uid, token as access_token, secret as expire_in from account_ProviderUserToken where userId = 2124561663'>  

  2. Traceback (most recent call last):  

  3.   File "manage.py", line 14, in <module>  

  4.     execute_manager(settings)  

  5.   File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/core/management/__init__.py", line 459, in execute_manager  

  6.     utility.execute()  

  7.   File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/core/management/__init__.py", line 382, in execute  

  8.     self.fetch_command(subcommand).run_from_argv(self.argv)  

  9.   File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/core/management/base.py", line 196, in run_from_argv  

  10.     self.execute(*args, **options.__dict__)  

  11.   File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/core/management/base.py", line 232, in execute  

  12.     output = self.handle(*args, **options)  

  13.   File "/home/dongsong/boosencms/src/boosencms/../boosencms/sinaweibo/analysis/management/commands/testapi.py", line 31, in handle  

  14.     testRawSql(2124561663);  

  15.   File "/home/dongsong/boosencms/src/boosencms/../boosencms/sinaweibo/analysis/management/commands/testapi.py", line 21, in testRawSql  

  16.     for account in accounts:  

  17.   File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/query.py", line 1480, in __iter__  

  18.     raise InvalidQuery('Raw query must include the primary key')  

  19. django.db.models.query_utils.InvalidQuery: Raw query must include the primary key  


原因:

self.model.objects.raw() expects the query result to contain primary keys from the modelself.model, so it can turn these into a list of objects for the function result.

解决办法:

https://docs.djangoproject.com/en/dev/topics/db/sql/#executing-custom-sql-directly

查看文本打印?

  1. def my_custom_sql():  

  2.     from django.db import connection, transaction  

  3.     cursor = connection.cursor()  

  4.   

  5.     # Data modifying operation - commit required  

  6.     cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])  

  7.     transaction.commit_unless_managed()  

  8.   

  9.     # Data retrieval operation - no commit required  

  10.     cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])  

  11.     row = cursor.fetchone()  

  12.   

  13.     return row  

或者(多数据库)

查看文本打印?

  1. from django.db import connections  

  2. cursor = connections['my_db_alias'].cursor()  

  3. # Your code here...  

  4. transaction.commit_unless_managed(using='my_db_alias')  

list转dict

查看文本打印?

  1. def dictfetchall(cursor):  

  2.     "Returns all rows from a cursor as a dict"  

  3.     desc = cursor.description  

  4.     return [  

  5.         dict(zip([col[0for col in desc], row))  

  6.         for row in cursor.fetchall()  

  7.     ]  

改进后的代码:

def testRawSql(uid):
    from boosencms.sinaweibo.analysis.models import WomAccount
    from django.db import connections, transaction
    cursor = connections['wom'].cursor()
    cursor.execute("select userId as uid, token as access_token, secret as expire_in from account_ProviderUserToken where userId = %s" % uid)
    transaction.commit_unless_managed(using='wom')
    #print cursor.fetchall()
    desc = cursor.description
    print [dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()]

结果:

[dongsong@bogon boosencms]$ vpython manage.py testapi
/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/conf/__init__.py:75: DeprecationWarning: The ADMIN_MEDIA_PREFIX setting has been removed; use STATIC_URL instead.
  "use STATIC_URL instead.", DeprecationWarning)
[{'access_token': u'1895748860', 'expire_in': u'79a5a918c3161847ca49e4ff8646904a', 'uid': u'2124561663'}]



参考:

http://stackoverflow.com/questions/2909869/error-while-executing-query


2.修改模型层从父类继承来的字段

直接改写报错如下(CustomizeModel没有任何显式的属性,那么默认会有id属性作为主键,子类WomStatusPostQueue定义了id字段则报错)

查看文本打印?

  1. django.core.exceptions.FieldError: Local field 'id' in class 'WomStatusPostQueue' clashes with field of similar name from base class 'CustomizeModel'  

实际代码不方便贴出来,贴一个范例吧

查看文本打印?

  1. class Place(models.Model):  

  2.     name = models.CharField(max_length=20)  

  3.     rating = models.DecimalField()  

  4. class LongNamedRestaurant(Place):  

  5.     def __init__(self, *args, **kwargs):  

  6.         super(LongNamedRestaurant, self).__init__(*args, **kwargs)  

  7.         name = models.CharField(max_length=255)  

  8.         name.contribute_to_class('name'self)  

参考:http://stackoverflow.com/questions/2344751/in-django-model-inheritance-does-it-allow-you-to-override-a-parent-models-a

            https://docs.djangoproject.com/en/1.1/topics/db/models/#field-name-hiding-is-not-permitted


3.django的mysql重连

a>>>常规用法(save/update/delete)的断线重连

mysql关闭,django在xxRecord.save()时候会抛出与下面类似的一些错误(对于cursor.execute()和xxModel.objects.raw()不管用)

查看文本打印?

  1. <class 'django.db.utils.DatabaseError'>:(2006'MySQL server has gone away')  

  2. <class 'django.db.utils.DatabaseError'> : (2013'Lost connection to MySQL server during query')  

  3. <class '_mysql_exceptions.OperationalError'>,(2003"Can't connect to MySQL server on '127.0.0.1' (111)")  

  4. <class '_mysql_exceptions.OperationalError'>,(2002"Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)")  


分析如下:

1>进程启动默认会有数据库连接启动(即使代码里没有数据库操作)
2>save()时如果检测到没有数据库连接(比如在连接闲置期间数据库重启了)则自动重连,业务逻辑感觉不到
3>save()做数据库连接失败则抛出异常,不会重复尝试

结论:

如需要重复尝试数据库连接(一般http服务当然不需要,定制的一个command服务等可能会需要),可以在save()、update()、delete()等方法前后加上异常捕捉并重复的save()/update()/delete();

这样改动大,更好的方法是编写一个改写了save()/update()/delete()方法的model类,然后其他业务相关的model类从这里继承就ok了

b>>>对于如下使用sql进行操作的情况

查看文本打印?

  1. from django.db import connections  

  2. cursor = connections['my_db_alias'].cursor()  

  3. cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])  

  4. transaction.commit_unless_managed(using='my_db_alias')  

每次调用connections['my_db_alias'].cursor()或者connection.cursor(),django底层会自动获取数据库连接并返回,如果没有连接(可能mysql重启过了)则会当场建立连接并返回
如果我们拿着一个获取时有效而现在已经断开连接的cursor进行操作则会报错,需要捕获异常并重新获取cursor

c>>>对于xxModel.objects.raw()的断线重连,暂时不考虑了,不喜欢这种鸟用法


4.django settings.py 中设置DEBUG=True时 leak memory的一个问题,见另一篇文章


5.django多线程、多进程中数据库连接的问题:

多线程下,底层ConnectionHandler会为不同线程向各数据库建立单独的连接;

多进程(os.fork())下,子进程会继承父进程的连接,所以会有问题(可以在子进程中对所有继承来的数据库连接执行django.db.connections['xxx'].close(),然后该子进程会在需要的时候启动新的连接);

多进程(popen())下,子进程会有单独连接,因为子进程是独立的django程序

下面是一些测试代码和结果:

查看文本打印?

  1. # -*- coding: utf-8 -*-  

  2.   

  3. from django.core.management.base import BaseCommand, CommandError  

  4. import time, copy, pprint, urllib, os, logging, threading, multiprocessing  

  5.   

  6. logger = logging.getLogger('main')  

  7.       

  8. def logic_body(sleepTime):  

  9.     ''''' 

  10.     为test_multi_thread_connection提供线程体 

  11.     '''  

  12.     from django.db import connections  

  13.     debugStr = 'pid(%d) ' % os.getpid()  

  14.     for dbAlias in connections.databases.keys():  

  15.         cn = connections[dbAlias]  

  16.         debugStr += '"%s":%d ' % (dbAlias, id(cn))  

  17.     logger.debug(debugStr)  

  18.     time.sleep(sleepTime)  

  19.   

  20. def test_threading_connection():  

  21.     ''''' 

  22.     测试使用therading模块时数据库连接 

  23.     '''  

  24.     from django.db import connections  

  25.     debugStr = 'pid(%d) ' % os.getpid()  

  26.     for dbAlias in connections.databases.keys():  

  27.         cn = connections[dbAlias]  

  28.         debugStr += '"%s":%d ' % (dbAlias, id(cn))  

  29.     logger.debug(debugStr)  

  30.           

  31.     threadList = list()  

  32.     for index in range(10):  

  33.         tmpThread = threading.Thread(target = logic_body, name = "thread-%d" % index, args = (10,))  

  34.         tmpThread.start()  

  35.         threadList.append(tmpThread)  

  36.       

  37.     time.sleep(5)  

  38.     threadNum = threading.active_count()  

  39.     logger.debug('************%d active threads**********' % threadNum)  

  40.     for tmpThread in threading.enumerate():  

  41.         logger.debug('*%s %d' % (tmpThread.name, tmpThread.ident))  

  42.     logger.debug('****************************************')  

  43.     for tmpThread in threadList:  

  44.         tmpThread.join()  

  45.       

  46. def test_multiprocessing_connection():  

  47.     ''''' 

  48.     测试使用multi-process模块时数据库连接 

  49.     '''  

  50.     from django.db import connections  

  51.     debugStr = 'pid(%d) ' % os.getpid()  

  52.     for dbAlias in connections.databases.keys():  

  53.         cn = connections[dbAlias]  

  54.         debugStr += '"%s":%d ' % (dbAlias, id(cn))  

  55.     logger.debug(debugStr)  

  56.   

  57.     processList = list()  

  58.     for index in range(10):  

  59.         tmpProcess = multiprocessing.Process(target = logic_body, name = "process-%d" % index, args = (10,))  

  60.         tmpProcess.start()  

  61.         processList.append(tmpProcess)  

  62.   

  63.     time.sleep(5)  

  64.     logger.debug('************%d active child processes**********' % len(processList))  

  65.     for tmpProcess in processList:  

  66.         logger.debug('*%s %d' % (tmpProcess.name, tmpProcess.ident))  

  67.     logger.debug('****************************************')  

  68.       

  69.     for tmpPorcess in processList:  

  70.         tmpPorcess.join()  

  71.       

  72. def test_os_fork_connection():  

  73.     ''''' 

  74.     测试使用os.fork()时数据库连接 

  75.     '''  

  76.     from django.db import connections  

  77.     debugStr = 'pid(%d) ' % os.getpid()  

  78.     for dbAlias in connections.databases.keys():  

  79.         cn = connections[dbAlias]  

  80.         debugStr += '"%s":%d ' % (dbAlias, id(cn))  

  81.     logger.debug(debugStr)  

  82.   

  83.     rt = os.fork()  

  84.     if rt == 0 : # child process  

  85.         logic_body(10)  

  86.     else:  

  87.         time.sleep(12)  

  88.   

  89. class Command(BaseCommand):  

  90.     help = "test sina api"  

  91.   

  92.     def handle(self, *args, **options):  

  93.             #test_db_leak_mem()  

  94.               

  95.             logger.debug('--------------------------------------')  

  96.             test_threading_connection()  

  97.             logger.debug('--------------------------------------')  

  98.             test_multiprocessing_connection()  

  99.             logger.debug('--------------------------------------')  

  100.             test_os_fork_connection()  

  101.             logger.debug('--------------------------------------')  

  102.             return   


[dongsong@localhost boosencms]$ vpython manage.py testapi

MainThread 2012-08-21 10:54:27,125 DEBUG [testapi.py:142]--------------------------------------
MainThread 2012-08-21 10:54:27,132 DEBUG [testapi.py:76]pid(8572) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
thread-0 2012-08-21 10:54:27,155 DEBUG [testapi.py:64]pid(8572) "default":34628880 "content":34629328 "wom":34629776 "weibo":34630224 
thread-1 2012-08-21 10:54:27,165 DEBUG [testapi.py:64]pid(8572) "default":34630992 "content":34631504 "wom":34636112 "weibo":34636560 
thread-2 2012-08-21 10:54:27,183 DEBUG [testapi.py:64]pid(8572) "default":34637840 "content":34638288 "wom":34638736 "weibo":34639184 
thread-3 2012-08-21 10:54:27,207 DEBUG [testapi.py:64]pid(8572) "default":34639632 "content":34640144 "wom":34640592 "weibo":34641040 
thread-4 2012-08-21 10:54:27,240 DEBUG [testapi.py:64]pid(8572) "default":34642384 "content":34642832 "wom":34643280 "weibo":34643728 
thread-5 2012-08-21 10:54:27,249 DEBUG [testapi.py:64]pid(8572) "default":34648656 "content":34649168 "wom":34649616 "weibo":34650064 
thread-7 2012-08-21 10:54:27,267 DEBUG [testapi.py:64]pid(8572) "default":34652880 "content":34653328 "wom":34653776 "weibo":34654224 
thread-6 2012-08-21 10:54:27,264 DEBUG [testapi.py:64]pid(8572) "default":34651024 "content":34651472 "wom":34651920 "weibo":34652432 
thread-9 2012-08-21 10:54:27,305 DEBUG [testapi.py:64]pid(8572) "default":34661520 "content":34661968 "wom":34662416 "weibo":34662864 
thread-8 2012-08-21 10:54:27,300 DEBUG [testapi.py:64]pid(8572) "default":34655568 "content":34656016 "wom":34660624 "weibo":34661072 
MainThread 2012-08-21 10:54:32,314 DEBUG [testapi.py:86]************11 active threads**********
MainThread 2012-08-21 10:54:32,314 DEBUG [testapi.py:88]*MainThread 140094652663552
MainThread 2012-08-21 10:54:32,314 DEBUG [testapi.py:88]*thread-0 140094492190464
MainThread 2012-08-21 10:54:32,315 DEBUG [testapi.py:88]*thread-1 140094479546112
MainThread 2012-08-21 10:54:32,315 DEBUG [testapi.py:88]*thread-6 140094352639744
MainThread 2012-08-21 10:54:32,315 DEBUG [testapi.py:88]*thread-5 140094363129600
MainThread 2012-08-21 10:54:32,318 DEBUG [testapi.py:88]*thread-8 140094331660032
MainThread 2012-08-21 10:54:32,318 DEBUG [testapi.py:88]*thread-2 140094384109312
MainThread 2012-08-21 10:54:32,318 DEBUG [testapi.py:88]*thread-3 140094469056256
MainThread 2012-08-21 10:54:32,319 DEBUG [testapi.py:88]*thread-9 140093914347264
MainThread 2012-08-21 10:54:32,319 DEBUG [testapi.py:88]*thread-7 140094342149888
MainThread 2012-08-21 10:54:32,319 DEBUG [testapi.py:88]*thread-4 140094373619456
MainThread 2012-08-21 10:54:32,319 DEBUG [testapi.py:89]****************************************
MainThread 2012-08-21 10:54:37,322 DEBUG [testapi.py:144]--------------------------------------
MainThread 2012-08-21 10:54:37,324 DEBUG [testapi.py:102]pid(8572) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,378 DEBUG [testapi.py:64]pid(8585) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,423 DEBUG [testapi.py:64]pid(8587) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,442 DEBUG [testapi.py:64]pid(8586) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,475 DEBUG [testapi.py:64]pid(8588) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,511 DEBUG [testapi.py:64]pid(8589) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,513 DEBUG [testapi.py:64]pid(8590) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,558 DEBUG [testapi.py:64]pid(8591) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,591 DEBUG [testapi.py:64]pid(8592) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,621 DEBUG [testapi.py:64]pid(8593) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:37,624 DEBUG [testapi.py:64]pid(8594) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:42,632 DEBUG [testapi.py:111]************10 active child processes**********
MainThread 2012-08-21 10:54:42,665 DEBUG [testapi.py:113]*process-0 8585
MainThread 2012-08-21 10:54:42,685 DEBUG [testapi.py:113]*process-1 8586
MainThread 2012-08-21 10:54:42,719 DEBUG [testapi.py:113]*process-2 8587
MainThread 2012-08-21 10:54:42,720 DEBUG [testapi.py:113]*process-3 8588
MainThread 2012-08-21 10:54:42,728 DEBUG [testapi.py:113]*process-4 8589
MainThread 2012-08-21 10:54:42,732 DEBUG [testapi.py:113]*process-5 8590
MainThread 2012-08-21 10:54:42,741 DEBUG [testapi.py:113]*process-6 8591
MainThread 2012-08-21 10:54:42,750 DEBUG [testapi.py:113]*process-7 8592
MainThread 2012-08-21 10:54:42,759 DEBUG [testapi.py:113]*process-8 8593
MainThread 2012-08-21 10:54:42,761 DEBUG [testapi.py:113]*process-9 8594
MainThread 2012-08-21 10:54:42,762 DEBUG [testapi.py:114]****************************************
MainThread 2012-08-21 10:54:47,655 DEBUG [testapi.py:146]--------------------------------------
MainThread 2012-08-21 10:54:47,660 DEBUG [testapi.py:128]pid(8572) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:47,688 DEBUG [testapi.py:64]pid(8597) "default":28730832 "content":33663568 "wom":34623312 "weibo":34627920 
MainThread 2012-08-21 10:54:57,708 DEBUG [testapi.py:148]--------------------------------------
MainThread 2012-08-21 10:54:59,719 DEBUG [testapi.py:148]--------------------------------------


6.目前为止django(1.4)还不支持组合主键,需要组合主键可能得另辟蹊径,网上找到如下两个实现了composite primary key的扩展包(同一项目,版本不同)

https://github.com/simone/django-compositekey/wiki

http://linux.softpedia.com/get/Internet/HTTP-WWW-/django-compositekey-79245.shtml

前者会对composite primary key建主键的同时在预计组合列上建一个组合唯一索引,然后在组合主键的每个列上还建一个索引。要不要再多建几个呢!?(2012.8.23记,以后的版本可能会改进吧)

后者会对预计composite primary key建立的是组合唯一索引,而不是主键,然后也在“组合主键”的每个列上建了一个索引,主键和索引完全一样吗?no,这也太委曲求全了!(2012.8.23记,以后的版本可能会改进吧)

相比之前,后者更能接受一些,所以选用了后者

最满足需求的方案是修改前者的源码:在compositekey/db/models/fields/multiplekey.py中把对组合主键中各列分别建索引的代码注释掉、把在组合主键上同时建立unique索引的代码也注释掉

查看文本打印?

  1. 61   

  2.  62         def lazy_init():  

  3.  63             self.fields = self._get_key_fields()  

  4.  64             assert isinstance(self.fields, (list, tuple)) and len(self.fields) > 0, \  

  5.  65                "%s must have a %s with at least 2 fields (%s)" % (cls.__name__, self.__class__.__name__,  

  6.  66                                                                   ",".join([f.name for f in self.fields]))  

  7.  67             names = [f.name for f in self.fields]  

  8.  68             cls._meta.ordering = cls._meta.ordering or names  

  9.  69   

  10.  70             #import pdb  

  11.  71             #pdb.set_trace()  

  12.  72             #if names not in cls._meta.unique_together:  

  13.  73             #    cls._meta.unique_together.append(names)  

  14.  74             #for field in self.fields: field.db_index=True  

  15.  75   

  16.  76             # get/set PK propery  

  17.  77             setattr(clscls._meta.pk.attname, property(get_composite_pk(self.fields), del_composite_pk(), del_composite_pk()))  

  18.  78   

  19.  79             # hack db_column for joins see compiler  

  20.  80             self.column = MultiColumn(self.fields)  

  21.  81             self.db_type = nope  

  22.  82             self.db_index = False  

  23.  83             self.not_in_db = True  

  24.  84             self.primary_key = True  

当用django访问其他程序的数据表时,如果该数据表没有主键(id或其他userId之类的东西)则django访问会失败,而我们又不可以修改该表,这个时候CompositeKey就起作用了,用上述模块在表上指定组合主键就可以访问了


7.django从innodb数据表(至少我遇到的问题只出现在innodb数据表上)取数据时,第一次取到数据data1,那么当数据发生变化以后再次获取时还是data1。解决办法:

查看文本打印?

  1. >>> MyModel.objects.count()  

  2. 885  

  3. # (Here I added some more data from another process.)  

  4. >>> MyModel.objects.count()  

  5. 885  

  6. >>> MyModel.objects.update()  

  7. 0  

  8. >>> MyModel.objects.count()  

  9. 1025  

或者修改mysql的配置,在/etc/my.cnf中加入

transaction-isolation=READ-COMMITTED

参考:http://stackoverflow.com/questions/3346124/how-do-i-force-django-to-ignore-any-caches-and-reload-data


转载于:https://my.oschina.net/shniu/blog/289302

你可能感兴趣的:(python,数据库)