Django重设密码
密码太多真烦人,每次Django都会忘记密码。
好在Django提供了很方便的方法来修改密码1
python manage.py changepassword
密码太多真烦人,每次Django都会忘记密码。
好在Django提供了很方便的方法来修改密码1
python manage.py changepassword
在Django中,使用select_related和prefetch_related是两个很常见的优化手段。
举个例子最能说明问题。
首先建立如下model1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Category(models.Model):
name = models.CharField(max_length=30)
creat_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return u'%s' % self.name
class Tag(models.Model):
name = models.CharField(max_length=30)
creat_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return u'%s' % self.name
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=300, allow_unicode=True, unique=True)
content = models.TextField()
publish_time = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category)
tag = models.ManyToManyField(Tag, blank=True)
def __unicode__(self):
return u'%s' % self.title
之后编写序列化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer()
tag = TagSerializer(many=True)
class Meta:
model = Post
fields = ('id', 'title', 'slug', 'content', 'publish_time', 'category', 'tag', )
之后编写api1
2
3
4
5
6
7
8
9
class PostListAPI(generics.ListAPIView):
serializer_class = PostSerializer
model = Post
paginate_by = 10
def get_queryset(self):
return Post.objects.all().order_by('-publish_time')
编写url1
url(r'^api/posts/?$', PostListAPI.as_view(), name='post_list'),
之后在后台新建两篇文章,访问api, 可以看到访问的sql1
2
3
4
5DEBUG [24/Jul/2016 13:57:13] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."content", "blog_post"."publish_time", "blog_post"."category_id" FROM "blog_post" ORDER BY "blog_post"."publish_time" DESC; args=()
DEBUG [24/Jul/2016 13:57:13] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_category"."id", "blog_category"."name", "blog_category"."creat_time" FROM "blog_category" WHERE "blog_category"."id" = 1; args=(1,)
DEBUG [24/Jul/2016 13:57:13] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_tag"."id", "blog_tag"."name", "blog_tag"."creat_time" FROM "blog_tag" INNER JOIN "blog_post_tag" ON ("blog_tag"."id" = "blog_post_tag"."tag_id") WHERE "blog_post_tag"."post_id" = 2; args=(2,)
DEBUG [24/Jul/2016 13:57:13] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_category"."id", "blog_category"."name", "blog_category"."creat_time" FROM "blog_category" WHERE "blog_category"."id" = 1; args=(1,)
DEBUG [24/Jul/2016 13:57:13] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_tag"."id", "blog_tag"."name", "blog_tag"."creat_time" FROM "blog_tag" INNER JOIN "blog_post_tag" ON ("blog_tag"."id" = "blog_post_tag"."tag_id") WHERE "blog_post_tag"."post_id" = 1; args=(1,)
这里有两篇文章,访问tag和category表都分别访问了两次,如果是10篇文章,那访问tag和category则分别要10次。此时select_related和prefetch_related派上了用场。
查看select_related的文档,在返回QuerySet时,对于ForeignKey和OneToOneField等字段,通过添加select_related,可以把相关的对象在一次查询中查出,之后使用时就不需要再次查数据库。
还是看例子容易明白。对于上面的Post中category字段,因为是ForeignKey, 所以可以通过select_related查出,修改api如下1
2
3
4
5
6
7
8
9
10
11
class PostListAPI(generics.ListAPIView):
serializer_class = PostSerializer
model = Post
paginate_by = 10
def get_queryset(self):
queryset = Post.objects.all().order_by('-publish_time')
queryset = queryset.select_related('category')
return queryset
访问url, 查看sql1
2
3DEBUG [24/Jul/2016 13:58:29] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."content", "blog_post"."publish_time", "blog_post"."category_id", "blog_category"."id", "blog_category"."name", "blog_category"."creat_time" FROM "blog_post" INNER JOIN "blog_category" ON ("blog_post"."category_id" = "blog_category"."id") ORDER BY "blog_post"."publish_time" DESC; args=()
DEBUG [24/Jul/2016 13:58:29] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_tag"."id", "blog_tag"."name", "blog_tag"."creat_time" FROM "blog_tag" INNER JOIN "blog_post_tag" ON ("blog_tag"."id" = "blog_post_tag"."tag_id") WHERE "blog_post_tag"."post_id" = 2; args=(2,)
DEBUG [24/Jul/2016 13:58:29] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_tag"."id", "blog_tag"."name", "blog_tag"."creat_time" FROM "blog_tag" INNER JOIN "blog_post_tag" ON ("blog_tag"."id" = "blog_post_tag"."tag_id") WHERE "blog_post_tag"."post_id" = 1; args=(1,)
可以看到,对于category信息,在查询post的同时,也一起查出,减少了查询category表的操作。
查看Django的prefetch_related文档,了解到prefetch_related对于相关对象会进行一次独立的查询,然后在Python中把对象关联起来。所以prefetch_related可以用于many-to-many and many-to-one关系。
还是举例子,对于上面的tag, 可以使用prefetch_related来处理。修改api如下:1
2
3
4
5
6
7
8
9
10
11class PostListAPI(generics.ListAPIView):
serializer_class = PostSerializer
model = Post
paginate_by = 10
def get_queryset(self):
queryset = Post.objects.all().order_by('-publish_time')
queryset = queryset.select_related('category')
queryset = queryset.prefetch_related('tag')
return queryset
之后访问api,查看sql1
2DEBUG [24/Jul/2016 14:02:35] [django.db.backends:utils:execute:89] [None] (0.000) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."content", "blog_post"."publish_time", "blog_post"."category_id", "blog_category"."id", "blog_category"."name", "blog_category"."creat_time" FROM "blog_post" INNER JOIN "blog_category" ON ("blog_post"."category_id" = "blog_category"."id") ORDER BY "blog_post"."publish_time" DESC; args=()
DEBUG [24/Jul/2016 14:02:35] [django.db.backends:utils:execute:89] [None] (0.000) SELECT ("blog_post_tag"."post_id") AS "_prefetch_related_val_post_id", "blog_tag"."id", "blog_tag"."name", "blog_tag"."creat_time" FROM "blog_tag" INNER JOIN "blog_post_tag" ON ("blog_tag"."id" = "blog_post_tag"."tag_id") WHERE "blog_post_tag"."post_id" IN (2, 1); args=(2, 1)
可以看到对于tag的查询也只有一次。
从上面的例子可以看到,掌握select_related和prefetch_related的用法非常重要。本文的代码见test-django的blog
参看资料:
对于一个应用来说,日志是很重要的一部分。Django提供的logging功能,使用Python自带的logging模块来处理系统日志很方便。
下面是一份简单的配置。配置最主要的需求是能够看到Django查询数据库的sql语句。
1 | LOGGING = { |
Django提供了Q和F算子来方便查询。
在对queryset进行filter时,filter语句的关系是AND关系,要实现OR关系,则需要使用Q算子。
例如1
Q(question__startswith='Who') | Q(question__startswith='What')
相当于1
WHERE question LIKE 'Who%' OR question LIKE 'What%'
在实际开发中,常常需要对数据库中的某一个字段进行加减操作,如评论数,访问量等等。一种做法是把这个字段读出来,然后进行操作,之后再保存到数据库中,而F算子提供了简便的方法,它使这些操作直接在数据库中执行。
例如,在下面的例子中需要对stories_filed字段进行加1操作。1
2
3
4# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()
而使用F算子,则是1
2
3
4from django.db.models import F
reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_filed=F('stories_filed') + 1)
F算子的优势在于不需要使用Python, 而只是使用数据库来完成操作。
而F算子的另外一个好处是减少多线程下的变量问题。
在Wordpress迁移到Hexo遇到的问题中说过Wordpress迁移到Hexo时会遇到文件名问题,当时写了个Python脚本解决,现在有了更好的解决办法,于是记下。
在迁移的时候会生成1
2
3e8-af-ad-e8-a8-80-e7-89-b9-e6-80-a7-e8-bf-98-e6-98-af-e6-9c-89-e5-bf-85-e8-a6-81-e5-ad-a6-e4-b9-a0-e7-9a-84.md
e8-bd-af-e8-bf-9e-e6-8e-a5-e5-92-8c-e7-a1-ac-e8-bf-9e-e6-8e-a5.md
e8-bf-bd-e8-b8-aaquery-too-complex-not-enough-stack-e9-94-99-e8-af-af.md
这样的文件名, 原因是文件名是从URL转化而来。后来发现将URL进行encode之后就没有这个问题。在fork出的hexo-migrator-wordpress里解决了这个问题,在github上提交了一个pull request, 但一直没有被合并,匪夷所思。
不过还是可以直接安装我的仓库里的代码来解决这个问题。npm install https://github.com/dengshilong/hexo-migrator-wordpress.git --save
如果需要对提交的代码进行检查,可以使用hooks, 也就是钩子,如果需要在代码提交到参考后进行发布,也可以使用hooks.
在初始化git仓库时,在.git目录的hooks目录里提供了一些例子。
在我的学习笔记的仓库里,我定义了post-receive这个hooks, 当deploy出现在commit的说明信息中,就更新学习笔记。
1 | #file info |
参考资料:
Django提供的migrations功能将项目中的model与数据库表同步,方便开发。
在天涯的时候,有个同事建议我分享一下正则,只是那时候使用正则的开发经验还少,没有能力分享。
后来到杭州后,进入同花顺,使用正则解决了一些问题。也是在这里,我才知道,许多开发人员对于正则还不是很理解。
离开同花顺,来到天猪,做Web开发,使用正则的场景比之前多。在这里,同样发现同事的正则水平需要提高。前端组同学组织了两周一次的技术分享,后来发展到技术组的技术分享,于是我也给大家进行了一次正则分享。
在Django之cache的例子中,我们使用了缓存。但这里存在一个问题,当我们添加了数据后,因为缓存是有实效的,里面的数据没有更新。此时需要有一个机制,当我们更新的model的数据时,要清除缓存。Django提供的Signals可以满足这个需求。
在model保存和删除时,需要进行清除缓存。这里用到post_save和post_delete两个信号。
在area的models.py里添加如下代码1
2
3
4
5
6
7
8
9
10@receiver(post_delete, sender=Area)
@receiver(post_save, sender=Area)
def delete_cache(sender, **kwargs):
#清除cache
obj = kwargs['instance']
if obj.parent is None:
key = "city_list_0"
else:
key = "city_list_%s" % obj.parent_id
cache.delete(key)
之后在admin后台添加和删除数据时,缓存就会被清除。访问http://127.0.0.1:8000/area/api/city/list/1 时就可以读取到最新的数据
想自己尝试的可以下载test-django
在Django开发过程中,要实现REST api时,Django REST framework是一个很不错的模块。
拿前面的省/市/区(县)的例子来说,假设我们要提供一个返回某个省份下所有城市的api。
pip install djangorestframework进行安装, 在项目的settings的INSTALLED_APPS里添加’rest_framework’1 | from rest_framework import generics |
在代码里我们看到city_cache的调用,这里就是用的之前介绍的cache
在area应用里添加serizlizer.py文件,添加序列化, 序列化如何编写参看serializers
1 | from rest_framework import serializers |
在area中添加urls.py文件,内容如下
1 |
|
在项目的urls.py里添加area的urlurl(r'area/', include('area.urls'))
之后启动服务,就可以访问http://127.0.0.1:8000/area/api/city/list/1 看到结果。