Django 笔记

    博客分类: 笔记 阅读次数:

Django 笔记

自强学堂Django教程笔记

基本命令

django-admin.py startproject project_name           #新建项目
python manage.py startapp app_name                  #新建APP
django-admin.py startapp app_name                   #新建APP
python manage.py makemigrations                     #创建数据库迁移文件
python manage.py migrate                            #数据库迁移
python manage.py flush                              #清空数据库,留下空表
python manage.py runserver [port]                   #运行服务器
#开发服务器每次更改代码后会自动重启,但由于性能问题不建议用在生产环境
python manage.py createsuperuser                    #创建超级用户
python manage.py changepassword username            #修改密码
python manage.py dumpdata appname > appname.json
python manage.py loaddata appname.json              #导入导出数据
python manage.py shell                              #django终端环境
python manage.py dbshell                            #数据命令行
python manage.py                                    #查看更多命令

视图与网址

创建项目与应用

django-admin startproject mysite
cd mysite
django-admin startapp learn
tree .

.
├── learn
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── mysite
    ├── __init__.py             
    ├── settings.py             #项目设置文件
    ├── urls.py                 #url配置文件
    └── wsgi.py                 #与部署到服务器有关

3 directories, 12 files

将app添加到settings.py的INSTALLED_APPS中。这样django能够自动找到app中的模板文件(app_name/templates/下的文件)和静态文件(app_name/static/下的文件)。

定义视图函数

视图基本

编辑learn/views.py:

# coding:utf-8
from django.http import HttpResponse
 
def index(request):
    return HttpResponse(u"欢迎光临 自强学堂!")

我们定义了index()函数,第一个参数必须是 request,与网页发来的请求有关,request 变量里面包含get或post的内容。函数返回了一个 HttpResponse 对象,经过一些处理,最终显示几个字到网页上。

编辑mysite/urls.py:

from django.conf.urls import url
from django.contrib import admin
from learn import views as learn_views  # new
 
urlpatterns = [
    url(r'^$', learn_views.index),  # new
    url(r'^admin/', admin.site.urls),
]

运行服务器即可访问localhost:

python manage.py runserver

视图进阶

编辑musite/urls.py:

from django.conf.urls import url
from django.contrib import admin
from learn import views as learn_views
 
urlpatterns = [
    url(r'^add/$', learn_views.add, name='add'),  # 注意修改了这一行
    url(r'^admin/', admin.site.urls),
]

在learn/views.py中定义add:

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.

就可以访问localhost/add/?a=123&b=456这样的url了。如果要采用/add/3/4这样的网址,可以讲urls.py中的URL改为:

url(r'^add/(\d+)/(\d+)/$',learn_views.add, name='add')

其中,(\d+)表示一个或多个数字,括号表示保存为一个子组,每个子组将作为一个参数,被views.py中对应的视图函数接收。然后修改视图中add()函数即可访问localhost/add/3/4

def add(request, a, b):    c = int(a) + int(b)    return HttpResponse(str(c))

URL name

我们经常要在网页中引用某个url,比如<a href="/add/4/5/">计算 4+5</a>可以使用前文的加法运算功能。但是,如果因为某些原因,我们需要修改urls.py中的url,那么所有引用该url的代码都要进行修改,非常麻烦。

在urls.py中指定URL name后,可以通过URL name引用网址,引用处会被渲染成urls.py中对应的url。当修改urls.py中的正则表达式部分,只要name不变,其他地方都不需要修改。

在网页模板中,可以如下使用:

不带参数的:
 {% url 'name' %}
带参数的:参数可以是变量名
 {% url 'name' 参数 %} 

例如:
<a href=" {% url 'add' 4 5 %}">link</a>
渲染结果为:
<a href="/add/4/5/">link</a>

在models.py等处可以使用reverse()函数通过url name得到url。如:

from django.urls import reverse
>>> reverse('add2', args=(4,5))
u'/add/4/5/'

另外,如果用户收藏夹中收藏的url是旧的,要跳转到新url的话,可以利用HttpResponseRedirect实现跳转。

Django 模板

