文章目录:
  1. 安装
    1. 常用命令
  2. 简介
    1. 项目结构
  3. 模板
    1. 变量
    2. 列表
    3. 字典
    4. 过滤器
      1. default
      2. length
      3. filesizeformat
      4. date
      5. truncatechars
    5. 标签
      1. if/else 标签
      2. for标签
    6. 配置静态文件
    7. 模板继承
      1. 父模板
      2. 子模板
  4. 模型
    1. 模型配置
    2. 数据库操作
      1. 所有常用的函数
  5. 表单
    1. HTTP请求
    2. 基本用法
    3. ModelForm
      1. 单独字段验证
      2. 表单整体验证
  6. 路由
    1. 路由分发
    2. 反向解析
    3. 路径转换器
  7. Admin管理工具
  8. 视图
    1. 请求对象:HttpRequest
    2. 响应对象:HttpResponse
    3. 其他函数

参考:

官方文档 Django 文档 | Django documentation | Django

菜鸟教程 Django 教程 | 菜鸟教程

哔哩哔哩

安装

直接pip install django

常用命令

创建项目:使用Pycharm Pro 或 django-admin startproject ProjectName

启动服务python manage.py runserver 8000

创建应用python manage.py startapp appName

创建管理员python manage.py createsuperuser

生成迁移文件python manage.py makemigrations

应用迁移python manage.py migrate

简介

Django是一种Web开发框架,遵从MTV(Model-Template-View)原则。

Model负责与数据库交互,在model.py中可以配置数据的结构、类型、相关函数。

View为视图,用户访问URL时会被urls.py映射到对应的View中,View内部调用Model进行数据的处理、数据库操作等,可以将数据传递到模板当中。

Template为模板,一般使用HTML+CSS+JS+Django中特定的规则完成,接受View传递的数据,渲染为HTML,显示在前端。

项目结构

创建项目后会出现以下的目录:
来自菜鸟教程

一般主项目需要修改的只有settings.py的简单配置,和urls.py里面的路由配置。

创建应用后,应用下的结构:

myapp/
│
├── migrations/
│   └── __init__.py
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── urls.py
└── views.py

// From 菜鸟教程

models.py定义数据模型,views.py处理业务逻辑,admin.py配置管理后台,有时候会把路由映射放到应用中处理,再创建一个urls.py

模板

Django的模板有变量渲染、逻辑控制、模板继承、静态文件、自定义过滤器等功能。

需要先向Django说明模板文件的路径,将TEMPLATES中的 DIRS[BASE_DIR / "templates"]views.py中使用render函数作为响应,如:

from django.shortcuts import render
 
def runoob(request):
    context          = {}
    context['hello'] = 'Hello World!'
    return render(request, 'runoob.html', context)

使用字典context作为参数,键值hello对应模板中变量{{ hello }}

变量

view:{"HTML变量名" : "值"}# 使用字典传输
HTML:{{变量名}}

如上文可以使用<p>{{ hello }}</p>可以打印Hello World!

列表

使用.索引下标可以取出对应列表中的元素。

如views.py:

from django.shortcuts import render

def runoob(request):
    views_list = ["菜鸟教程1","菜鸟教程2","菜鸟教程3"]
    return render(request, "runoob.html", {"views_list": views_list})

templates/runoob.html:

<p>{{ views_list }}</p>   # 取出整个列表
<p>{{ views_list.0 }}</p> # 取出列表的第一个元素

字典

可以用 .键 取出对应的值。如:

from django.shortcuts import render

def runoob(request):
    views_dict = {"name":"菜鸟教程"}
    return render(request, "runoob.html", {"views_dict": views_dict})
<p>{{ views_dict }}</p>
<p>{{ views_dict.name }}</p>

过滤器

相当于一种非常便捷的库函数,可以用于处理字符串等数据。模板语法:

{{ 变量名 | 过滤器:可选参数 }}

过滤器可以被套接,一个过滤器管道的输出可以作为下一个管道的输入。如:

