Django基础+优化

来源:互联网 发布:游戏源码怎么变成游戏 编辑:程序博客网 时间:2024/05/16 13:47

https://docs.djangoproject.com/en/1.4/
Django系统搭建
一. 建立一个pydjango的项目
   初始的项目包括根目录的manage.py,以及一个以project name命名的文件夹(包含settings.py, urls.py,和一个wsgi.py)
   如果project包含多个app,那么应该建立多个包,然后在每个包创建自己的urls设置和models,然后建立一个总的urls转发到各app的url中。

二、修改settings.py
     1. 数据库连接
          DATABASES = {
               'default': {
                    'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
                    'NAME': 'blogdemo',                      # Or path to database file if using sqlite3.
                    'USER': 'root',                      # Not used with sqlite3.
                    'PASSWORD': '111111',                  # Not used with sqlite3.
                    'HOST': '127.0.0.1',                      # Set to empty string for localhost. Not used with sqlite3.
                    'PORT': '3306',                      # Set to empty string for default. Not used with sqlite3.
               }
          }
     2. 时区和语言
          TIME_ZONE='Asia/Beijing'
          LANGUAGE_CODE='zh-cn'
     3.模板目录
          TEMPLATE_DIRS = (
          # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
          # Always use forward slashes, even on Windows.
          # Don't forget to use absolute paths, not relative paths.
          os.path.join(os.path.dirname(__file__), 'template').replace('\\','/'),
          '.',
          )
     4. 支持的app
          支持的app 注意包的路径.如果model没在这里注册的话,那么将syncdb则无法自动生成表
          INSTALLED_APPS = (
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.sites',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'django.contrib.admin',
          'django.contrib.admindocs',
          #上边的都是默认的
          'blogdemo',
          )
     5. media路径:保存媒体文件的路径(上传的文件保存路径等等)
          HERE = os.path.dirname(os.path.abspath(__file__))
          MEDIA_ROOT = os.path.join(HERE, 'media/').replace('\\','/')
          MEDIA_URL = '/media/
     6. 静态文件路径:css,js,image等等
          STATIC_ROOT = os.path.join(HERE, 'collectedstatic').replace('\\','/')
          STATIC_URL = '/static/'

          # Additional locations of static files
          STATICFILES_DIRS = (
               # Put strings here, like "/home/html/static" or "C:/www/django/static".
               # Always use forward slashes, even on Windows.
               # Don't forget to use absolute paths, not relative paths.
               os.path.join(HERE, 'static').replace('\\','/'),
          )
     
          https://docs.djangoproject.com/en/1.4/ref/contrib/staticfiles/

     7. 系统的最顶层url映射地址。默认的'urls'是与manage.py同级目录的urls.py
          ROOT_URLCONF = 'package.urls'
     8. wsgi配置:
          # Python dotted path to the WSGI application used by Django's runserver.
          WSGI_APPLICATION = 'config.wsgi.application'

三. 创建应用: blogdemo
     1. 建立一个models.py
          #!usr/bin/env python
          #coding: utf-8
          from django.db import models
          class Category(models.Model):
               name = models.CharField(max_length=32)
               def __unicode__(self):
                    return self.name
               class Admin:
                    pass
     2. 同步DB
          建立好这个model后(当然任何时候都可以执行下述的命令来创建数据库)
          python manage.py validate来验证是不是正确的models路径配置
          python manage.py sqlall blogdemo 来展示如何创建sql数据库脚本
          python manage.py syncdb 来创建数据库表
          python manage.py dbshell 进入db的shell
          python manage.py createsuperuser 创建超级用户
     3. 建立url配置:
          admin.autodiscover() #这一行是为了给管理员加具体模块操作的权限的,如果没有,那么管理员就无法对数据库中的model进行直接操作
          urlpatterns = patterns('',
               (r'^$', 'blogdemo.views.listArticle'),
               (r'^admin/', include(admin.site.urls)),
          )
     4. 注册管理员界面(如果需要的话)
          在app下建立admin.py文件 (settings中的'django.contrib.admin'是对应的管理admin的app,如果没添加到install_app中,那么要加上)
          admin.site.register(Category)
     5. 添加views.py与数据库和模板交互。这里只给了个获取全部article的
          from blogdemo.models import Article
          from django.shortcuts import render
          def listArticle(request):
               lists=Article.objects.all()
               return render(request,'article_list.html', {'object_list': lists})
     6. 建立模板。在已经创建的template下(看settings中的设置路径)
          article_list.html:
          {% if object_list %}
            {% for article in object_list %}
               <div class="article">
                 <div class="title"><a href="/blog/{{ article.id }}">{{ article.title }}</a></div>
               </div>
            {% endfor %}
          {% else %}
            <p>對不起沒有文章喔!</p>
          {% endif %}
     7. 运行服务 http://localhost:8000/admin和http://localhost:8000/
     8. 如果需要上传文件,则需要添加如下设计:
          urls中增加media url映射
          url(r'^media/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT}),

          model中增加类型:
          link = models.FileField(upload_to='rateTemplate/%Y%m%d%H%M%S/')

          settings中增加:
          HERE = os.path.dirname(os.path.abspath(__file__))
          MEDIA_ROOT = os.path.join(HERE, 'media/').replace('\\','/')
          MEDIA_URL = '/media/'

DjangoDB基本操作:
一、模型定义
     https://docs.djangoproject.com/en/1.4/topics/db/models/
     Django模型是django.db.models.Model的子类
     class Entry(models.Model):
     name = models.CharField(u'name', max_length=64, db_index=True, unique=True)
     def __unicode__(self):
        pass #定义print时对象展现的数据
    class Admin:
        pass #是否支持Django Admin管理
    class Meta:
          #权限,表明等可选信息 Meta options - https://docs.djangoproject.com/en/1.4/ref/models/options/
        db_table = 'metadata_keyword' #model在db中对应的表名
        permissions = (
            ("view_entry", "Can view entry list"), #除django自管理的权限外的新增权限
        )
二、Django模型字段类型清单:
     https://docs.djangoproject.com/en/1.4/ref/models/fields/#
     AutoField:自增整型字段。如果不指定主键的话,系统会自动添加一个自增主键字段到你的model
     BooleanField:布尔字段。
     CharField:用于较短的字符串,CharField有一个必填参数:CharField.max_length
     TextField:一个容量很大的文本字段
     CommaSeparatedIntegerField:用于存放逗号分隔的整数值。类似 CharField,必填参数:CharField.max_length
     DateField:可选参数:auto_now:对象被保存时的时间,表示 “last-modified” 时间戳;auto_now_add:通常用于表示对象创建时间。
     EmailField:一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数。
     FileField:一个文件上传字段。要求一个必须有的参数:upload_to,一个用于保存上载文件的本地文件系统路径,这个路径必须包含strftime formatting。
          在一个model中使用 FileField 或ImageField 需要以下步骤:
          1、在你的 settings 文件中,定义一个完整路径给 MEDIA_ROOT以便让 Django在此处保存上传文件。
          2、定义 MEDIA_URL 作为该目录的公共 URL。 要确保该目录对 WEB 服务器用户帐号是可写的。
          3、在Model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件。数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT)。
          4、使用 Django提供的 get__url 函数获取image或者文件的url。如果ImageField叫作mug_shot,模板中采用{{ object.get_mug_shot_url }}得到图像的绝对路径。
     FilePathField:选择指定目录按限制规则选择文件,有三个参数可选, 其中”path”必需的,这三个参数可以同时使用, 参数描述:path:必需参数,一个目录的绝对文件系统路径。 FilePathField 据此得到可选项目。 Example: “/home/images”;match:可选参数, 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名。 注意这个正则表达式只会应用到 base filename 而不是路径全名。 Example: “foo。*\。txt^”, 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif;recursive:可选参数, 是否包括 path 下全部子目录,True 或 False,默认值为 False。match 仅应用于 base filename, 而不是路径全名。 如:FilePathField(path=”/home/images”, match=”foo.*”, recursive=True)…会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
     FloatField:浮点型字段。 必须提供两个 参数, 参数描述:max_digits:总位数(不包括小数点和符号) decimal_places:小数位数。如:要保存最大值为 999 (小数点后保存2位),你要这样定义字段:models.FloatField(…,max_digits=5, decimal_places=2),要保存最大值一百万(小数点后保存10位)的话,你要这样定义:models.FloatField(…,max_digits=19, decimal_places=10)
     ImageField:类似 FileField, 不过要校验上传对象是否是一个合法图片。它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存。 该字段要求 Python Imaging 库。
     IntegerField:用于保存一个整数。
     IPAddressField:一个字符串形式的 IP 地址, (如 “202.1241.30″)。
     NullBooleanField:类似 BooleanField, 不过允许 NULL 作为其中一个选项。
     PhoneNumberField:美国风格电话号码校验的 CharField(格式:XXX-XXX-XXXX)。
     PositiveIntegerField:类似IntegerField,但取值范围为非负整数(0-无穷)
     PositiveSmallIntegerField:正小整型字段,取值范围较小。默认50以内,可以指定maxlength。
     SlugField:一个报纸术语. 只包含字母,数字,下划线和连字符.它们通常用于URLs。
     SmallIntegerField:类似 IntegerField, 不过只允许某个取值范围内的整数。(依赖数据库)
     TimeField:时间字段,类似于 DateField 和 DateTimeField。
     URLField:用于保存 URL。 若verify_exists参数为True(默认),给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应)。
     USStateField:美国州名缩写
     XMLField:XML字符字段,校验值是否为合法XML的 TextField,必须提供参数:schema_path:校验文本的 RelaxNG schema 的文件系统路径。

