Django是一个用 Python 编写的 Web 框架。Web 框架是一种软件,基于web框架可以开发动态网站,各种应用程序以及服务。它提供了一系列工具和功能,可以解决许多与Web开发相关的常见问题,比如:安全功能,数据库访问,会话,模板处理,URL路由,国际化,本地化,等等
第一个django项目
新建一个django project
创建了一个名为myblog的项目
django-admin.py startproject mysite
运行后,我们会看到如下的目录样式:
mysite
├── manage.py
└── mysite
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
新建app
一般一个项目有多个app,当然通用的app也可以在多个项目中使用。
创建一个名为learn的app,注意:先要进入项目目录中(第一个mysite),cd mysite
,然后执行下面语句,创建app。
python manage.py startapp learn
我们可以看到mysite中多了一个learn文件夹:
mysite
└── learn
| ├── migrations
| | └── __init__.py
| ├── __init__.py
| ├── admin.py
| ├── app.py
| ├── models.py
| ├── tests.py
| └── views.py
├── manage.py
└── mysite
把新定义的app加到settings.py中的INSTALL——APPS中,修改mysite/mysite/settings.py
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'learn',
)
目的:新建的app如果不加到INSTALL——APPS中的话,django就不能自动找到app中的模板文件(learn/templates/下的文件)和静态文件(learn/static/中的文件)。
定义视图函数(访问页面的内容)
打开learn/views.py,编写代码:
#coding:utf-8
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World!') #用来向浏览器返回内容
定义URL
打开mysite/mysite/urls.py,进行修改:
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', learn_views.index),
]
在命令行上运行python manage.py runserver
,这样就打开了服务器,然后我们就可以通过浏览器访问了。
打开浏览器,输入http://127.0.0.1:8000/,就可以看到Hello World!
注意:如果是要在另一台电脑上访问,要用python manage.py ip:port的形式,比如监听所有ip:
python manage.py runserver 0.0.0.0:8000
#监听机器所有ip,8000端口,访问时用电脑的ip代替127.0.0.1
视图与网址进阶
GET方法获取参数
采用/add/?a=4&b=5这样的方式进行
1、修改learn/views.py文件
from django.shortcuts import render
from django.http import HttpResponse
def add(request):
a=request.GET['a']
b=request.GET['b']
c=int(a)+int(b)
return HttpResponse(str(c))
注:request.GET 类似于一个字典,更好的办法是用 request.GET.get(‘a’, 0) 当没有传递 a 的时候默认 a 为 0
2、修改mysite/urls.py文件
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^add/$', learn_views.add,name='add'),
]
打开网址:http://127.0.0.1:8000/add/?a=4&b=5,就可以看到网页上显示了一个9。
采用/add/3/4/这样的方式进行
1、修改learn/views.py文件,新定义一个add2函数
def add2(request,a,b):
c=int(a)+int(b)
return HttpResponse(str(c))
2、修改mysite/urls.py文件,加上:
url(r'^add/(d+)/(d+)/$', learn_views.add2, name='add2'),
正则表达式中:
d 代表一个数字,
- 代表一个或多个前面的字符,
写在一起 d+ 就是一个或多个数字,用括号括起来的意思是保存为一个子组,每一个子组将作为一个参数,被 views.py 中的对应视图函数接收。
我们再访问:http://127.0.0.1:8000/add/4/5/,就可以看到和刚才一样的效果,但是现在网址更优雅了。
URL name详解
url(…,name=’add’)中name的作用:name可以用于在templates、models、views中得到对应的网址,相当于给网址取了一个名字,只要这个名字不变,地址变了也能通过名字获取到。
修改learn/views.py文件:
from django.shortcuts import render
def index(request):
return render(request,'home.html')
在learn中新建一个templates文件夹,在该文件夹中新建一个home.html文件,向其中写入:
<!DOCTYPE html>
<html>
<head>
<title>首页</title>
</head>
<body>
<a href="/add/4/5/">计算 4+5</a>
</body>
</html>
修改mysite/urls.py文件,加入:
url(r'^$', learn_views.index, name='home'),
运行服务器,访问:http://127.0.0.1:8000/就可以看到一个页面,页面上有一个链接计算 4+5
问题:这样会写死网址,会在改了网址(正则)后,很多地方都要修改。
解决:
reverse接收url中的name作为第一个参数,代码中可以通过reverse()获取对应的网址,只要url的name不改,就不用改代码中的网址
from django.urls import reverse
>>>reverse('add2',args=(4,5))
u'/add/4/5'
在网页模板中:
#不带参数的:
{% url 'name' %}
#带参数的:参数可以是变量名
{% url 'name' 参数 %}
#例如:
<a href="{% url 'add2' 4 5 %}">link</a>
#结果
<a href="/add/4/5/">link</a>
当urls.py进行更改时,前提是name不改变,获取的网址也会动态改变:
url(r'^new_add/(d+)/(d+)/$', learn_views.add2, name='add2'),
这时 {% url ‘add2’ 4 5 %} 就会渲染对应的网址成 /new_add/4/5/。用在 views.py 或 models.py 等地方的 reverse函数,同样会根据 name 对应的url获取到新的网址。
用旧的url访问现在新的网址,在views.py中写一个跳转方法:
from django.http import HttpResponseRedirect
from django.urls import reverse
def old_add2_redirect(request, a, b):
return HttpResponseRedirect(
reverse('add2', args=(a, b))
)
urls.py中
url(r'^add/(d+)/(d+)/$', learn_views.old_add2_redirect),
url(r'^new_add/(d+)/(d+)/$', learn_views.add2, name='add2'),
这样,假如有/add/4/5/,访问时就会自动跳转到新的/new_add/4/5/ 了
模板
之前大部分是用HttpResponse来把内容显示到网页上,现在这部分是使用渲染模板的方法来显示内容,我们在前面也接触过。
在app目录下新建一个templates文件夹,里面新建一个home.html文件,默认配置下,django的模板系统会自动找到app下面的templates文件夹中的模板文件。
加入通用文件
网站模板的设计,一般的,我们做网站的一些通用的部分,比如:导航,底部。
{% extends 'base.html' %} #继承或者扩展原来的base.html
{% include 'nav.html' %} #这样就加入了通用文件
{% block content %}
<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% endblock %}
不同app中模板文件名称冲突
模板一般放在app下的templates中,Django会自动去这个文件夹中找。但是假如我们每个app的templates中都有一个 index.html,当我们在views.py中使用的时候,直接写一个 render(request, ‘index.html’),Django 能不能找到当前 app 的 templates 文件夹中的 index.html 文件夹呢?
模板查找机制:Django 查找模板的过程是在每个app的templates文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个app的templates形成一个文件夹列表,Django遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found(过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个app可以用另一个app的模板文件,弊是有可能会找错了。所以我们使用的时候在templates中建立一个app同名的文件夹,这样就好了。
这就需要把每个app中的 templates 文件夹中再建一个以 app 的名称为名字的文件夹,仅和该app相关的模板放在 app/templates/app/ 目录下面。访问时加上app,如:app/index.html。
django模板标签
for循环和list内容的显示
views.py,在视图中传递了一个list到模板home.html
def home(request):
classList=['html','css','jquery']
return render(request,'home.html',{'classList':classList})
home.html,在模板中这样使用它:
教程列表:
{% for i in classList %}
{{ i }}
{% endfor %}
for循环要有一个结束标记,上面的代码假如对应首页的网址,则会显示:教程列表:html css jquery
知识点:一般的变量之类的用{{}}(变量),功能类的,比如循环,条件判断是用{% %}(标签)
显示字典中内容
views.py
def home(request):
dict={'name':u'文学','age':'21'}
return render(request,'home.html',{'dict':dict})
home.html
姓名:{{dict.name}}年龄:{{dict.age}}
还可以这样遍历字典:
{% for key,value in dict.items %}
{{ key }}:{{value}}
{% endfor %}
for循环中的条件判断
home.html
{% for i in classList %}
{{ i }}{% if not forloop.last %},{% endif %}
{% endfor %}
结果:html,css,jquery
在for循环中还有很多有用的东西,如下:
| forloop.counter | 索引从1开始算 |
| forloop.counter0 | 索引从0开始算 |
| forloop.revcounter | 索引从最大长度到1 |
| forloop.revcounter0 | 索引从最大长度到0 |
| forloop.first | 当遍历的元素为第一项时为真 |
| forloop.last | 当遍历的元素为最后一项时为真 |
| forloop.parentloop | 用在嵌套的for循环中,获取上一层for循环的forloop |
当列表中可能为空值时用for empty
{% for a in alist %}
{{a.name}}
{% empty %}
列表为空
{% endfor %}
模板上得到视图对应的网址
{% url 'add' 4 5 %}
结果:/add/4/5,就算以后修改网址,只要不修改name,就不用修改模板。
还可以使用as语句取别名:
{% url 'add' 4 5 as the_url %}
<a href="{{the_url}}">链接到:{{the_url}}</a>
模板中的逻辑操作
==、!=、>=、<=、>、<这些都可以在模板中使用
{% if var >= 90 %}
成绩优秀,教室你没少去吧!学得不错
{% elif var >= 80 %}
成绩良好
{% elif var >= 70 %}
成绩一般
{% elif var >= 60 %}
需要努力
{% else %}
不及格啊,大哥!多去教室学习啊!
{% endif %}
and、or、not、in、not in也可以在模板中使用
{% if num <= 100 and num >= 0 %}
num在0到100之间
{% else %}
数值不在范围之内!
{% endif %}
{% if 'wenxue' in nameList %}
文学在名单中
{% endif %}
在模板中获取当前网址,当前用户等
如果不是在views.py中用的render函数,是render_to_response的话,需要将request加入到上下文渲染器。
修改settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
'django.template.context_processors.request',
...
],
},
},
]
然后在模板中我们就可以用request了,一般情况下,推荐使用render而不是render_to_response。
获取当前用户
{{request.user}}
如果登录就显示内容,不登录就不显示内容:
{% if request.user.is_authenticated %}
{{ request.user.username }},您好!
{% else %}
请登录,这里放登录链接
{% endif %
获取当前网址
{{request.path}}
获取当前get参数
{{request.GET.urlencode}}
使用
<a href="{{ request.path }}?{{ request.GET.urlencode }}&delete=1">当前网址加参数 delete</a>
模型(数据库)
django模型是与数据库相关的,与数据库相关的代码一般写在models.py中,django支持sqlite3、mysql、postgresql等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用。
使用数据库
-
新建一个项目:
django-admin.py startproject learn_models
-
进入项目中:
cd learn_models
-
新建一个应用:
python manage.py startapp people
-
将项目加入settings.py的INSTALLED_APPS中,也就是告诉django有这么一个应用:
INSTALLED_APPS=( ... 'people', )
-
打开people/models.py文件,修改代码:新建了一个People类,继承自model.Model,一个人有姓名和年龄,这里用到了两种Field。
from django.db import models class Person(models.Model): name=models.CharField(max_length=30) age=models.IntegerField()
-
同步一下数据库(默认使用sqlite3,无需配置)
python manage.py makemigrations python manage.py migrate
-
使用表
python manage.py shell #使用该代码,而不是直接使用python,那样会报错 >>>from people.models import Person >>>Person.objects.create(name='wenxue',age=21) <Person: Person object> >>>Person.objects.get(name='wenxue') <Person: Person object>
上面的查询结果,并没有获取该对象相关信息,所以并不好,所以我们要修改people/models.py,加入函数:
def __unicode__(self):
return self.name
结果:<Person: wenxue>
获取对象
Person.objects.all()
Person.objects.all()[:10] #切片操作,获取10个人,不支持负索引,切片可以节约内存
Person.objects.get(name=name) #get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
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")# 正则表达式不区分大小写
Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的
自定义field
减少文本的长度,保存数据的时候压缩,读取数据的时候解压缩,如果发现压缩后更长,就用晕文本直接存储
#coding:utf-8
from django.db import models
class CompressedTextField(models.TextField):
"""
model Fields for storing text in a compressed format (bz2 by default)
"""
def from_db_value(self, value, expression, connection, context):
if not value:
return value
try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value
def to_python(self, value):
if not value:
return value
try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value
def get_prep_value(self, value):
if not value:
return value
try:
value.decode('base64')
return value
except Exception:
try:
return value.encode('utf-8').encode('bz2').encode('base64')
except Exception:
return value
to_python函数用于转化数据库中的字符到Python的变量,get_prep_value 用于将Python变量处理后(此处为压缩)保存到数据库,使用和Django自带的 Field 一样。from_db_value 函数用于转化数据库中的字符到 Python的变量。
比如保存一个列表到数据库中,在读取的时候要是python列表的形式,我们来自己写一个ListField
from django.db import models
import ast
class ListField(models.TextField):
__metaclass__ = models.SubfieldBase
description = "Stores a python list"
def __init__(self, *args, **kwargs):
super(ListField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value:
value = []
if isinstance(value, list):
return value
return ast.literal_eval(value)
def get_prep_value(self, value):
if value is None:
return value
return unicode(value) # use str(value) in Python 3
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)
使用时很简单,首先是导入ListField,像自带的Field一样使用:
class Article(models.Model):
labels = ListField()
运行:
>>> from app.models import Article
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]
数据表更改
更改数据表后,需要执行代码:
python manage.py makemigrations
python manage.py migrate
同步,这两行命令会对models.py进行检测,自动发现需要更改的,应用到数据表中。
在原理的类上增加字段或者删除字段:
python manage.py sql appname
QuerySet API
QuerySet是数据库接口相关的接口,从数据库中查询出来的结果一般是一个集合,这个集合叫做QuerySet。
from django.db import models
class Author(models.Model):
name=models.CharField(max_length=50)
email=models.EmailField()
def __unicode__(self):
return self.name
QuerySet创建对象
#方法1
Author.objects.create(name='wenxue',email='wenxue@163.com')
#方法2
wx=Author(name='wenxue',email='wenxue@163.com')
wx.save()
#方法3
wx=Author()
wx.name='wenxue'
wx.email('wenxue@163.com')
wx.save()
#方法4
Author.objects.get_or_create(name='wenxue',email='wenxue@163.com')
#返回值(object,True/False)
获取对象的方法
见上部分
QuerySet是可迭代的
as=Author.objects.all()
for a in as:
print a.name
小知识
1、检查Author中是否有对象:Author.objects.all().exists()
2、得到Author的数量:len(as)
,但是推荐使用Author.objects.count()
来查询数量,它是用sql:select count(*)
3、将QuerySet变成列表:list(as)
4、QuerySet用pickle序列化到硬盘再读取出来
import pickle
query=pickle.load(s) #s is the pickle string
qs=MyModel.objects.all()
qs.query=query #restore the original 'query'
5、QuerySet查询结果排序
Author.objects.all().order_by('name')#按照名称排序
Author.objects.all().order_by('-name')#实现倒序
6、QuerySet支持链式查询
Author.objects.filter(name__contains='wen').filter(email='wenxue@163.com')
Author.objects.filter(name__contains='wen').exclude(email='wenxue@163.com')
7、QuerySet不支持负索引。解决:
Author.objects.all().reverse()[:2]#最后两条
Author.objects.order_by('-id')[:2]#id最大的2条
8、QuerySet重复的问题
as=as.distinct()
小知识进阶
1、查看Django QuerySet执行的sql:print str(Author.objects.all().query)
2、values_list获取元组形式结果:
as=Author.objects.values_list('name','email')
list(as)#元组的列表
#如果只需要1个字段,可以指定flat=true
as=Author.objects.values_list('name',flat=True)
3、values获取字典形式的结果
as=Author.objects.values('name','email')
list(as)#字典的列表
4、extra实现别名,条件,排序等
extra中可实现别名、条件、排序等,后面两个用filter,exclude一般都能实现,排序用order_by也能实现。主要看别名:
要执行select name as tag_name from blog_tag语句
tags=Tag.ojects.all().extra(select={'tag_name':'name'})
#如果只想其中一个能用,可以用defer排除原来的name
...extra(...).def('name')
5、annotate聚合计数、求和、平均数等
a、查询每个作者的文章数
Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id','count')
执行的sql语句
SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
b、求一个作者的所有文章的得分平均值
Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score')
执行的sql语句SELECT "blog_article"."author_id", AVG("blog_article"."score") AS "avg_score" FROM "blog_article" GROUP BY "blog_article"."author_id"
求总分则是Sum('score')
6、select_related优化一对一,多对多查询
修改settings.py让Django打印出在数据库中执行的语句
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执行了什么语句
查询文章的时候连同作者一起查询出来,“文章”和“作者”的关系就是多对一
articles = Article.objects.all().select_related(‘author’)[:10]
>>>a1 = articles[0] # 取第一篇
>>>al.title
>>>al.author.name
7、prefetch_related 优化一对多,多对多查询
和 select_related 功能类似,但是实现不同。select_related 是使用 SQL JOIN 一次性取出相关的内容。prefetch_related 用于 一对多,多对多 的情况,这时 select_related 用不了,因为当前一条有好几条与之相关的内容。
prefetch_related是通过再执行一条额外的SQL语句,然后用 Python 把两次SQL查询的内容关联(joining)到一起。我们来看个例子,查询文章的同时,查询文章对应的标签。“文章”与“标签”是多对多的关系。
>>>articles = Article.objects.all().prefetch_related('tags')[:10]
>>>for a in articles:
print a.title, a.tags.all()#一次性查出了所有相关的内容。
8、defer排除不需要的字段
在复杂的情况下,表中可能有些字段内容非常多,取出来转化成 Python 对象会占用大量的资源。这时候可以用 defer 来排除这些字段,比如我们在文章列表页,只需要文章的标题和作者,没有必要把文章的内容也获取出来(因为会转换成python对象,浪费内存)
Article.objects.all().defer('content')# 注意这里没有查 content 字段了
9、only仅选择需要的字段
和 defer 相反,only 用于取出需要的字段,假如我们只需要查出 作者的名称
Author.objects.all().only('name')
原生的 SQL 查询(注意:原生查询必须包含主键)
authors = Author.objects.raw('select id,name from blog_author limit 1')
10、自定义聚合功能
前面看到了 django.db.models 中有 Count, Avg, Sum 等,但是有一些没有的,比如 GROUP_CONCAT,它用来聚合时将符合某分组条件(group by)的不同的值,连到一起,作为整体返回。我们来演示一下,如何实现 GROUP_CONCAT 功能。
新建一个文件 比如 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')
Django后台
新建一个账户
python manage.py createsuperuser
修改admin.py
进入blog文件夹,修改admin.py文件:
from django.contrib import admin
from .models import Person
admin.site.register(Person)
只需要这样,我们就有了一个强大的后台
打开开发服务器
python manager.py runserver
访问http://localhost:8000/admin/,输入设定的账号和密码,就可以看到我们的后台管理界面,上面有默认的Auth,和我们建立的应用blog
,blog中包含我们在这个应用中的数据表格,我们可以进行管理,这就是django为我们提供的强大后台。
[图片上传失败...(image-b5f4c3-1529227685937)]
注意:要在models中添加函数:
def __unicode__(self):
return self.name
否则表中的每条数据名称都看起来一样,不能区分(或str函数)。
在列表中显示与字段相关的内容
from django.contrib import admin
from .models import Person
class PersonAdmin(admin.ModelAdmin):
list_display=('name','age')
admin.site.register(Person,PersonAdmin)
这样就会在列表中显示name和age
修改django admin site
定制加载的列表
根据不同的人显示不同的内容列表,比如输入员只能看见自己输入的,审核员能看到所有的草稿,这时候就需要重写get_queryset方法。
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyModelAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(author=request.user)
该类实现的功能是如果是超级管理员就列出所有的,如果不是,就仅列出访问者自己相关的。
定制搜索功能
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'age')
search_fields = ('name',)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term)
try:
search_term_as_int = int(search_term)
queryset |= self.model.objects.filter(age=search_term_as_int)
except:
pass
return queryset, use_distinct
queryset 是默认的结果,search_term 是在后台搜索的关键词。
修改保存时的一些操作
可以检查用户,保存的内容等,比如保存时加上添加人。
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
其中obj是修改后的对象,form是返回的表单(修改后的),当新建一个对象时 change = False, 当修改一个对象时 change = True。如果需要获取修改前的对象的内容可以用:
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj_original = self.model.objects.get(pk=obj.pk)
obj.user = request.user
obj.save()
那么又有问题了,这里如果原来的obj不存在,也就是如果我们是新建的一个怎么办呢,这时候可以用try,except的方法尝试获取,当然更好的方法是判断一下这个对象是新建还是修改,是新建就没有 obj_original,是修改就有:
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if change:# 更改的时候
obj_original = self.model.objects.get(pk=obj.pk)
else:# 新增的时候
obj_original = None
obj.user = request.user
obj.save()
删除时做一些处理
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def delete_model(self, request, obj):
"""
Given a model instance delete it from the database.
"""
# handle something here
obj.delete()
django表单
django的表单forms
在app中新建一个forms.py文件
from django import forms
class AddForm(forms.Form):
a=forms.IntegerField()
b=forms.IntegerField()
视图函数views.py中
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)
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])
对应的模板文件index.html
<form method='post'>
{% csrf_token %}
{{form}}
<input type='submit' value='提交'/>
</form>
在urls.py中增加这个函数:
url(r'^$', views.index, name='home'),
叙说整个过程:当第一次访问http://localhost:8000/时,执行index函数中的else部分,然后根据表单类AddForm,生成表单元素,渲染到index.html页面,我们看到的效果是两个标签label、两个文本框和一个提交按钮。在我们在输入框中输入数据后,依然是传递到views.py的index函数,然后去执行if部分,获取传送的数据,进行计算,最后响应给用户。
django配置
settings.py文件
file 这个变量可以获取到当前文件(包含这个代码的文件)的路径,os.path.dirname(file) 得到文件所在目录,再来一个os.path.dirname()就是目录的上一级,BASE_DIR 即为 项目 所在目录。
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DEBUG=True 时,如果出现 bug 便于我们看见问题所在,但是部署时最好不要让用户看见bug的详情,可能一些不怀好心的人攻击网站,造成不必要的麻烦。
DEBUG = True
TEMPLATE_DEBUG = True
ALLOWED_HOSTS 允许你设置哪些域名可以访问,即使在 Apache 或 Nginx 等中绑定了,这里不允许的话,也是不能访问的。当 DEBUG=False 时,这个为必填项,如果不想输入,可以用 ALLOW_HOSTS = [‘*’] 来允许所有的。
ALLOWED_HOSTS = ['*.besttome.com','www.ziqiangxuetang.com']
static 是静态文件所有目录,比如 jquery.js, bootstrap.min.css 等文件。
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
一般来说我们只要把静态文件放在 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也能找到它们。
media文件夹用来存放用户上传的文件,与权限有关。
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
有时候有一些模板不是属于app的,比如 baidutongji.html, share.html等。这样 就可以把模板文件放在 templates 和 templates2 文件夹中了。
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,
},
]
静态文件
settings.py中静态文件位置
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
# 当运行 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/', # 用不到的时候可以不写这一行
)
# 这个是默认设置,Django 默认会在 STATICFILES_DIRS中的文件夹 和 各app下的static文件夹中找文件
# 注意有先后顺序,找到了就不再继续找了
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder"
)
目录结构
静态文件放在对应的 app 下的 static 文件夹中 或者 STATICFILES_DIRS 中的文件夹中。当 DEBUG = True 时,Django 就能自动找到放在里面的静态文件。(Django 通过 STATICFILES_FINDERS 中的“查找器”,找到符合的就停下来,寻找的过程 类似于 Python 中使用 import xxx 时,找 xxx 这个包的过程)。
myblog
├── blog
│ ├── __init__.py
│ ├── admin.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── static # 应用 blog 下的 static, 默认会找这个文件夹
│ │ └── 【zqxt.png】
│ ├── tests.py
│ │
│ └── views.py
├── common_static # 已经添加到了 STATICFILES_DIRS 的文件夹
│ └── js
│ └── 【jquery.js】
│
├── myblog
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
部署时
收集静态文件。它就会把以前放在app下static中的静态文件全部拷贝到 settings.py 中设置的 STATIC_ROOT 文件夹中
python manage.py collectstatic
本文转载自https://blog.csdn.net/qq_31792281/article/details/70473739