访问html文件

在app的views.py中定义home()函数,并设定对应url:

from django.shortcuts import render  
def home(request):    
    return render(request, 'home.html')

在app目录下新建templates目录,在里面新建一个home.html。

<!DOCTYPE html>
<html>
    <head><title>欢迎光临</title></head>
    <body>欢迎光临自强学堂</body>
</html>

进行数据库迁移后就运行服务器,就可以访问home.html了。

模板的使用

通常,我们做网站有一些通用部分,比如导航、底部、访问统计代码等。可以写一个base.html来包含这些通用文件:

<!DOCTYPE html>
<html>
<head><title> {% block title %}默认标题 {% endblock %} - 自强学堂</title>
</head>
<body> 
 {% include 'nav.html' %} 
 {% block content %\
}<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% block content %\
}<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% raw %} {% endblock %}  {% include 'bottom.html' %} 
 {% include 'tongji.html' %}
</body>
</html>

继承的模板可以重写block部分,include可以包含其他文件。home.html可以由base.html扩展:

 {% extends 'base.html' %} 
 {% block title %}欢迎光临首页 {% endblock %} 
 {% block content %}
 {% include 'ad.html' %}
这里是首页,欢迎光临
 {% endblock %}

Django模板查找机制:Django会查找每个app的templates文件夹,而不仅仅是当前app中的templates文件夹。这样一个app可以使用另一个app的模板,但是当不同app出现同名模板时,有可能会找错模板。可以在templates文件夹下建立一个app同名文件夹,将模板放在app/templates/app/文件夹下。

模板语法

在模板中,可以使用{{}}引用变量,用 {% %}插入语句。

引入静态文件

首先在项目同名文件夹下的setting.py中进行如下设置:

 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"
    )

然后在HTML文件开头加上{% load static %}就可以如下引用:

<link href=" {% static "css/index.css" %}" rel="stylesheet">
<script src=" {% static "js/scrollReveal.js" %}"></script>

Django 模型

基本操作流程

建立数据表

Django使用模型操作数据库。可以修改app/models.py修改数据表:

#people/models.py
from django.db import models
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

注意,name和age等字段名不能含有__(双下划线)和Python关键字。

接下来同步数据库:

python manage.py makemigrations
python manage.py migrate

操作数据库

python manage.py shell
>>> from people.models import Person
>>> Person.objects.create(name="Mike", age=24)<Person: Person object>
>>> Person.objects.get(name="Mike")<Person: Person object>

这里查询结果都是<Person: Person object>,并没有显示结果的具体信息,可以修改models.py指定显示信息:

from django.db import models 
class Person(models.Model):    
    name = models.CharField(max_length=30)    
    age = models.IntegerField()         
    
    def __unicode__(self):    # 在Python3中使用 def __str__(self):        
        return self.name

此时查询结果变为:

>>> Person.objects.getname="Mike")
<Person: Mike>

页面引用

模型定义如下:

from django.urls import reverse
class Log(models.Model):    
    title = models.CharField(max_length=70)    
    text = RichTextUploadingField()    
    
    # reverse()会将url.py中定义的logs路由转换成对应文章的url    
    # 在html中使用{{ log.get_absolute_url }}引用    
    def get_absolute_url(self):        
        return reverse('log_detail', kwargs={'pk': self.pk})

url.py中定义了如下路由:

url(r'^logs/$', blog_views.logs, name='logs'),
url(r'^log/(?P<pk>[0-9]+)/$', blog_views.log_detail, name='log_detail'),

blog_views中对应views如下:

from django.shortcuts import render, get_object_or_404
def logs(request):    
    log_list = Log.objects.all().    
    return render(request, 'logs.html', context={'log_list': log_list})
    
def log_detail(request, pk):    
    log = get_object_or_404(Log, pk=pk)    
    return render(request, 'log_detail.html', context={'log':log})

那么host/logs便可以访问文章列表,host/log/2便可以访问第二篇文章。在文章列表也使用可以用get_absolute_url得到相应文章的url。

QuerySet API