三、Django Field 选项:
     https://docs.djangoproject.com/en/dev/ref/models/fields/#model-field-types
     null :缺省设置为false.
     blank:该字段是否可以为空。如果为假,则必须有值。
     choices:一个用来选择值的2维元组。如SEX_CHOICES= ((‘F’,'Female’),(‘M’,'Male’),)
     core:为内联编辑设定的
     db_column: 如果设置了,则会修改列名
     db_index 如果为真将为此字段创建索引
     default:设定缺省值
     editable:如果为假,admin模式下将不能改写。缺省为真
     help_text:admin模式下帮助文档
     primary_key:设置主键,如果没有设置django创建表时会自动加上:
     radio_admin:用于admin模式下将select转换为radio显示。只用于ForeignKey或者设置了choices
     unique:数据唯一,unique字段会被Django自动加上索引
     unique_for_date:日期唯一,如下例中系统将不允许title和pub_date两个都相同的数据重复出现
     unique_for_month / unique_for_year:用法同上
     validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误
     on_delete:关联对象被删除时的行为。默认CASCADE。其他PROTECT、SET_NULL、SET_DEFAULT、SET() - https://docs.djangoproject.com/en/1.4/ref/models/fields/#django.db.models.ForeignKey.on_delete
     CASCADE: Cascade deletes; the default.

四、模型间关系
     Django模型间关系:Many-to-one,Many-to-Many,One-to-One
     1. Many-to-One:外键关联
          class UseCaseModel(models.Model):
               name = models.CharField(max_length=32)
               status = models.ForeignKey(UseCaseStatus)
          可选参数:
          related_name:方便代码阅读.(各种模型见关系都可用)
               如status = models.ForeignKey(UseCaseStatus,related_name=entries),则采用下述方式取得数据
               https://docs.djangoproject.com/en/1.4/topics/db/queries/#backwards-related-objects
               b = UseCaseModel.objects.get(id=1)
               b.entries.all() # Returns all Entry objects related to Blog. # b.entries is a Manager that returns QuerySets.
               b.entries.filter(headline__contains='Lennon')
          特殊的Many-to-one自关联的递归关系:name需要是self来表示,同时必须给定一个不同于本类名的related_name和verbose_name
          class KeywordCategory(models.Model):
               name = models.CharField(max_length=32, db_index=True, blank=False)
               parent = models.ForeignKey('self', verbose_name='parentCategory', related_name='parentCategory', blank=True, null=True)
     
     外键关系设置默认值:
     type = models.ForeignKey (EvaProductType, default=lambda: EvaProductType.objects.get( id=1))

     2. Many-to-Many: 多对多关联。只要在两个Model中的一方写即可,然后需要重新定义一下save方法
          class Keyword(models.Model):
          name = models.CharField(u'name', max_length=64, db_index=True, unique=True)
          category = models.ManyToManyField(KeywordCategory, blank=True, null=True,related_name='category')
          categorylist = []

          def save(self, *args, **kwargs):
               super(Keyword, self).save()
               for cg in self.categorylist:
                    cg = KeywordCategory.objects.get(id=cg.id)
                    self.category.add(cg)
     3. One-to-One:主键关联

