Python Djiango

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极大的方便了使用。

使用数据库

  1. 新建一个项目:django-admin.py startproject learn_models

  2. 进入项目中:cd learn_models

  3. 新建一个应用:python manage.py startapp people

  4. 将项目加入settings.py的INSTALLED_APPS中,也就是告诉django有这么一个应用:

    INSTALLED_APPS=(
        ...
        'people',
    )
    
    
  5. 打开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()
    
    
  6. 同步一下数据库(默认使用sqlite3,无需配置)

    python manage.py makemigrations
    python manage.py migrate
    
    
  7. 使用表

    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

原文地址:https://www.cnblogs.com/m0488/p/12331327.html