从数据库中查询得到的结果一般是一个集合,称为QuerySet。本节例子大部分基于此blog/models.py:

from django.db import models 
class Blog(models.Model):    
    name = models.CharField(max_length=100)    
    tagline = models.TextField()     
    def __unicode__(self):  # __str__ on Python 3        
    return self.name 
    
class Author(models.Model):   
    name = models.CharField(max_length=50)    
    email = models.EmailField()     
    
    def __unicode__(self):  # __str__ on Python 3       
        return self.name 
        
class Entry(models.Model):    
    blog = models.ForeignKey(Blog)    
    headline = models.CharField(max_length=255)    
    body_text = models.TextField()    
    pub_date = models.DateField()    
    mod_date = models.DateField()    
    authors = models.ManyToManyField(Author)    
    n_comments = models.IntegerField()    
    n_pingbacks = models.IntegerField()    
    rating = models.IntegerField()     
    
    def __unicode__(self):  # __str__ on Python 3        
    return self.headline
  1. 创建对象

    >>> from blog.models import Blog
    >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
    >>> b.save()
    # 总之,一共有四种方法
    # 方法 1
    Author.objects.create(name="WeizhongTu", email="tuweizhong@163.com")
    # 方法 2
    twz = Author(name="WeizhongTu", email="tuweizhong@163.com")twz.save()
    # 方法 3
    twz = Author()twz.name="WeizhongTu"twz.email="tuweizhong@163.com"twz.save()
    # 方法 4,首先尝试获取,不存在就创建,可以防止重复
    Author.objects.get_or_create(name="WeizhongTu", email="tuweizhong@163.com")
    # 返回值(object, True/False),创建时返回true,已经存在时返回False
    
  2. 获取对象

    Person.objects.all() # 查询所有
    Person.objects.all()[:10] #切片操作,获取10个人,不支持负索引,切片可以节约内存,不支持负索引,后面有相应解决办法,第7条
    Person.objects.get(name="WeizhongTu") # 名称为 WeizhongTu 的一条,多条会报错 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岁的
    
  3. djang删除数据

    Person.objects.filter(name__contains="abc").delete() # 删除 名称中包含 "abc"的人. 
    # 以下两句效果与上面等同,Django实际只执行一条 SQL 语句
    people = Person.objects.filter(name__contains="abc")
    people.delete()
    
  4. 更新内容

    # 批量更新
    Person.objects.filter(name__contains="abc").update(name='xxx')   # 名称中包含 "abc"的人 都改成 xxx
    Person.objects.all().delete()    # 删除所有 Person 记录
    # 单独更新
    twz = Author.objects.get(name="WeizhongTu")
    twz.name="WeizhongTu"
    twz.email="tuweizhong@163.com"
    twz.save()                       # 最后不要忘了保存!!!
    
  5. QuerySet是可迭代的

    es = Entry.objects.all()
        for e in es:    
            print(e.headline)
    

    注意事项:

    • 如果只是检查Entry中是否有对象,应该用Entry.objects.all().exists()
    • QuerySet支持切片Entry.objects.all()[:10],可以节省内存
    • len(es)可以得到Entry的数量,但推荐使用Entry.objects.count(),后者用的是SQL:SELECT COUNT(*)
    • list(es)可以将QuerySet变为列表
  6. 查询结果排序

    Author.objects.all().order_by('name')
    Author.objects.all().order_by('-name') # 在 column name 前加一个负号,可以实现倒序
    
  7. 链式查询

    Author.objects.filter(name__contains="WeizhongTu").filter(email="tuweizhong@163.com")
    Author.objects.filter(name__contains="Wei").exclude(email="tuweizhong@163.com") 
    # 找出名称含有abc, 但是排除年龄是23岁的
    Person.objects.filter(name__contains="abc").exclude(age=23)
    
  8. QuerySet不支持负索引

    Person.objects.all()[:10]        # 切片操作,前10条
    Person.objects.all()[-10:]       # 会报错!!!
    # 1. 使用 reverse() 解决
    Person.objects.all().reverse()[:2]   # 最后两条
    Person.objects.all().reverse()[0]    # 最后一条
    # 2. 使用 order_by,在栏目名(column name)前加一个负号
    Author.objects.order_by('-id')[:20]  # id最大的20条
    
  9. QuerySet去重

    一般情况下,QuerySet不会出现重复。但当跨越多张表进行检索后,结果拼接后可能重复,可以使用distinct()去重:

    qs1 = Pathway.objects.filter(label__name='x')
    qs2 = Pathway.objects.filter(reaction__name='A + B >> C')
    qs3 = Pathway.objects.filter(inputer__name='WeizhongTu')
    # 合并到一起
    qs = qs1 | qs2 | qs3
    # 这个时候就有可能出现重复的,去重:
    qs = qs.distinct()
    

对于从文章列表页到文章详情页的跳转,可以在定义文章model时添加以下函数:

from django.urls import reverse
class Log(models.Model):    
    title = models.CharField(max_length=70)    
    text = RichTextUploadingField()    
    def __str__(self):       
         return self.title    
         
    
    # reverse()会将url.py中定义的logs路由转换成对应文章的url    
    # 在html中使用{{ log.get_absolute_url }}引用    
    def get_absolute_url(self):        
        return reverse('logs', kwargs={'pk': self.pk})   

def log(request, pk):    
        log = get_object_or_404(Log, pk=pk)    
        return render(request, 'log.html', context={'log':log})

Django 后台

model定义完毕后,同步数据表并创建super user:

python manage.py makemigrations
python manage.py migratep
ython manage.py createsuperuser

在应用的admin.py中加入:

from .models import Article 

admin.site.register(Article)

登录host/admin就可以操作Article表了。但是只显示一个字段(__str__(self)方法定义的字段),如下可以显示更多字段:

from django.contrib import admin
from .models import Article 
class ArticleAdmin(admin.ModelAdmin):    
    list_display = ('title','pub_date','update_time',) 
    
admin.site.register(Article,ArticleAdmin)

ckeditor

博客文章等的排版可以使用ckeditor。在Django中使用ckeditor步骤如下:

  1. 安装

    pip3 install django-ckeditor
    pip3 install Pillow
    
  2. 在setting.py中添加到INSTALLED_APPS中:

    INSTALLED_APPS = (
        ...    
        'ckeditor',    
        'ckeditor_uploader'
        )
    
  3. 在settings.py里设置ckeditor的文件上传路径(在此之前需要配置MEDIA_URL和MEDIA_ROOT)。

    CKEDITOR_UPLOAD_PATH = "ckupload"
    
  4. 在settings.py里进行ckeditor的相关配置。

    CKEDITOR_CONFIGS = {
        'awesome_ckeditor':{
            'toolbar': 'Basic',
         }, 
         'default_ckeditor':{
             'toolbar': 'Full',
         },
     }
    
  5. 在urls.py里配置ckeditor相关的url。

    urlpatterns = [
        ... 
        url(r'^ckeditor/', include('ckeditor_uploader.urls')),
     ]
    
  6. 在models.py中使用:

    from ckeditor.fields import RichTextField
    instructions = RichTextField() # RichTextField拥有TextField的全部参数
    learn = RichTextField(config_name='default_ckeditor')
    

CSRF 认证

使用Ajax交换数据会遇到csrf问题,此时,可以先:

from django.views.decorators.csrf import csrf_exempt

然后在需要使用Ajax交换数据的函数前加上@csrf_exempt:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
@csrf_exempt
def encode(request):                #  获得前端传过来的json    
    res = json.loads(request.body)   
    # do something    
    return HttpResponse(json.dumps(res,ensure_ascii=False),content_type="application/json,charset=utf-8")

CORS

res = {}
res["content"] = "test"
response = HttpResponse(json.dumps(res,ensure_ascii=False),content_type="application/json,charset=utf-8")    
response["Access-Control-Allow-Origin"] = "*"     
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"    
response["Access-Control-Max-Age"] = "1000"    
response["Access-Control-Allow-Headers"] = "*"    
return response 

获取请求参数

参见 https://www.cnblogs.com/chichung/p/9873425.html