五、增删改查:
     https://docs.djangoproject.com/en/1.4/topics/db/queries/#backwards-related-object
     1. 查询:
          sets = Model.Objects.all() #批量查询
          sets是一个惰性集合:在对该集合取值之前,无法知道该集合有哪些成员.     当经各种条件处理后的结果集符合你的要求时, 就可以对它进行取值操作.
     2. 定制结果子集:过滤器
          filter(**kwargs) 返回一个匹配查询参数的新的结果集.
          exclude(**kwargs) 返回一个不匹配查询参数的新的结果集.
          例如:
               Poll.objects.filter(name__startswith="What").exclude(name_endswith="do") #查询允许多个条件参数, 逗号分隔的多个条件参数会被 "AND" 起来使用:
               polls.objects.filter(name__startswith="What",name_endswith="do") #如果需要用OR的方式,应该采用Q查询
               如果未提供查找类型, 系统就认为查找类型是 exact . 下面两个语句是等价的:
               Keyword.objects.get(id=14)
               Keyword.objects.get(id__exact=14)

          过滤器的参数为:
          exact 精确匹配: polls.get_object(id__exact=14).
          iexact 忽略大小写的精确匹配
          contains 大小写敏感的内容包含测试:
               polls.objects.filter(question__contains="spam")(仅PostgreSQL和 MySQL支持. Sqlite contains 等于icontains .)
          icontains 大小写不敏感的内容包含测试
          gt     大于: polls.objects.filter(id__gt=4).
          gte     大于等于.
          lt     小于.
          lte     小于等于.
          ne     不等于.
          in     位于给定列表中: polls.objects.filter(id__in=[1, 3, 4]) 返回一个 polls 列表(ID 值分别是 1或3或4).
          startswith     大小写敏感的 starts-with: polls.objects.filter(question__startswith="Would") .(仅PostgreSQL 和MySQL支持. SQLite 的LIKE 语句不支持大小写敏感特性. 对Sqlite 来说,``startswith`` 等于 istartswith )
          endswith     大小写敏感的 ends-with. (仅PostgreSQL 和 MySQL)
          istartswith     大小写不敏感的 starts-with.
          iendswith     大小写不敏感的 ends-with.
          range     范围测试: polls.objects.filter(pub_date__range=(start_date, end_date)) 返回 pub_date 位于 start_date 和 end_date (包括)之间的 所有民意测验
          year     对 date/datetime 字段, 进行精确的 年 匹配: polls.get_count(pub_date__year=2005) .
          month     对 date/datetime 字段, 进行精确的 月 匹配:
          day     对 date/datetime 字段, 进行精确的 日 匹配:
           isnull    True/False; 做 IF NULL/IF NOT NULL 查询: polls.objects.filter(expire_date__isnull=True).

          对于复杂的查询,采用Q(django.core.meta.Q)查询的方式:Q查询支持AND和OR查询:
          Q 对象可以使用 & 和 | 运算符进行组合. 当两个Q对象进行 & 或 | 运算时,会生成一个新的Q对象.
          举例来说语句:
                 Q(question__startswith='Who') | Q(question__startswith='What')等同于下面的 SQL WHERE 子句:
                 WHERE question LIKE 'Who%' OR question LIKE 'What%'
          查询函数可以接受一个或多个 Q 对象作为参数.如果提供有多个 Q 对象参数, 它们将被 "AND" 到一起.
          举例来说:(,号代表的默认的AND操作)
                 Entry.get_object(Q(name__startswith='Who'),(Q(name__endswith='do') | Q(name='Me'))等同于西面的SQL语句:
                 SELECT * FROM Entry WHERE name LIKE 'Who%' AND (name LIKE '%do' OR name = 'Me')
               
                 Entry.objects.filter(question__startswith='Who').exclude(Q(question='a') | Q(question='b'))
          但是注意:如果采用Q查询,那么这个过滤条件中的查询需要都为Q查询,不能有如下错误查询:
                 Entry.get_object(name__startswith='Who',Q(name__endswith='do'))
          但是注意:如果采用Q查询,那么这个过滤条件中的查询需要都为Q查询,不能有如        
                 Entry.get_object(name__startswith='Who',Q(name__endswith='do'))
     3. 结果集取值函数
          get(**kwargs)
          count() 返回结果集的行数.count()永远不会引发异常.
          in_bulk(id_list) 接受一个 ID 列表作为参数, 返回一个字典(每个ID映射一个具有给定ID的对象实例).
          latest(field_name=None)
          select_related() "追踪" 所有的关系对象, 并将它们预先存放到一个简单的缓存中.但是select_related()对many-to-many无效,支持depth=n来设置追踪深度.还可以指定只抓取某一个外键的Model的数据而不是全部select_related(tModel, depth)

          extra虽然扩展性不太好,但功能很强大,如果实体里需要需要增加额外属性,不得已时,通过extra来实现,也是个好办法。
               extra接收四个参数:params、where、tables、select. 其中params是给其他三个传递参数使用的
               select 关键字参数允许你选择特定的字段. 它是一个字典(属性名与 SQL 语句的映射). 举例来说:
                      extra参数主要是where的作用
                      Entry.objects.extra(
                            select={'choice_count': 'SELECT COUNT(*) FROM choices WHERE entry_id = entry.id'}
                      )
                      每个返回的Entry对象会有一个额外的属性: 'choice_count'   
                     
               where/tables
               如果你需要传递一个额外的 WHERE 子句 -- 比方进行一个非显式的连接--你可以使用 where 关键字参数. 如果你需要在查询中连接其它的表,你可以传递它们的名字给 tables 参数.
               where 和 tables 都接受一个字符串列表作为它们的值.所有的 where 参数都被 "AND" 到其它的查询条件中.
               举例来说:
                      Entry.objects.filter(question__startswith='Who').extra(where=['id IN (3, 4, 5, 20)'])
                      SELECT * FROM entry WHERE question LIKE 'Who%' AND id IN (3, 4, 5, 20);

                       Entry.objects.all().extra(select={"usename:'fist||last'"},where=["first||last like 'jeffrey%' "])
                       select first, last, (first||last) as username form entry where first||last like 'jeffrey%'

                       Entry.objects.all().extra(tables=['author_entry'],where={"last='auther_last' "})
                       select * from entry, author_entry a where a.last='auther_last' 
               params是python数据库产查询的最佳实践之一。python的所有批量处理语句都可以采用params这个参数。Params是一个list或者set,内容是(%s,%s)格式的补充原sql的数据
Django性能优化
一个项目大量优化工作一般是放在后期来做,早期的优化是“万恶之源”,这是前人总结的经验,不无道理。
如果事先理解Django的优化技巧,开发过程中稍稍留意,后期会省不少的工作量。
一、利用缓存
     可以用django自带的缓存,也可以找第三方的memcached。不过针对目前的项目django自带的就已经足够了。
     Django自带的cache:https://docs.djangoproject.com/en/1.3/topics/cache/#memcached

     系统优化的第一步,需要将改动频率较小的,在内存允许的情况下存储到缓存中。
     但是一定要注意cache的更新时间问题。对于一个应用来说,增删改时一定要修改缓存的数据。

      可以把常用的数据放到缓存中。但是对于缓存中的QuerySet对象(最好先prefetch__related和select_related),for循环的时候不会从数据库中取。但是filter和get后还是会从DB中取。这是因为filter和get后已经是新的对象了。

二、利用标准数据库优化技术:
通用的数据库优化:在系统建立前期就应该做的事情有两个
1. 索引,给关键的字段添加索引,如给表的关联字段,搜索频率高的字段加上索引等。
     Django建立实体Model的时候,支持给字段添加索引,具体参考Django.db.models.Field.db_index。
     Django.db.models.Field.db_index: 显示给字段加索引
     Django.db.models.Field.unique: 事实上,unique的数据在django中也设置了索引。
     Django Model的Meta中存在一个选项:index_together,可以设置多列索引。index_together对多对多无效
          index_together = [["pub_date", "deadline"],]

2. 使用适当的字段
     尽量使用符合应用条件的字段。能用BooleanField:布尔字段的尽量不用CharFiled

     Django字段类型清单:https://docs.djangoproject.com/en/1.4/ref/models/fields/#
     常用的字段如下:
     BooleanField:布尔字段,管理工具里会自动将其描述为checkbox。
     CharField:字符串字段,单行输入,用于较短的字符串,如要保存大量文本, 使用 TextField,CharField有一个必填参数:
     TextField:一个容量很大的文本字段, admin 管理界面用 多行编辑框表示该字段数据
     DateField:日期字段,admin 用一个文本框  来表示该字段数据(附带一个 JavaScript 日历和一个”Today”快捷按键。有下列额外的可选参数:auto_now:当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 “last-modified” 时间戳;auto_now_add:当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间。
     EmailField:一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数。
     IntegerField:用于保存一个整数。
     IPAddressField:一个字符串形式的 IP 地址, (如 “202.1241.30″)。
     NullBooleanField:类似 BooleanField, 不过允许 NULL 作为其中一个选项。 推荐使用这个字段而不要用 BooleanField 加 null=True 选项。 admin 用一个选择框  (三个可选择的值: “Unknown”, “Yes” 和 “No” ) 来表示这种字段数据。
     PhoneNumberField:一个带有合法美国风格电话号码校验的 CharField(格式:XXX-XXX-XXXX)。
     SmallIntegerField:类似 IntegerField, 不过只允许某个取值范围内的整数。(依赖数据库)
     URLField:用于保存 URL。 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应)。

三 理解并优化使用Django的QuerySets:
     https://docs.djangoproject.com/en/1.4/ref/models/querysets/
     对一个新创建的QuerySets来说, 缓存区是空的.当一个结果集第一次被取值, Django会进行一次数据库查询,并将查询结果放入缓存中,尽量重用它。
     举个简单的例子:
          entry = Entry.objects.get(id=1)
          entry.blog   # 博客实体第一次取出,是要访问数据库的
          entry.blog   # 第二次再用,那它就是缓存里的实体了,不再访问数据库

          entry = Entry.objects.get(id=1)
          entry.authors.all()   # 第一次all函数会查询数据库
          entry.authors.all()   # 第二次all函数还会查询数据库

     上述的两个例子的区别是,all()是调用函数,而Django中对于函数调用的返回结果,每次都去连接数据库,因此尽量复用这些结果集。
     常见的调用函数如all(),count(), exists(), values(), values_list(),in_bulk(id_list),latest,select_related(),extra等

四、数据库的工作就交给数据库本身计算,别用Python处理:
1. 使用 filter and exclude 过滤不需要的记录,这两个是最常用语句,相当是SQL的where。
     filter(**kwargs) 返回一个匹配查询参数的新的结果集.
     exclude(**kwargs) 返回一个不匹配查询参数的新的结果集.
          Entry.objects.filter(name__startswith="What").exclude(name_endswith="do")
          Entry.objects.filter(name__startswith="What",name_endswith="do") #查询允许多个条件参数, 逗号分隔的多个条件参数会被 "AND" 起来使用:
     过滤器的参数有:exact(精确)、iexact(精确但忽略大小写)、contains(%a%)、gt(>)、lt(<)、startswith(%a)、endswith(a%)、range(范围)等
          Entry.get_object(id__exact=14) #如果没有带过滤器参数,默认的参数为__exact精确匹配
     但是普通的过滤参数,只是支持“与”操作,对于复杂的操作需要采用Django的Q(django.core.meta.Q)查询的方式
     Q 对象可以使用 & 和 | 运算符进行组合. 当两个Q对象进行 & 或 | 运算时,会生成一个新的Q对象.
     举例来说语句:
          Q(question__startswith='Who') | Q(question__startswith='What')等同于下面的 SQL WHERE 子句:
          WHERE question LIKE 'Who%' OR question LIKE 'What%'
     查询函数可以接受一个或多个 Q 对象作为参数.如果提供有多个 Q 对象参数, 它们将被 "AND" 到一起.
     举例来说:(,号代表的默认的AND操作)
          Entry.get_object(Q(name__startswith='Who'),(Q(name__endswith='do') | Q(name='Me'))等同于西面的SQL语句:
          SELECT * FROM Entry WHERE name LIKE 'Who%' AND (name LIKE '%do' OR name = 'Me')
     但是注意:如果采用Q查询,那么这个过滤条件中的查询需要都为Q查询,不能有如下错误查询:
          Entry.get_object(name__startswith='Who',Q(name__endswith='do'))

2. 使用annotate对数据库做聚合运算,以及使用values只获取需要的数据.
     values函数,只会以dict的方式返回所需要的数据
     annotate函数支持的聚合函数有Sum、Max、Min、Count等
          mytasks = mytasks.values('eva_task_id').distinct().annotate(evaluatedKeywordNumber=Sum('status'), allKeywordNumber=Count('status')).order_by(sortType)
          {'eva_task_id':1,evaluatedKeywordNumber:12,allKeywordNumber='20'}
    #这句话是按照eva_task_id进行group by。对status进行sum和count操作

     不要用python语言对以上类型数据过滤筛选,同样的结果,python处理复杂度要高,而且效率不高, 白白浪费内存。
3. 使用QuerySet.extra()来批量修改结果集合
     extra虽然扩展性不太好,但功能很强大,如果实体里需要需要增加额外属性,不得已时,通过extra来实现,也是个好办法。
     extra接收四个参数:params、where、tables、select. 其中params是给其他三个传递参数使用的
     select 关键字参数允许你选择特定的字段. 它是一个字典(属性名与 SQL 语句的映射). 举例来说:
          Entry.objects.extra(
               select={'choice_count': 'SELECT COUNT(*) FROM choices WHERE entry_id = entry.id'}
          )
     每个返回的Entry对象会有一个额外的属性: 'choice_count'
     where/tables
     如果你需要传递一个额外的 WHERE 子句 -- 比方进行一个非显式的连接--你可以使用 where 关键字参数. 如果你需要在查询中连接其它的表,你可以传递它们的名字给 tables 参数.
     where 和 tables 都接受一个字符串列表作为它们的值.所有的 where 参数都被 "AND" 到其它的查询条件中.
     举例来说:
          Entry.objects.filter(question__startswith='Who').extra(where=['id IN (3, 4, 5, 20)'])
          SELECT * FROM entry WHERE question LIKE 'Who%' AND id IN (3, 4, 5, 20);
     更复杂的例子:不是整数id类型的
          values = ['名字一','名字二',...]
          columnName = 'name'
          condition = string.join([columnName + "='" + MySQLdb.escape_string(value) + "'" for value in values], ' OR ')
          sets = Model.objects.extra(where=[condition])
4. 使用原生的SQL语句:
     https://docs.djangoproject.com/en/dev/topics/db/sql/#executing-custom-sql-directly
     如果发现Django的ORM已经实现不了你的需求,而extra也无济于事的时候,那就用原生SQL语句吧。
     Django的Raw sql如下:
          Blog.objects.raw("select * from blog_blog")
    Python的sql: cursor
    默认的fetchone()和fecthmany()方法返回的数据格式为包含tuple的tuple(('user1', 1000L), ('user2', 1000L)),可使用下面的方法转化为dict,方便阅读
          def getDictResults(sql):
             try:
                    cursor = connection.cursor()
                    cursor.execute(sql)
                    desc = cursor.description
                    return [dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall() ] #fetchone()
             except:
                    trace_back()
             finally:
                    cursor.close()
     注意:cursor.execute()可以接受一个参数,也可以接受两个参数:
          (1) cursor.execute("insert into resource(cid,name) values(%s, %s)" , (12,name) );
               这种格式是接受两个参数,MySQLdb会自动替你对字符串进行转义和加引号,不必再自己进行转义
          (2) cursor.execute("insert into resource(cid,name) values(%s, %s)" % (12,name) );
               这种格式是利用python的字符串格式化自己生成一个query,也就是传给execute一个参数,此时必须自己对字符串转义和增加引号,即上边的语句是错误的,应该修改为:
               cursor.execute("insert into resource(cid,name) values(%s, '%s')" % (12,MySQLdb.escape_string(name)) );
     Python中批量执行函数:cursor.executemany(sql, args)
          sql = "insert into metadata_keyword_category(keyword_id, keywordcategory_id) values(%s, %s)"
          cursor.executemany(sql, unExistRelations)
    自己写raw sql时,要注意Django的事务处理: http://www.yihaomen.com/article/python/267.htm

五、如果需要就一次性取出你所需要的数据:
     单一动作(如:同一个页面)需要多个关联表中的数据而需要多次连接数据库时,最好一次性取出所有需要的数据,减少连接数据库次数。
     此类需求推荐使用QuerySet.select_related()一次性的抓取所有想关联的数据并放在内存中。

     不过此时需要注意的是,select_related()是直接获取数据而不仅仅是放在缓存中,因此别取出你不需要的东西,模版templates里往往只需要实体的某几个字段而不是全部,所以尽量使用QuerySet.values() 和 values_list()来简化数据

六、尽量减少数据库的连接数:
     1、使用 QuerySet.update() 和 delete(),这两个函数是能批处理多条记录的,适当使用它们事半功倍;
          如果可以,别一条条数据去update delete处理。
          # Update all the headlines with pub_date in 2007.
          Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
          Entry.objects.extra(where=['id IN ' + str("(" + ids + ")") + '']).delete()
     2、使用bulk_create进行批量插入进度
          Entry.objects.bulk_create([
                 Entry(headline="Python 3.0 Released"),
                 Entry(headline="Python 3.1 Planned")
          ])
          优于
          Entry.objects.create(headline="Python 3.0 Released")
          Entry.objects.create(headline="Python 3.1 Planned")
          bulk_create的不足:
          1. 不能有主键冲突的插入,否则出错的话全部都插入不进去。因此插入之前不确定的一定要查询一次。然后利用list的different方法取差集
          2. bulk_create只能单表更新,且不调用Model中重载的save()方法,只调用Django初始的save()。
               one-to-many,先插一次外键表,在插Model表。
               many-to-many,先插两个基本表,在用原生Sql插中间表

     3、对于一次性取出来的关联记录,获取外键的时候,直接取关联表的属性,而不是取关联属性,如:
          entry.blog.id 优于 entry.blog_id(减少一次数据库连接)
     4、 批量添加Many-to-Many的关系:
          my_band.members.add(me, my_friend)
          优于
          my_band.members.add(me)
          my_band.members.add(my_friend)

Django事物:基于django ORM的和基于SQL的
     http://docs.oneele.com/django/topics/db/transactions.html
     http://docs.oneele.com/django/topics/db/transactions.html
一、基于django ORM的事物
     1. django默认事务, 是自动处理的(@transaction.autocommit),所有改动会被立即提交,没有隐藏的rollback
     2. django使用了transaction中间件来完成,必须在settings.py中配置.
     MIDDLEWARE_CLASSES = (
          'django.middleware.cache.UpdateCacheMiddleware',
          'django.contrib.sessions.middleware.SessionMiddleware',
          'django.middleware.common.CommonMiddleware',
          'django.middleware.transaction.TransactionMiddleware',
     )
     但需要注意的是,配置与你中间件的配置顺序是有很大关系的。在TransactionMiddleware之后的所有中间件都会受到事务的控制。
     另外 TransactionMiddleware 只对 default 的数据库配置有效,如果要对另外的数据连接用这种方式,必须自己实现中间件。
     3.自己来控制事务.在settings.py 中不用配置TransactionMiddleware 中间件了, 基本采用装饰模式来实现。
          a)@transaction.autocommit,django默认的事务处理, 采用此装饰模式会忽略掉全局的transaction 设置
          b) @transaction.commit_on_success 在一个方法中,所有工作完成后,提交事务。
          c) commit_manually(),完全自己处理,但如果你没有调用commit()或者rollback(),将会抛出TransactionManagementError 异常.
               @transaction.commit_manually(using="my_other_database")