{{ my_list|first|upper }}

以下是一些常用的过滤器:

default

default 为变量提供一个默认值。

如果 views 传的变量的布尔值是false,则使用指定的默认值。以下值都是false:

0  0.0  False  0j  ""  []  ()  set()  {}  None

实际应用如:

from django.shortcuts import render

def runoob(request):
    name =0
    return render(request, "runoob.html", {"name": name})
{{ name|default:"菜鸟教程666" }}

length

返回对象的长度,适用于字符串和列表。

字典返回的是键值对的数量,集合返回的是去重后的长度。

from django.shortcuts import render

def runoob(request):
    name ="菜鸟教程"
    return render(request, "runoob.html", {"name": name})
{{ name|length}}

filesizeformat

以更易读的方式显示文件的大小(即'13 KB', '4.1 MB', '102 bytes'等)。

字典返回的是键值对的数量,集合返回的是去重后的长度。

{{ num|filesizeformat}}

date

根据给定格式对一个日期变量进行格式化。

格式Y-m-d H:i:s返回 年-月-日 小时:分钟:秒 的格式时间。

{{ time|date:"Y-m-d" }}

truncatechars

如果字符串包含的字符总个数多于指定的字符数量,那么会被截断掉后面的部分。截断的字符串将以 ... 结尾。

{{ views_str|truncatechars:2}}

标签

标签使用 {% %}的格式。

上面都是对数据进行处理,下面是一些逻辑的语句:

if/else 标签

基本语法格式:

{% if condition1 %}
   ... display 1
{% elif condition2 %}
   ... display 2
{% else %}
   ... display 3
{% endif %}

根据条件判断是否输出。if/else 支持嵌套。

{% if %}标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反(not)。

for标签

与 Python 的 for 语句的情形类似,循环语法是 for X in Y ,Y 是要迭代的序列而 X 是在每一个特定的循环中使用的变量名称。

每一次循环中,模板系统会渲染在{% for %}{% endfor %}之间的所有内容。

如遍历athlete_list:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

在标签中可以通过{{forloop.xxxx}}获取循环序号:

{% empty %}:再循环为空时执行(即 in 后面的参数布尔值为 False )。如下所示:

from django.shortcuts import render

def runoob(request):
  views_list = []
  return render(request, "runoob.html", {"listvar": views_list})
{% for i in listvar %}
    {{ forloop.counter0 }}
{% empty %}
    空空如也~
{% endfor %}

配置静态文件

1、在项目根目录下创建 statics 目录。

来自菜鸟教程

2、在 settings 文件的最下方配置添加以下配置:

STATIC_URL = '/static/' # 别名 
STATICFILES_DIRS = [ 
    os.path.join(BASE_DIR, "statics"), 
]

3、在 statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件。

4、把 bootstrap 框架放入插件目录 plugins。

5、在 HTML 文件的 head 标签中引入 bootstrap。

<link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7/dist/css/bootstrap.css">

此时需要使用别名,而不是真实路径statics,这样做方便修改外部访问路径而不需要修改真实位置,且对于真实路径也有保护。

在模板中使用需要加入 {% load static %} 代码,模板标签{% static '...' %},其中的点为相对于static目录的相对路径,以下实例我们从静态目录中引入图片。

from django.shortcuts import render

def runoob(request):
    name ="菜鸟教程"
    return render(request, "runoob.html", {"name": name})
{% load static %}
{{name}}<img src="{% static 'images/runoob-logo.png' %}" alt="runoob-logo">

模板继承

模板可以用继承的方式来实现复用,减少冗余内容。

网页的头部和尾部内容一般都是一致的,我们就可以通过模板继承来实现复用。

父模板用于放置可重复利用的内容,子模板继承父模板的内容,并放置自己的内容。

父模板

{% block 名称 %} 
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}

子模板

{% extends "父模板路径"%} ]

{ % block 名称 % }
内容 
{% endblock 名称 %}

如下面的示例:

base.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
    <h1>Hello World!</h1>
    <p>菜鸟教程 Django 测试。</p>
    {% block mainbody %}
       <p>original</p>
    {% endblock %}
</body>
</html>

index.html

{%extends "base.html" %}
 
{% block mainbody %}
<p>继承了 base.html 文件</p>
{% endblock %}

模型

直接使用SQLite数据库。

模型配置

模型在Model/models.py中配置。

首先需要引入库 from django.db import models

定义表

class Book(models.Model):
    name = models.CharField(max_length=20)

models.Model是统一继承的类,后面的CharField是变量类型,后面括号的max_length=为可选择的参数。

常用的变量类型和参数

字段类型描述示例
AutoField自增整数主键,通常用于ID字段id = models.AutoField(primary_key=True)
BigAutoField大整数自增主键,适用于需要很大id的表id = models.BigAutoField(primary_key=True)
IntegerField普通整数age = models.IntegerField()
BigIntegerField大整数big_num = models.BigIntegerField()
PositiveIntegerField非负整数score = models.PositiveIntegerField()
PositiveSmallIntegerField非负小整数small_num = models.PositiveSmallIntegerField()
SmallIntegerField小整数small_num = models.SmallIntegerField()
FloatField浮点数price = models.FloatField()
DecimalField定点数(需要指定max_digitsdecimal_placescost = models.DecimalField(max_digits=5, decimal_places=2)
CharField字符串,需要指定max_lengthname = models.CharField(max_length=100)
TextField长文本,不限制长度description = models.TextField()
BooleanField布尔值is_active = models.BooleanField(default=True)
NullBooleanField(废弃)可空布尔值 Django3.1后移除改用 BooleanField(null=True)
DateField日期birthday = models.DateField()
DateTimeField日期时间created_at = models.DateTimeField(auto_now_add=True)
TimeField时间start_time = models.TimeField()
DurationField时间间隔duration = models.DurationField()
EmailField邮箱格式字符串email = models.EmailField()
URLFieldURL格式字符串website = models.URLField()
FileField文件上传字段upload = models.FileField(upload_to='uploads/')
ImageField图片上传字段,需要Pillow库photo = models.ImageField(upload_to='photos/')
ForeignKey外键,关联另一模型author = models.ForeignKey(User, on_delete=models.CASCADE)
OneToOneField一对一字段profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
ManyToManyField多对多字段tags = models.ManyToManyField(Tag)
参数名描述示例
null是否允许在数据库存储NULL(不等同于空字符串)null=True
blank是否允许该字段在Django表单中留空(字段验证时是否必填)blank=True
default字段默认值default=0
primary_key是否为主键字段primary_key=True
unique是否唯一(数据库层面唯一索引)unique=True
choices限制字段的可选值,传入tuple列表status = models.CharField(max_length=2, choices=STATUS_CHOICES)
max_length最大长度,仅对CharFieldTextField(部分字段)适用max_length=255
db_index是否建立数据库索引db_index=True
editable是否在admin或modelform中编辑editable=False
verbose_name字段的人类可读名称verbose_name='用户名'
help_text字段帮助信息help_text='请输入用户名'
auto_now设为True时自动设置当前时间戳(每次更新记录时)auto_now=True
auto_now_add设为True时自动设置创建时间戳(只在记录创建时设置)auto_now_add=True
upload_to文件/图片上传字段,指定文件存储目录upload_to='photos/%Y/%m/%d'
on_delete外键关联对象被删除时的行为on_delete=models.CASCADE

常用的外键on_delete选项:

选项含义
models.CASCADE级联删除,关联对象删除了,当前对象也删除
models.PROTECT禁止删除关联对象,抛出异常
models.SET_NULL关联对象删除时,把外键字段置为 NULL
models.SET_DEFAULT关联对象删除时,把外键字段设为默认值
models.SET(...)关联对象删除时,把外键字段设为指定的值
models.DO_NOTHING什么都不做,数据库会报错(不推荐)

数据库操作

首先都需要从模型文件中引入类,如:from TestModel.models import Test

def adddb(request):
    test1 = Test(name='nantong')
    test1.save()

def testdb(request):
    # 删除id=1的数据
    test1 = Test.objects.get(id=1)
    test1.delete()
    
    # 另外一种方式
    Test.objects.filter(id=1).delete()
    
    # 删除所有数据
    Test.objects.all().delete()

def testdb(request):
    # 修改其中一个id=1的name字段,再save
    test1 = Test.objects.get(id=1)
    test1.name = 'Google'
    test1.save()
    
    # or
    Test.objects.filter(id=1).update(name='Google')
    
    # 修改所有的列
    Test.objects.all().update(name='Google')

def testdb(request):
    # 初始化
    response = ""
    response1 = ""
    
    # 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
    listTest = Test.objects.all()
        
    # filter相当于SQL中的WHERE,可设置条件过滤结果
    response2 = Test.objects.filter(id=1) 
    
    # 获取单个对象
    response3 = Test.objects.get(id=1) 
    
    # 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
    Test.objects.order_by('name')[0:2]
    
    #数据排序
    Test.objects.order_by("id")
    
    # 上面的方法可以连锁使用
    Test.objects.filter(name="runoob").order_by("id")

所有常用的函数

  1. .all()

返回所有记录的 QuerySet(类似列表,但是惰性查询,可链式调用)

resources = Resource.objects.all()

  1. .filter(**kwargs)

过滤数据,返回满足条件的 QuerySet。kwargs 是字段名+条件,支持双下划线查询。

# 查找状态是 normal 的
qs = Resource.objects.filter(status="normal")

# 查找标题包含 "python" 的(不区分大小写)
qs = Resource.objects.filter(title__icontains="python")

# 过滤上传者为当前用户,且状态不是 removed
qs = Resource.objects.filter(uploader=request.user).exclude(status="removed")

# 多条件“且”关系,会默认 AND
qs = Resource.objects.filter(status="normal", file_type="pdf")

  1. .exclude(**kwargs)

排除满足条件的记录,返回其余记录。

qs = Resource.objects.exclude(status="removed")

  1. .get(**kwargs)

获取唯一一条符合条件的记录。如果没有或者多于一条,会抛出异常。

res = Resource.objects.get(pk=1)

一般搭配 try...except 使用。


  1. .order_by(*fields)

排序。字段名前加 - 表示倒序。

res = Resource.objects.all().order_by("-created_at")  # 按创建时间倒序

  1. .values(*fields).values_list(*fields, flat=False)

只查询部分字段,用于优化性能。

names = Resource.objects.values_list("title", flat=True)  # 只要标题列表

data = Resource.objects.values("id", "title")   # 返回字典列表 [{id:1,title:"..."}, ...]

  1. .first().last()

拿 QuerySet 的第一条/最后一条数据,方便快捷。可能返回 None。

res = Resource.objects.filter(status="normal").first()

  1. .count()

统计符合条件的数据数量。

count = Resource.objects.filter(status="normal").count()

  1. .exists()

判断符合条件的记录是否存在,效率高。

has_pdf = Resource.objects.filter(file_type="pdf").exists()

  1. .update(**kwargs)

一次批量更新符合条件的记录,不会触发 save() 方法。

Resource.objects.filter(status="pending").update(status="removed")

  1. .create(**kwargs)

创建并保存一条新记录,快捷写法。

Resource.objects.create(title="新资源", uploader=request.user, status="normal")

  1. .update_or_create(defaults=None, **kwargs)

尝试查找满足条件的记录,有则更新,没有则创建。

obj, created = Resource.objects.update_or_create(
    file_hash="xxx123",
    defaults={"title": "新标题", "status": "normal"},
)

  1. .get_or_create(defaults=None, **kwargs)

查找是否存在满足条件的对象,没有则创建,返回对象和是否创建标志。

obj, created = Resource.objects.get_or_create(title="Unique title",
                                              defaults={"status": "normal"})

  1. .aggregate().annotate()

用于聚合查询,比如计数、求和、最大值等。需要导入 django.db.models 中的聚合函数。

from django.db.models import Count, Sum

# 统计所有资源的总下载次数
total_downloads = Resource.objects.aggregate(total=Sum("downloads"))["total"]

# 给每个上传者统计上传资源数
users = User.objects.annotate(resource_count=Count("resource"))

  1. 复杂查询 - 使用 Q() 对象

实现 “或” 逻辑或复杂逻辑。

from django.db.models import Q

qs = Resource.objects.filter(Q(title__icontains="python") | Q(summary__icontains="python"))

表单

Django的表单功能通过django.forms提供,可以渲染HTML表单,自动生成网页上的表单数据;验证用户的输入是否符合要求;获取清洗后的数据,将用户输入转化为Python数据类型;关联数据类型,把表单和数据库模型相关联。

HTTP请求

GET:请求服务器数据,请求数据附加在URL后面

POST:提交数据在请求体中

基本用法

创建一个表单类:一般在forms.py中,比如下面这个联系人表单类

from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(label='你的名字', max_length=100)
    email = forms.EmailField(label='邮箱')
    message  = forms.CharField(label='留言', widget=forms.Textarea)

视图中显示表单:views.py

from django.shortcuts import render
from .forms import ContactForm

def contact_view(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)  # 带数据实例化表单,准备验证
        if form.is_valid():  # 验证表单数据
            # 获取表单清洗后的数据
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']
            # TODO: 处理数据,如保存或发送邮件
            return render(request, 'thanks.html', {'name': name})
    else:
        form = ContactForm()  # 空表单实例
    
    return render(request, 'contact.html', {'form': form})

模板中渲染表单:contact.html

<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">提交</button>
</form>

ModelForm

如果已经定义好了模型,那么可以使用ModelForm简化模型相关表单。

假设你有一个models.py

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

创建对应的表单:

# forms.py
from django.forms import ModelForm
from .models import Article

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content']  # 指定需要包含的字段

然后在视图中使用:

def create_article(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            form.save()  # 直接保存到数据库
            return redirect('article_list')
    else:
        form = ArticleForm()
    return render(request, 'article_form.html', {'form': form})

表单验证中可以使用其他高级方法。

单独字段验证

比如对于某个单独字段进行额外验证,可以在ModelForm中定义一个cleam_<fieldname>方法,如下:

class BookForm(ModelForm):
    class Meta:
        model = Book
        fields = '__all__'

    def clean_price(self):
        price = self.cleaned_data.get('price')
        if price <= 0:
            raise forms.ValidationError('价格必须大于0')
        return price

clean_price方法会自动被调用,验证price字段。

表单整体验证

如果需要基于多个字段一起验证,可以重写clean方法:

def clean(self):
    cleaned_data = super().clean() # 因为重写了,所以先调用父类原有的clean()方法
    title = cleaned_data.get('title')
    author = cleaned_data.get('author')
    if title == author:
        raise forms.ValidationError('书名和作者不能相同')

路由

路由是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。路由在urls.py中配置。

from django.urls import path  
urlpatterns = [ 
    path('admin/', admin.site.urls), 
    path('index/', views.index),
]

路由分发

路由分发是把路由的分配分发到每个app目录当中,使用的是include,比如:

from django.contrib import admin 
from django.urls import path,include # 需引入include 
urlpatterns = [ 
    path('admin/', admin.site.urls), 
    path("app01/", include("app01.urls")), 
    path("app02/", include("app02.urls")), 
]

反向解析

随着功能的增加,路由层的 url 发生变化,就需要去更改对应的视图层和模板层的 url,非常麻烦,不便维护。

可以利用反向解析,当路由层 url 发生改变,在视图层和模板层动态反向解析出更改后的 url,免去修改的操作。

反向解析一般用在模板中的超链接及视图中的重定向。

这时候在路由中添加字段 name='BieMing',

path('login1/', views.login, name='login')

在views.py中可以引入reverse进行反向解析:

return redirect(reverse('login'))

路径转换器

用来捕获URL路径中的某部分,并将其转换成特定类型变量,可以从 URL 中提取参数并传递给视图。比如:

path('article/<int:id>/', views.article_detail)

这里 <int:id> 是一个路径转换器,表示从 URL 中匹配一个整数,并把它命名为 id,传给视图函数。

而在views.py中,可以用第二个变量接受这个值:

def index(request, id:int):
    ...
转换器类型匹配说明传给视图的变量类型
str匹配非斜杠(/)的任意字符序列字符串(str
int匹配正整数(由数字组成)整数(int
slug匹配字母、数字、连字符和下划线字符串(str
uuid匹配UUID格式的字符串UUID 对象
path匹配任意字符(包含斜杠 /字符串(str

Admin管理工具

需要先注册数据模型到 admin。比如,先在 TestModel 中创建了模型 Test 。

修改 TestModel/admin.py

from django.contrib import admin
from TestModel.models import Test
 
# Register your models here.
admin.site.register(Test)

也可以一次导入多个模型:

admin.site.register([Test, Contact, Tag])

还有其他的管理方法,不过小学期任务展示下前端就行了,管理后台没必要做的很完善()

视图

请求对象:HttpRequest

1. GET

数据类型是 QueryDict,一个类似于字典的对象,包含HTTP GET的所有参数。这些参数就是URL最后的/?name=北航&year=2024

如果有相同的键,就把所有的值放到对应的列表里,取值格式:对象.方法request即请求的对象。

可以使用和模型一样的方法,都在GET下,如get()返回最后一个匹配的字符串。

def index(request):
    name = request.GET.get("name")
    return HttpResponse('姓名: {}'.format(name))

如果存在第二个参数,则表示不存在时的默认值(避免报错)。

2. POST

也是一种HTTP请求方式,请求参数放在请求体(body)中,不显示在URL中。数据类型同样是 QueryDict。

使用方法和GET几乎一模一样。

def index(request):
    name = request.POST.get("name")
    return HttpResponse('姓名: {}'.format(name))

3. path

用来获取URL中的路径部分,数据类型是字符串。

def index(request):
    name = request.path
    print(name)
    ...

响应对象:HttpResponse

响应对象主要有三种形式:HttpResponse()render()redirect()

1. HttpResponse()

返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。

def index(request):
    return HttpResponse("<a href='https://lixu.cc'>点击访问</a>")

2. render()

返回文本,第一个参数是 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。

def index(request):
    name = "LX的博客"
    return render(request, "index.html", {"name": name})

3. redirect()

用于重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。可以传入 URL 名称(反向解析)或路径。

举例:redirect("detail", pk=1) 会跳转到名为 detail 的路由,参数 pk=1。

其他函数

1. get_object_or_404(Model, **kwargs)

从数据库中去获取指定条件的对象(比如 Resource 表中的一条记录)。如果找不到,则直接返回 404 页面,避免程序继续执行和报错。

例:get_object_or_404(Resource, pk=pk):查找主键为 pk 的资源。

2. messages

Django 的内置消息框架,用于控制器(视图)向模板传递状态信息(如“操作成功”、“错误提示”等)。

messages.success(request, "成功消息")messages.error(request, "错误消息") 等,都可以给前端发送提示。


3. @login_required

装饰器,保证访问此视图的用户已登录。未登录则跳转至登录页。

使用它能方便地保护一些只能登录用户使用的操作,如上传、评分、举报等。


4. Q 对象

Django ORM 的高级查询工具,可以用来组合复杂的查询条件,实现“或”、“且”等关系。

示例:Q(title__icontains=q) | Q(summary__icontains=q) 意思是:标题或摘要包含关键词 q。