您的位置:首页 > 编程语言 > Go语言

Django基础+优化

2013-06-13 15:14 453 查看
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
},
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: