Skip to content

Django

bookmark

步骤

  1. 安装python
  2. 查看pip版本:python -m pip —version
  3. pip3安装依赖: pip install django
  4. pip3 freeze 查看已安装;
  5. pip3 freeze | grep xxx 查找xxx依赖版本 (linux)
  6. 使用venv创建虚拟环境: py -m venv .venv(.venv环境名称)
  7. 启用:
    1. macOS: source .venv/bin/activate
    2. window: .venv\Scripts\activate
  8. 安装依赖:py -m pip install Django
  9. 停用: deactivate

一台机器Django多版本管理

python常用虚拟环境: Anaconda、Virtualenvs、pyvenv

miniconda电脑安装位置:/opt/miniconda3

文档:

bookmark

启动虚拟环境

conda activate

初始化项目x

创建项目

django-admin startproject mysite

启动项目

python3 manage.py runserver 8000

创建app

python3 manage.py startapp xxx(应用名称)

编写视图

polls/views.py

python
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

定义URLConf, 创建**polls/urls.py** 的文件

python
from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
]

设置根目录的URLconf,包含polls/urls.py中定义的URLconf;

mysite/urls.py

python
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]

path() 函数至少需要两个参数:route 和 viewinclude() 函数允许引用其他 URLconfs。每当 Django 遇到 include() 时,它会截断 URL 中匹配到该点的部分,并将剩余的字符串发送到包含的 URLconf 以进行进一步处理。

创建数据库表

bash
python manage.py migrate

创建模型 polls/models.py文件

python
from django.db import models

# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text
    

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

mysite/settings.pyINSTALL_APPS中添加设置

python
INSTALLED_APPS = [
    "polls.apps.PollsConfig",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

现在你的 Django 项目会包含 polls 应用。接着运行下面的命令:

python
python manage.py makemigrations polls

通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移

如果你想的话,你可以阅读一下你模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。

再次运行 migrate 命令,在数据库里创建新定义的模型的数据表

python
python manage.py migrate

image.png

现在让我们进入交互式 Python 命令行,尝试一下 Django 为你创建的各种 API。通过以下命令打开 Python 命令行:

python
python manage.py shell

Django管理页面

创建管理员账号

python
python manage.py createsuperuser # admin/1215323.../admin123

添加Question对象

python
from django.contrib import admin

from .models import Question

admin.site.register(Question)

polls/views.py新增视图

python
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)


def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)


def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

新视图添加进 polls.urls 模块里

python
from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path("", views.index, name="index"),
    # ex: /polls/5/
    path("<int:question_id>/", views.detail, name="detail"),
    # ex: /polls/5/results/
    path("<int:question_id>/results/", views.results, name="results"),
    # ex: /polls/5/vote/
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

项目的 TEMPLATES 配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 DjangoTemplates 后端,并将 APP_DIRS 设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 "templates" 子目录。这就是为什么尽管我们没有像在第二部分中那样修改 DIRS 设置,Django 也能正确找到 polls 的模板位置的原因。

在你的 polls 目录里创建一个 templates 目录。

templates 目录里,再创建一个目录 polls,然后在其中新建一个文件 index.html

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  {% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
  {% else %}
      <p>No polls are available.</p>
  {% endif %}
</body>
</html>

polls/views.py 里的 index 视图来使用模板

python
from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    template = loader.get_template("polls/index.html")
    context = {"latest_question_list": latest_question_list}
    return HttpResponse(template.render(context, request))

使用快捷函数render

python
from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)

抛出404

python
from django.http import Http404
from django.shortcuts import render

from .models import Question


# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, "polls/detail.html", {"question": question})

template/polls/detail.html

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  {{ question }}
</body>
</html>

404快捷函数 get_object_or_404()   get_list_or_404()

python
from django.http import render, get_object_or_404

def detail(request, question_id):
	question = get_object_or_404(Question, pk=question_id)
	
	return render(request, 'polls/detail.html', {"question": question})

模版语法

polls/index.html

python
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
# path("<int:question_id>/", views.detail, name="detail"),  detail对应name

为 URL 名称添加命名空间

教程项目只有一个应用,polls 。在一个真实的 Django 项目中,可能会有五个,十个,二十个,甚至更多应用。Django 如何分辨重名的 URL 呢?举个例子,polls 应用有 detail 视图,可能另一个博客应用也有同名的视图。Django 如何知道 {% url %} 标签到底对应哪一个应用的 URL 呢?

答案是:在根 URLconf 中添加命名空间。在 polls/urls.py 文件中稍作修改,加上 app_name 设置命名空间:

polls/urls.py

python
from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.index, name="index"),
    path("<int:question_id>/", views.detail, name="detail"),
    path("<int:question_id>/results/", views.results, name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

现在,编辑 polls/index.html 文件,从:

python
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

修改为指向具有命名空间的详细视图:

polls/templates/polls/index.html

python
<
li
><
a
 href
=
"
{%
 
url
 'polls:detail' question.id 
%}
">
{{
 question.question_text 
}}
</
a
></
li
>

Django 模型(Models)与 ORM 操作指南

模型定义示例

以下是多个 Django 模型的定义示例,包含普通字段、关联字段(ForeignKeyManyToManyField)等:

python
from django.db import models
from datetime import date


class Person(models.Model):
    SEX_CHOICES = {"m": "male", "f": "female"}
    userName = models.CharField(max_length=30, blank=False)
    password = models.CharField(max_length=30, blank=False)
    email = models.EmailField(null=True, blank=True)
    sex = models.CharField(blank=False, choices=SEX_CHOICES)


class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)


class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)  # 外键关联Musician
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()


class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)  # 外键关联Blog
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField(default=date.today)
    authors = models.ManyToManyField(Author)  # 多对多关联Author
    number_of_comments = models.IntegerField(default=0)
    number_of_pingBacks = models.IntegerField(default=0)
    rating = models.IntegerField(default=5)

    def __str__(self):
        return self.headline

基本操作

创建与保存数据

python
# 创建并保存Blog实例
b = Blog(name='my-blog', tagline='oooo')
b.save()  # 保存到数据库

# 快速创建(无需手动调用save())
p = Person.objects.create(
    userName='lining',
    password='111',
    email='1215@qq.com',
    sex='m'
)

修改数据

python
# 查询并修改数据
b5 = Blog.objects.get(name='b5')  # 获取名称为'b5'的记录
b5.name = 'new Name'  # 修改字段值
b5.save()  # 保存修改

关联字段操作(ForeignKey 与 ManyToManyField)

ForeignKey(一对多)

python
# 更新Entry的外键关联(指向另一个Blog)
entry = Entry.objects.get(pk=1)  # 获取id=1的Entry
cheese_blog = Blog.objects.get(name='waw')  # 获取目标Blog
entry.blog = cheese_blog  # 关联到新的Blog
entry.save()  # 保存修改

ManyToManyField(多对多)

python
# 给Entry添加一个作者
John = Author.objects.create(name='John', email='eee')
entry.authors.add(John)  # 关联John到entry

# 一次性添加多个作者
lim = Author.objects.create(name='lim')
Danny = Author.objects.create(name='Danny')
entry.authors.add(lim, Danny)  # 批量关联

数据检索(查询)

基础查询

python
# 获取所有记录
all_entries = Entry.objects.all()

# 条件查询(筛选2006年发布的Entry)
Entry.objects.filter(pub_date__year=2006)

# 链式操作(多条件组合)
Entry.objects.filter(headline__startswith='What')  # 标题以'What'开头
             .exclude(pub_date__gte=date.today())  # 排除发布日期在今天及之后的
             .filter(pub_date__gte=date(2005,1,31))  # 发布日期在2005-01-31及之后的

切片查询

切片格式:[start:stop:step]

  • start(起始索引):省略,默认从0开始
  • stop(结束索引):10,表示到索引10为止(不包括索引10)
  • step(步长):2,表示每隔一个元素取一个

使用 Python 切片语法限制查询结果数量:

python
# 获取前5条记录
Entry.objects.all()[:5]

# 获取第6-10条记录(offset=5, limit=5)
Entry.objects.all()[5:10]