二、基于SQL的事务处理
     通常是比较复杂的SQL,用ORM处理不方便的时候用的。或者是大批量SQL语句执行,比较在意效率的情况下用。
     def batch_execsql(sqlarray):
          cursor = connection.cursor() # 得到处理的游标对象
          ret=""
          try:
               for sql in sqlarray:
                    cursor.execute(sql)
               transaction.commit_unless_managed() # 这是重点,没有这条语句,就不会commit 。
          except Exception,e: #简单的异常处理,可以忽略
               ret=str(e)
          cursor.close()
          return ret #有异常则返回异常,否则返回为空字符串
     由上面可以看出transaction.commit_unless_managed()的重要性,这是自定义SQL 语句情况下处理事务的方法.
     上面的例子中的 sqlarray 表示一个list,里面有很多自己写的SQL 语句,而这些语句要求在一个事务中完成。
三、基于ORM与SQL的混合事物处理
     @commit_on_success
     def save_company_callinfo(request):
          response=HttpResponse()
          try:
                 #ORM
                 model1.save()
                 model2.save()
                 ...
                 #==自定义SQL 部分====
                 sqlarray=[]
                 sqlarray.append("insert into table .......")
                 sqlarray.append("update table set.......")
                 ret=batch_execsql(sqlarray)
                 if len(ret)>0:
                      transaction.rollback()
                      response.write('{"status":"no","error":"%s"}' % ('add call information error',))
                 else:
                      response.write('{"status":"no","error":"%s"}' % ('',))
          except Exception,e:
                 response.write('{"status":"no","error":"%s"}' % (str(e),))
          return response

