注: 以下内容为学习 涂伟忠前辈的自强学堂的课堂笔记, 用来查漏补缺.
像
python3 -m venv ll_env
source ll_env/bin/activate
pip install django==?
django-admin startproject project_name .
python manage.py startapp app_name
python manage.py migrate
还有要在project_name
下的settings.py
里面添加所有的app, 等等...
这些我是不会去细说的
下面, 是一些我个人需要熟悉的点:
视图(VIEWS)
views.py 中GET请求的获取:
1, a = request.GET['a']
2, a = request.GET.get('a', 0)
最好是用第二种, 当没有传递 a 的时候默认 a 为 0, 增加容错率
URL
urlpatterns = [
url(r'^add/(\d+)/(\d+)/$', calc_views.add, name = 'add' ),
]
其中的name = 'add'
, 可以用于在 templates, models, views ……中得到对应的网址,相当于“给网址取了个名字”,只要这个名字不变,网址变了也能通过名字获取到。
比如在模板中, 对于这个请求链接可以写死:
<a href="/add/4/5/">计算 4+5</a>
但如果我们改变了URL中的正则表达, 那么这个模板中的链接就失效了.
所以前辈们给出了更好的解决方法:
<a href="{% url 'add' 4 5 %}">link</a>
其中的add
就是URL中的name所命的名
这个例子对应的是含参的, 不含参的直接把上边的 4 5
去掉即可
<a href="{% url 'add2' %}">link</a>
另外, 引入一个实例:
- 比如用户收藏夹中收藏的URL是旧的,如何让以前的 /add/3/4/自动跳转到现在新的网址呢?
要清楚我们需要的做的是把老的链接, 链接到也就是重定向到新的URL匹配中;
那么重定向的问题我们在视图里去写:
from django.core.urlresolvers import
def old_add2_redirect(request, a, b):
return HttpResponseRedirect(reverse('add2', args=(a, b)))
# URL中
url(r'^add/(\d+)/(\d+)/$', calc_views.old_add2_redirect),
url(r'^new_add/(\d+)/(\d+)/$', calc_views.add2, name='add2'),
这样就实现了一次我们对网站的更新换代
模板
{% include 'xxxx.html' %}
, 是包含其它文件的内容,就是把一些网页共用的部分拿出来,重复利用,改动的时候也方便一些,还可以把广告代码放在一个单独的html中,改动也方便一些,在用到的地方include进去
模板支持语法:
1, 列表, 字典, 类的实例的使用 {{ info_dict.site }}
2, 循环, 迭代显示可迭代对象内容 {% for item in List %}
3, 条件判断 {% if var >= 90 %}
4, 标签 {{ the_url }}
5, 过滤器
6, 逻辑运算符, 比较运算符 {% if num <= 100 and num >= 0 %}
7, 身份判断 {% if 'ziqiangxuetang' in List %}
下面这些是for循环中很有用的东西:
{% for .. %}
...
{% empty %}
...
{% endfor %}
变量 | 描述 |
---|---|
forloop.counter | 索引从 1 开始算 |
forloop.counter0 | 索引从 0 开始算 |
forloop.revcounter | 索引从最大长度到 1 |
forloop.revcounter0 | 索引从最大长度到 0 |
forloop.first | 当遍历的元素为第一项时为真 |
forloop.last | 当遍历的元素为最后一项时为真 |
forloop.parentloop | 用在嵌套的 for 循环中, |
注: 以下内容可以暂时不看
如果views 里面使用的不是render而是 render_ to_response, 有一个叫上下文渲染器的内容:
原文: 有时候我们想让一些内容在多个模板中都要有,比如导航内容,我们又不想每个视图函数都写一次这些变量内容,怎么办呢?
在1.8版本以后的settings.py中 的TEMPLATES下, 有 'django.template.context_processors.request',
这里的 context_processors
中放了一系列的 渲染器
上下文渲染器 其实就是函数返回字典,字典的 keys 可以用在模板中。
request 函数就是在返回一个字典,每一个模板中都可以使用这个字典中提供的 request 变量。
比如{{ request.user.username }}
下面手动写一个上下文渲染器:
在settings.py同级目录下新建一个 context_processor.py
from django.conf import settings as original_settings
def settings(request):
return {'settings': original_settings}
def ip_address(request):
return {'ip_address': request.META['REMOTE_ADDR']}
把新建的两个 上下文渲染器 加入到 settings.py 中的TEMPLATES下
'项目名.context_processor.settings',
'项目名.context_processor.ip_address',
在模板中直接使用, 而不用在views.py下传给模板html.
DEBUG: {{ settings.DEBUG }}
ip: {{ ip_address }}
以上内容可以先不看
模型(数据库)
命名字段中不能有 __ (双下划线),
因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等), 也不能有Python的关键字
新建一个对象的方法:
1. Person.objects.create(name=name,age=age)
2. p = Person(name="WZ", age=23)
p.save()
3. p = Person(name="TWZ")
p.age = 23
p.save()
前面三种返回的都是对应的object
4. Person.objects.get_or_create(name="WZT", age=23)
这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False, 新建时返回的是True, 已经存在时返回False.
获取对象的方法:
Person.objects.all()
Person.objects.all()[:10] # 切片操作,获取10个人,不支持负索引,切片可以节约内存
Person.objects.get(name=name)# get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人
Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件
Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人
Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写
Person.objects.filter(name__regex="^abc") # 正则表达式查询
Person.objects.filter(name__iregex="^abc") # 正则表达式不区分大小写
filter是找出满足条件的,当然也有排除符合某条件的
Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的
注: 以下内容可以先不看
QuerySet API
1 当有一对多,多对一,或者多对多的关系的时候,先把相关的对象查询出来
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
2 注意事项:
(1). 如果只是检查 Entry 中是否有对象,应该用 Entry.objects.all().exists()
(2). QuerySet 支持切片 Entry.objects.all()[:10]
取出10条,可以节省内存
(3). 用 len(es)
可以得到Entry的数量,但是推荐用 Entry.objects.count()
来查询数量,后者用的是SQL:SELECT COUNT(*)
(4). list(es) 可以强行将 QuerySet 变成 列表
3 QuerySet 是可以用pickle序列化到硬盘再读取出来的
>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.
4 不支持负索引
5 去重: .distinct()
当跨越多张表进行检索后,结果并到一起,可能会出来重复的值
QuerySet进阶
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Author(models.Model):
pass
其中的python_2_unicode_compatible是为了向下兼容, 有兴趣的同学可以去看源码
1, 查看Django queryset执行的SQL语句:
>>> print(str(Author.objects.all().query))
SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author"
>>> print str(Author.objects.filter(name="WeizhongTu").query)
SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" WHERE "blog_author"."name" = WeizhongTu
当不知道Django做了什么时, 可以把执行的SQL语句打印出来,
也可以借助 django-debug-toolbar 等工具在页面上看到访问当前页面执行了哪些SQL,耗时等。
还有一种办法就是修改一下 log 的设置...后续
2, values_list 获取元组形式结果
获取作者的 name 和 qq:
In [8]: authors = Author.objects.values_list('name', 'qq')
In [9]: authors
Out[9]: [('WeizhouTu', '545071666'), ('twz916', '099569249'), ('dachui', '686331473'), ('zhe', '643326207'), ('zhen', '063342958')]
如果只需要 1 个字段,可以指定 flat=True
In [10]: Author.objects.values_list('name', flat=True)
Out[10]: ['WeizhouTu', 'twz916', 'dachui', 'zhe', 'zhen']
In [14]: type(Author.objects.values_list('name', flat=True))
Out[14]: django.db.models.query.ValuesListQuerySet
可以通过list转换为列表
In [19]: type(list(Author.objects.values_list('name', flat=True)))
Out [19]: list
# 默认是False
In [11]: Author.objects.values_list('name')
Out[11]: [('WeizhouTu',), ('twz916',), ('dachui',), ('zhe',), ('zhen',)]
查询 twz915 这个人的文章标题, 以列表表示出来
In [11]: Article.objects.filter(author__name='twz915').values_list('title', flat=True)
3, values获取字典形式的结果
In [17]: Author.objects.values('name', 'qq')
Out[17]: [{'name': 'WeizhouTu', 'qq': '545071666'}, {'name': 'twz916', 'qq': '099569249'}, {'name': 'dachui', 'qq': '686331473'}, {'name': 'zhe', 'qq': '643326207'}, {'name': 'zhen', 'qq': '063342958'}]
注意:
- values_list 和 values 返回的并不是真正的 列表 或 字典,也是 queryset,他们也是 lazy evaluation 的(惰性评估,通俗地说,就是用的时候才真正的去数据库查)
- 如果查询后没有使用,在数据库更新后再使用,你发现得到的是新内容!!!如果想要旧内容保持着,数据库更新后不要变,可以 list 一下
- 如果只是遍历这些结果,没有必要 list 它们转成列表(浪费内存,数据量大的时候要更谨慎!!!)
4, extra 实现 别名, 条件, 排序等
extra 中可实现别名,条件,排序等,后面两个用 filter, exclude 一般都能实现,排序用 order_by 也能实现。
我们主要看一下别名这个
比如 Author 中有 name, Tag 中有 name 我们想执行
SELECT name AS tag_name FROM blog_tag;
可以这样实现:
In [34]: tags = Tag.objects.all().extra(select={'tag_name':'name'})
In [35]: tags[0].name
Out[35]: 'Django教程'
In [36]: tags[0].tag_name
Out[36]: 'Django教程'
发现name 和 tag_name 都可以使用,确认一下执行的 SQL
查看以下SQL语句:
In [37]: Tag.objects.all().extra(select={'tag_name':'name'}).query.__
...: str__()
Out[37]: 'SELECT (name) AS "tag_name", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag"'
发现查询的时候弄了两次 (name) AS "tag_name" 和 "blog_tag"."name"
如果我们只想其中一个能用,可以用 defer 排除掉原来的 name
最常见的需求就是数据转变成 list,然后可视化等,我们在下面一个里面讲。
---但是不知道为什么我这里还是都能用
5, annotate 聚合 技术, 求和, 平均数等
- 计数
In [48]: from django.db.models import Count
In [50]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count')
Out[50]: [{'author_id': 3, 'count': 40}, {'author_id': 4, 'count': 20}]
查看SQL语句
In [51]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count').query.__str__()
Out[51]: 'SELECT "blog_article"."author_id", COUNT("blog_article"."author_id") AS "count" FROM "blog_article" GROUP BY "blog_article"."author_id"'
简化一下SQL: SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
获取作者名称 及 作者文章数
In [68]: Article.objects.all().values('author__name').annotate(count=
...: Count('author'))
Out[68]: [{'author_s_name': 'dachui', 'count': 40}, {'author__name': 'zhe', 'count': 20}]
这是跨表查询
- 求和 与 平均值
In [70]: from django.db.models import Avg
In [71]: Article.objects.values('author_id').annotate(avg_score=Avg('score'))
Out[71]: [{'author_id': 3, 'avg_score': 84.95}, {'author_id': 4, 'avg_score': 81.45}]
- 求一个作者所有文章的总分
In [82]: from django.db.models import Sum
In [83]: Article.objects.values('author__name').annotate(sum_scoress=
...: Sum('score'))
Out[83]: [{'author__name': 'dachui', 'sum_scoress': 3398}, {'author__name': 'zhe', 'sum_scoress': 1629}]
6, select_related 优化一对一, 多对一查询
开始之前我们修改一个 settings.py 让Django打印出在数据库中执行的语句
settings.py 尾部加上
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG' if DEBUG else 'INFO',
},
},
}
这样当 DEBUG 为 True 的时候,我们可以看出 django 执行了什么 SQL 语句
注: 数据库查询 一个下划线 _ 查询不是跨表查询, 是自身的字段, 比如:
author = models.ForeignKey(Author)
author 关联外键到Author的主键 id, 这时候查询内容 author 等同于 author_id
而author__ 则为跨表查询, 查询的内容是Author中的字段, 比如查询
author__name, author__addr, 这name, addr都是Author类模型中的字段.
In [2]: from blog.models import *
In [3]: Author.objects.all()
Out[3]: (0.001) QUERY = 'SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" LIMIT 21' - PARAMS = (); args=()
[<Author: WeizhouTu>, <Author: twz916>, <Author: dachui>, <Author: zhe>, <Author: zhen>]
重点来了:
In [13]: articles = Article.objects.all()[:10]
此处没有与数据库交互
In [14]: a1 = articles[0]
(0.000) QUERY = 'SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 1' - PARAMS = (); args=()
将其第一个内容赋给其他变量时 查询了!
In [15]: a1.title
Out[15]: 'Django教程_1'
In [16]: a1.author_id
Out[16]: 4
In [17]: a1.author.name
(0.001) QUERY = 'SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" WHERE "blog_author"."id" = %s' - PARAMS = (4,); args=(4,)
Out[17]: 'zhe'
此时因为有跨表查询, 所以又进行与数据库交互
这样的话我们遍历查询结果的时候就会查询很多次数据库,能不能只查询一次,把作者的信息也查出来呢?
这就要使用select_related ,
In [18]: articles = Article.objects.all().select_related('author')[:10]
In [19]: a1 = articles[0]
(0.001) QUERY = 'SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score", "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_article" INNER JOIN "blog_author" ON ( "blog_article"."author_id" = "blog_author"."id" ) LIMIT 1' - PARAMS = (); args=()
In [20]: a1.title
Out[20]: 'Django教程_1'
In [21]: a1.author.name
Out[21]: 'zhe'
In [22]: a1.author.addr
Out[22]: 'addr_1'
In [23]: a1.author.qq
Out[23]: '643326207'
此时, 再进行作者信息的查询便不会再与数据库进行交互了
而且, 我们可以发现, 当对数据库的查询结果赋给其他变量时, 不会立即查询, 用到哪个, 才会进行查询哪个
7, prefetch_related 优化一对多, do对多查询
和 select_related 功能类似,但是实现不同。
select_related 是使用 SQL JOIN 一次性取出相关的内容。
prefetch_related 用于 一对多,多对多 的情况,这时 select_related 用不了,因为当前一条有好几条与之相关的内容。
prefetch_related是通过再执行一条额外的SQL语句,然后用 Python 把两次SQL查询的内容关联(joining)到一起
我们来看个例子,查询文章的同时,查询文章对应的标签。“文章”与“标签”是多对多的关系。
下面来看两个例子: 一个是不适用prefetch_related进行赋值遍历查询, 另一个使用, 看有什么区别
In [33]: articles = Article.objects.all()[:3]
In [34]: for a in articles:
...: print(a.title, a.tags.all())
...:
(0.000) QUERY = 'SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 3' - PARAMS = (); args=()
(0.000) QUERY = 'SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ( "blog_tag"."id" = "blog_article_tags"."tag_id" ) WHERE "blog_article_tags"."article_id" = %s LIMIT 21' - PARAMS = (1,); args=(1,)
Django教程_1 [<Tag: Django教程>]
(0.004) QUERY = 'SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ( "blog_tag"."id" = "blog_article_tags"."tag_id" ) WHERE "blog_article_tags"."article_id" = %s LIMIT 21' - PARAMS = (2,); args=(2,)
Django教程_2 [<Tag: Django教程>]
(0.000) QUERY = 'SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ( "blog_tag"."id" = "blog_article_tags"."tag_id" ) WHERE "blog_article_tags"."article_id" = %s LIMIT 21' - PARAMS = (3,); args=(3,)
Django教程_3 [<Tag: Django教程>]
可以看出, 执行的SQL语句是查询了三次, 也就是与数据库交互了三次
In [35]: articles = Article.objects.all().prefetch_related('tags')[:3
...: ]
In [36]: for a in articles:
...: print(a.title, a.tags.all())
...:
(0.002) QUERY = 'SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 3' - PARAMS = (); args=()
(0.003) QUERY = 'SELECT ("blog_article_tags"."article_id") AS "_prefetch_related_val_article_id", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ( "blog_tag"."id" = "blog_article_tags"."tag_id" ) WHERE "blog_article_tags"."article_id" IN (%s, %s, %s)' - PARAMS = (1, 2, 3); args=(1, 2, 3)
Django教程_1 [<Tag: Django教程>]
Django教程_2 [<Tag: Django教程>]
Django教程_3 [<Tag: Django教程>]
明显看出, 一次性查处了所有相关内容, 遍历的是查询到的结果
8, defer 排除不需要的字段
在复杂的情况下,表中可能有些字段内容非常多,取出来转化成 Python 对象会占用大量的资源。
这时候可以用 defer 来排除这些字段,比如我们在文章列表页,只需要文章的标题和作者,没有必要把文章的内容也获取出来(因为会转换成python对象,浪费内存)
In [47]: Article.objects.all().defer('content', 'score', 'author_id')
Out[47]: (0.000) QUERY = 'SELECT "blog_article"."id", "blog_article"."title" FROM "blog_article" LIMIT 21' - PARAMS = (); args=()
[<Article_Deferred_author_id_content_score: Django教程_1>, <Article_Deferred_author_id_content_score: Django教程_2>, <Article_Deferred_author_id_content_score: Django教程_3>, <Article_Deferred_author_id_content_score: Django教程_4>, <Article_Deferred_author_id_content_score: Django教程_5>, <Article_Deferred_author_id_content_score: Django教程_6>, <Article_Deferred_author_id_content_score: Django教程_7>, <Article_Deferred_author_id_content_score: Django教程_8>, <Article_Deferred_author_id_content_score: Django教程_9>, <Article_Deferred_author_id_content_score: Django教程_10>, <Article_Deferred_author_id_content_score: Django教程_11>, <Article_Deferred_author_id_content_score: Django教程_12>, <Article_Deferred_author_id_content_score: Django教程_13>, <Article_Deferred_author_id_content_score: Django教程_14>, <Article_Deferred_author_id_content_score: Django教程_15>, <Article_Deferred_author_id_content_score: Django教程_16>, <Article_Deferred_author_id_content_score: Django教程_17>, <Article_Deferred_author_id_content_score: Django教程_18>, <Article_Deferred_author_id_content_score: Django教程_19>, <Article_Deferred_author_id_content_score: Django教程_20>, '...(remaining elements truncated)...']
尝试把id defer掉, 但是好像没什么用
9, only 仅选择需要的字段
和 defer 相反,only 用于取出需要的字段,假如我们只需要查出 作者的名称, 如果再去把别的都defer掉, 那就麻烦了.
In [49]: Author.objects.all().only('name')
Out[49]: (0.000) QUERY = 'SELECT "blog_author"."id", "blog_author"."name" FROM "blog_author" LIMIT 21' - PARAMS = (); args=()
[<Author_Deferred_addr_email_qq: WeizhouTu>, <Author_Deferred_addr_email_qq: twz916>, <Author_Deferred_addr_email_qq: dachui>, <Author_Deferred_addr_email_qq: zhe>, <Author_Deferred_addr_email_qq: zhen>]
10, 自定义聚合功能 ( 比较高深, 暂未看懂 )
前面可以发现 django.db.models 中有 Count, Avg, Sum 等,也有一些没有的,比如 GROUP_CONCAT,它用来聚合时将符合某分组条件(group by)的不同的值,连到一起,作为整体返回。
新建一个文件 比如 my_aggregate.py
from django.db.models import Aggregate, CharField
class GroupConcat(Aggregate):
function = 'GROUP_CONCAT'
template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'
def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
super(GroupConcat, self).__init__(
expression,
distinct='DISTINCT ' if distinct else '',
ordering=' ORDER BY %s' % ordering if ordering is not None else '',
separator=' SEPARATOR "%s"' % separator,
output_field=CharField(),
**extra )
使用时先引入 GroupConcat 这个类,比如聚合后的错误日志记录有这些字段 time, level, info
我们想把 level, info 一样的 聚到到一起,按时间和发生次数倒序排列,并含有每次日志发生的时间。
ErrorLogModel.objects.values('level', 'info').annotate(
count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ')
).order_by('-time', '-count')
以下内容可以先不看
自定义Field
Django 的官方提供了很多的 Field,但是有时候还是不能满足我们的需求,不过Django提供了自定义 Field 的方法:
1, 减少文本的长度,保存数据的时候压缩,读取的时候解压缩,如果发现压缩后更长,就用原文本直接存储:
2. 比如我们想保存一个 列表到数据库中,在读取用的时候要是 Python的列表的形式,我们来自己写一个 ListField:
Django后台
1 django-admin startprojcet name .
创建好项目
2 python manage.py startapp name
创建好应用
3 修改应用中的models
4 添加到settings.py
5 迁移数据库
6 修改admin.py
from django.contrib import admin
from .models import Article
admin.site.register(Article)
7 打开服务器
看到显示的列表都是 xxxx object
修改应用中的models
在末尾添加如下内容
def __unicode__(self):# 在Python3中用 __str__ 代替 __unicode__
return self.title
就会显示self.title
的内容了.
8 兼容问题: 需要向下兼容Python2
# coding:utf-8
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Article(models.Model):
pass
这样Django就会自适应处理python2
9 在列表显示与字段相关的其他内容
定义admin.py
class ArticleAdmin(admin.ModelAdmin):
list_display = ('title','pub_date','update_time',)
admin.site.register(Article,ArticleAdmin)
list_display 就是来配置要显示的字段的,当然也可以显示非字段内容,或者字段相关的内容,修改models.py
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def my_property(self):
return self.first_name + ' ' + self.last_name
my_property.short_description = "Full name of the person"
full_name = property(my_property)
在admin.py中
class PersonAdmin(admin.ModelAdmin):
list_display = ('full_name',)
注意list_display 后面需要是元祖, 记得加 , 逗号
表单
在views.py同级目录下创建 forms.py文件
from django import forms
class AddForm(forms.Form):
a = forms.IntegerField()
b = forms.IntegerField()
views.py中
# coding:utf-8
from django.shortcuts import render
from django.http import HttpResponse
# 引入我们创建的表单类
from .forms import AddForm
def index(request):
if request.method == 'POST':# 当提交表单时
form = AddForm(request.POST) # form 包含提交的数据
if form.is_valid():# 如果提交的数据合法
a = form.cleaned_data['a']
b = form.cleaned_data['b']
return HttpResponse(str(int(a) + int(b)))
else:# 当正常访问时
form = AddForm()
return render(request, 'index.html', {'form': form})
对应模板html
<form method='post'>
{% csrf_token %}
{{ form }}
<input type="submit" value="提交">
</form>
这有以下好处:
- 模板中表单的渲染
- 数据的验证工作,某一些输入不合法也不会丢失已经输入的数据。
- 还可以定制更复杂的验证工作,如果提供了10个输入框,必须必须要输入其中两个以上,在 forms.py 中都很容易实现
Django配置
1 settings.py 里有一句
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
os.path.dirname(__file__)
指的是当前文件的路径, 也就是settings.py的路径, 外面在加一层, 也就是BASE_DIR就是 项目 所在的路径了,
2 DEBUG
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
TEMPLATE_DEBUG = True
DEBUT = True 便于我们看到bug所在, 部署的时候记得False掉
3 ALLOWED_HOSTS
ALLOWED_HOSTS = ['*.besttome.com','www.ziqiangxuetang.com']
ALLOWED_HOSTS 允许你设置哪些域名可以访问,即使在 Apache 或 Nginx 等中绑定了,这里不允许的话,也是不能访问的。
当 DEBUG=False 时,这个为必填项,如果不想输入,可以用 ALLOW_HOSTS = ['*'] 来允许所有的。
4 STATIC
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
static 是静态文件所有目录,比如 jquery.js, bootstrap.min.css 等文件。
一般来说我们只要把静态文件放在 APP 中的 static 目录下,部署时用 python manage.py collectstatic 就可以把静态文件收集到(复制到) STATIC_ROOT 目录,但是有时我们有一些共用的静态文件,这时候可以设置 STATICFILES_DIRS 另外弄一个文件夹,如下:
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "common_static"),
'/var/www/static/',
)
这样我们就可以把静态文件放在 common_static 和 /var/www/static/中了,Django也能找到它们。
5 MEDIA
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
media文件夹用来存放用户上传的文件, 详情见后
有时候有一些模板不是属于app的,比如 baidutongji.html, share.html等,
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR,'templates').replace('\\', '/'),
os.path.join(BASE_DIR,'templates2').replace('\\', '/'),
],
'APP_DIRS': True,
]
这样 就可以把模板文件放在 templates 和 templates2 文件夹中了。
静态文件
当运行 python manage.py collectstatic 的时候, STATIC_ROOT 文件夹 是用来将所有STATICFILES_DIRS中所有文件夹中的文件,以及各app中static中的文件都复制过来, 把这些文件放到一起是为了用apache等部署的时候更方便
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
其它存放静态文件的文件夹,可以用来存放项目中公用的静态文件,里面不能包含 STATIC_ROOT所指向的目录
如果不想用 STATICFILES_DIRS 可以不用,都放在 app 里的 static 中也可以
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "common_static"),
'/path/to/others/static/', # 用不到的时候可以不写这一行
)
静态文件放在对应的 app 下的 static 文件夹中 或者 STATICFILES_DIRS 中的文件夹中。
当 DEBUG = True 时,Django 就能自动找到放在里面的静态文件。