Django 拾遗
1.python_2_unicode_compatible装饰器
from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible # 当你想支持python2版本的时候才需要这个装饰器 class Question(models.Model): # ... def __str__(self): # 在python2版本中使用的是__unicode__ return self.question_text @python_2_unicode_compatible class Choice(models.Model): # ... def __str__(self): return self.choice_text
2.URLconf的命名空间 app_name
现实中很显然会有5个、10个、更多的app同时存在一个项目中。Django用URL name区分这些app之间的关系
from django.conf.urls import url from . import views app_name = 'polls' # 关键是这行 urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ] <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
3.404错误错误页
get_object_or_404() 替代get()
get_list_or_404() 替代filter()
from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
4.HttpResponseRedirect 当POST数据处理成功后,建议用此函数;
reverse 帮助我们避免在视图函数中硬编码URL;
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse from django.urls import reverse from .models import Choice, Question # ... def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # 发生choice未找到异常时,重新返回表单页面,并给出提示信息 return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes += 1 selected_choice.save() # 成功处理数据后,自动跳转到结果页面,防止用户连续多次提交。
# 例如'/polls/3/results/'
,其中的3是某个question.id
的值。重定向后将进入polls:results
对应的视图,并将question.id
传递给它 return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) from django.shortcuts import get_object_or_404, render def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question}) <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
5.查看一个包安装路径
python -c "import django; print(django.__path__)"
6.FileField upload_to
#upload_to
参数也可以接收一个回调函数
#user_directory_path
这种回调函数,必须接收两个参数,然后返回一个Unix风格的路径字符串。参数instace
代表一个定义了FileField
的模型的实例,说白了就是当前数据记录。filename
是原本的文件名。
def user_directory_path(instance, filename): #文件上传到MEDIA_ROOT/user_<id>/<filename>目录中 return 'user_{0}/{1}'.format(instance.user.id, filename) class MyModel(models.Model): upload = models.FileField(upload_to=user_directory_path) class MyModel(models.Model): # 文件被传至`MEDIA_ROOT/uploads`目录,MEDIA_ROOT由你在settings文件中设置 upload = models.FileField(upload_to='uploads/') # 或者 # 被传到`MEDIA_ROOT/uploads/2015/01/30`目录,增加了一个时间划分 upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
7.递归外键 self ,典型的例子就是评论系统!一条评论可以被很多人继续评论
class Comment(models.Model): title = models.CharField(max_length=128) text = models.TextField() parent_comment = models.ForeignKey('self', on_delete=models.CASCADE)
8.on_delete
on_delete 当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应操作。比如,你有一个可为空的外键,并且你想让它在关联的对象被删除时,自动设为null,可以如下定义: user = models.ForeignKey( User, models.SET_NULL, blank=True, null=True, ) 该参数可选的值都内置在django.db.models中,包括: CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!) PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常 SET_NULL:将外键字段设为null,只有当字段设置了null=True时,方可使用该值。 SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用。 DO_NOTHING:什么也不做。 SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
9.related_query_name
反向关联查询名。用于从目标模型反向过滤模型对象的名称。(过滤和查询在后续章节会介绍)
class Tag(models.Model): article = models.ForeignKey( Article, on_delete=models.CASCADE, related_name="tags", related_query_name="tag", # 注意这一行 ) name = models.CharField(max_length=255) # 现在可以使用‘tag’作为查询名了 Article.objects.filter(tag__name="important")
10.through(定义中间表)
如果你想自定义多对多关系的那张额外的关联表,可以使用这个参数!参数的值为一个中间模型。
最常见的使用场景是你需要为多对多关系添加额外的数据,比如两个人建立QQ好友的时间。
通常情况下,这张表在数据库内的结构是这个样子的:
中间表的id列....模型对象的id列.....被关联对象的id列 # 各行数据
如果自定义中间表并添加时间字段,则在数据库内的表结构如下:
中间表的id列....模型对象的id列.....被关联对象的id列.....时间对象列 # 各行数据
看下面的例子:
from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', ## 自定义中间表 through_fields=('group', 'person'), ) class Membership(models.Model): # 这就是具体的中间表模型 group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64)
上面的代码中,通过class Membership(models.Model)
定义了一个新的模型,用来保存Person和Group模型的多对多关系,并且同时增加了‘邀请人’和‘邀请原因’的字段。
through参数在某些使用场景中是必须的,至关重要,请务必掌握!
through_fields
接着上面的例子。Membership模型中包含两个关联Person的外键,Django无法确定到底使用哪个作为和Group关联的对象。所以,在这个例子中,必须显式的指定through_fields
参数,用于定义关系。
through_fields
参数接收一个二元元组('field1', 'field2'),field1是指向定义有多对多关系的模型的外键字段的名称,这里是Membership中的‘group’字段(注意大小写),另外一个则是指向目标模型的外键字段的名称,这里是Membership中的‘person’,而不是‘inviter’。
再通俗的说,就是through_fields
参数指定从中间表模型Membership中选择哪两个字段,作为关系连接字段。
11.自定义错误页面
- handler400 —— django.conf.urls.handler400。
- handler403 —— django.conf.urls.handler403。
- handler404 —— django.conf.urls.handler404。
- handler500 —— django.conf.urls.handler500。
首先,在根URLconf中额外增加下面的条目:
# 增加的条目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.page_error
然后在,views.py文件中增加四个处理视图:
def page_not_found(request): return render(request, '404.html') def page_error(request): return render(request, '500.html') def permission_denied(request): return render(request, '403.html') def bad_request(request): return render(request, '400.html')
注意:setttings 改成 DEBUG = False ALLOWED_HOSTS = ['*']
12.csv文件下载
import csv from django.http import StreamingHttpResponse class Echo(object): """An object that implements just the write method of the file-like interface. """ def write(self, value): """Write the value by returning it, instead of storing in a buffer.""" return value def some_view(request): # # Create the HttpResponse object with the appropriate CSV header. response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' writer = csv.writer(response) writer.writerow(['First row', 'Second row', 'Third row', 'Fourth row']) rows = (["Row {}".format(idx), str(idx + 10), str(idx + 20), str(idx + 30)] for idx in range(65536)) for row in rows: writer.writerow(row) return response # rows = (["Row {}".format(idx), str(idx+10),str(idx+20),str(idx+30)] for idx in range(65536)) # pseudo_buffer = Echo() # writer = csv.writer(pseudo_buffer) # response = StreamingHttpResponse((writer.writerow(['First row', 'Second row', 'Third row', 'Fourth row'])), # content_type="text/csv") # response = StreamingHttpResponse((writer.writerow(row) for row in rows), # content_type="text/csv") # response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' # return response
13.
CP: http://www.liujiangblog.com/course/django/96