Django连接多数据库:
http://docs.oneele.com/django/topics/db/multi-db.html
一、基本配置与设置
     1. 配置多数据库:settings中添加
     DATABASES = {
          'default': {
               'NAME': 'app_data',
               'ENGINE': 'django.db.backends.postgresql_psycopg2',
               'USER': 'postgres_user',
               'PASSWORD': 's3krit'
          },
          'users': {
               'NAME': 'user_data',
               'ENGINE': 'django.db.backends.mysql',
               'USER': 'mysql_user',
               'PASSWORD': 'priv4te'
          }
     }
     2. 同步数据库
     syncdb 管理命令一次只操作一个数据库。缺省情况下,它操作 default 数据库。
     --database 参数,可以同步不同的 数据库。所以要同步我们例子中的所有数据库的所有模型可以使用如下命令:
     ./manage.py syncdb
     ./manage.py syncdb --database=users
     如果你不是同步所有的程序到同一个数据库中,你可定义一个 数据库路由 来为指定的模型实施特定的控制 策略。
     如果你要精细地控制同步,那么还有一种方式是修改 sqlall的输出,手工在 数据库中执行命令,命令如下:
     ./manage.py sqlall sales | ./manage.py dbshell
二、使用多数据库
     Author.objects.using('default').all()
     Author.objects.using('other').all()
     my_object.save(using='legacy_users')
     User.objects.using('legacy_users').get(username='fred').delete()