# 每隔1条取1条(步长=2),取前10条中的第1、3、5、7、9条
Entry.objects.all()[:10:2]

跨关系查询

通过双下划线(__)实现跨模型查询:

python
# 查询所有属于名称为'waw'的Blog的Entry
Entry.objects.filter(blog__name='waw')

# 查询所有包含作者名为'Lennon'的Entry的Blog
Blog.objects.filter(entry__authors__name="Lennon")

# 查询包含作者名为空的Entry的Blog
Blog.objects.filter(entry__authors__name__isnull=True)

# 精确查询:作者存在但名称为空的Entry所属的Blog
Blog.objects.filter(
    entry__authors__isnull=False,  # 排除作者为空的Entry
    entry__authors__name__isnull=True  # 作者名称为空
)

常用查询操作符

Django ORM 通过双下划线(__)语法支持多种查询条件,以下是常用操作符:

数值比较操作符

操作符说明示例
__gt大于(greater than)pub_date__gt=date(2020, 1, 1)(发布日期 > 2020-01-01)
__gte大于等于(greater than or equal)pub_date__gte=date(2020, 1, 1)(发布日期 ≥ 2020-01-01)
__lt小于(less than)pub_date__lt=date(2020, 1, 1)(发布日期 < 2020-01-01)
__lte小于等于(less than or equal)pub_date__lte=date(2020, 1, 1)(发布日期 ≤ 2020-01-01)

其他常用操作符

操作符说明示例
__exact精确匹配(默认)name__exact='John'(名称严格等于'John')
__iexact不区分大小写的精确匹配name__iexact='john'(名称等于'john',忽略大小写)
__contains包含指定字符串name__contains='oh'(名称包含'oh')
__icontains不区分大小写的包含name__icontains='oh'(名称包含'oh',忽略大小写)
__startswith以指定字符串开头name__startswith='J'(名称以'J'开头)
__istartswith不区分大小写的开头匹配name__istartswith='j'(名称以'j'开头,忽略大小写)
__endswith以指定字符串结尾name__endswith='n'(名称以'n'结尾)
__iendswith不区分大小写的结尾匹配name__iendswith='N'(名称以'N'结尾,忽略大小写)
__in在指定列表中id__in=[1, 2, 3](id为1、2、3中的一个)
__range在指定范围内pub_date__range=(start_date, end_date)(发布日期在start_date和end_date之间)
__isnull是否为空email__isnull=True(邮箱为空)

python错误类型和继承关系

plain
BaseException
 ├── BaseExceptionGroup
 ├── GeneratorExit
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    ├── FloatingPointError
      │    ├── OverflowError
      │    └── ZeroDivisionError
      ├── AssertionError
      ├── AttributeError
      ├── BufferError
      ├── EOFError
      ├── ExceptionGroup [BaseExceptionGroup]
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── MemoryError
      ├── NameError
      │    └── UnboundLocalError
      ├── OSError
      │    ├── BlockingIOError
      │    ├── ChildProcessError
      │    ├── ConnectionError
      │    │    ├── BrokenPipeError
      │    │    ├── ConnectionAbortedError
      │    │    ├── ConnectionRefusedError
      │    │    └── ConnectionResetError
      │    ├── FileExistsError
      │    ├── FileNotFoundError
      │    ├── InterruptedError
      │    ├── IsADirectoryError
      │    ├── NotADirectoryError
      │    ├── PermissionError
      │    ├── ProcessLookupError
      │    └── TimeoutError
      ├── ReferenceError
      ├── RuntimeError
      │    ├── NotImplementedError
      │    ├── PythonFinalizationError
      │    └── RecursionError
      ├── StopAsyncIteration
      ├── StopIteration
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── SystemError
      ├── TypeError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      └── Warning
           ├── BytesWarning
           ├── DeprecationWarning
           ├── EncodingWarning
           ├── FutureWarning
           ├── ImportWarning
           ├── PendingDeprecationWarning
           ├── ResourceWarning
           ├── RuntimeWarning
           ├── SyntaxWarning
           ├── UnicodeWarning
           └── UserWarning

Released under the MIT License.