Django日志:
     formatters 用来配置日志打印的格式
     handlers 定义具体处理日志的方式,可以定义很多种,"default" 就是默认方式,"console" 就是打印到控制台方式.
     loggers 用来配置用哪几种handlers处理日志,比如同时需要输出日志到文件和到控制台,那就配置两种handlers.
     #有一点必须注意的是 loggers 类型 为"django" 这将处理所有类型的日志 -- 默认执行
     django会根据包的层次结构去找应该用那个logger来处理日志,如果本身这个module没有logger,就查找父类是否有logger,直至最顶层。如果找不到,就不输出日志。
     举例说明:
     views.py的路径为demo.app.comments.view
     import logging
     logger = logging.getLogger('demo.app')
     logger.info('aaa')
     logger.error('error occurs')
     这个时候会找到demo.app去处理,虽然没有定义 "demo.app.comment.views" 这样的logger,它会再他的父类找。
     更友好的方式
     logger = logging.getLogger(__name__) # 这里用__name__通用,自动检测.
     logger.info('aaa')

     如果既想看自己在程序中输出的日志,又想看django自己输出的日志,特别是sql语句:
     import logging
     logger = logging.getLogger('django') # 这里用__name__通用,自动检测.
     logger.info('aaa')
     LOGGING = {
          'version': 1,
          'disable_existing_loggers': True,
          'formatters': {
               'standard': {
                    'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s'
               },
          },
          'filters': {
          },
          'handlers': {
               'mail_admins': {
                    'level': 'ERROR',
                    'class': 'django.utils.log.AdminEmailHandler',
                    'include_html': True,
               },
               'default': {
                    'level':'DEBUG',
                    'class':'logging.handlers.RotatingFileHandler',
                    'filename': os.path.join(STATIC_ROOT+'/logs/','all.log'), #或者直接写路径:'c:\logs\all.log',
                    'maxBytes': 1024*1024*5, # 5 MB
                    'backupCount': 5,
                    'formatter':'standard',
               },
               'console':{
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'formatter': 'standard'
               },
               'request_handler': {
                    'level':'DEBUG',
                    'class':'logging.handlers.RotatingFileHandler',
                    'filename': os.path.join(STATIC_ROOT+'/logs/','script.log'), #或者直接写路径:'filename':'c:\logs\request.log''
                    'maxBytes': 1024*1024*5, # 5 MB
                    'backupCount': 5,
                    'formatter':'standard',
               },
               'scprits_handler': {
                    'level':'DEBUG',
                    'class':'logging.handlers.RotatingFileHandler',
                    'filename': os.path.join(STATIC_ROOT+'/logs/','script.log'), #或者直接写路径:'filename':'c:\logs\script.log'
                    'maxBytes': 1024*1024*5, # 5 MB
                    'backupCount': 5,
                    'formatter':'standard',
               },
          },
          'loggers': {
               'django': {
                    'handlers': ['default','console'],
                    'level': 'DEBUG',
                    'propagate': False
               },
               'demo.app':{
                    'handlers': ['default','console'],
                    'level': 'DEBUG',
                    'propagate': True
               },
               'django.request': {
                    'handlers': ['request_handler'],
                    'level': 'DEBUG',
                    'propagate': False
               },
               'scripts': { # 脚本专用日志
                    'handlers': ['scprits_handler'],
                    'level': 'INFO',
                    'propagate': False
               },
          }
     }