Django

Sherry 发布于 2025-03-27 59 次阅读 文章 最后更新于 2025-03-27 5495 字


[!tip]

创建 Django 项目

django-admin.exe startproject 项目名称

Pycharm 创建

特殊说明:

  • 命令行,创建的项目是标准的。
  • pycharm, 在标准的基础上默认给咱们加了点东西。
    • 创建了一个 templates 目录(删除)
    • settings. py 中(删除)

默认项目文件介绍

mysite
    ├─manage.py     【项目的管理,启动项目、创建app、数据管理】【不要动】【常常用】
    └─mysite
        ├─init.py   
        ├─settings.py   【项目配置】【常常修改】
        ├─urls.py   【URL和函数对应关系】【常常修改】
        ├─asgi.py   【接收网络请求】【不要动】【异步】
        └─wsqi.Dy   【接收网络请求】【不要动】【同步】

app

[!tip]

模块化管理

[!note]

创建 app

python manage.py startapp app01
app01
    ├─init.Py
    ├─admin.py  【固定,不用动】django默认提供了admin后台管理。
    ├─apps.py   【固定,不用动】
    ├─migrations    【固定,不用动】数据库字段变更记录
    ├─initpy
    ├─models.py 【重要】对数据库进行操作
    ├─tests.py  【固定,不用动】单元测试
    └─views.py  【重要】函数

快速上手

  • 确保 app 注册

  • 编写 URL 和视图函数对应关系

  • 编写视图函数【views. py】

  • 启动 Django

    • 命令行启动
    python manage.py runserver [端口]
    
    • pycharm 启动

templates 模板

[!warning]

如果在 settings. py 中设置了 DIRS:[os. path. join (BASE_DIR,'templates')],就在项目根目录中优先查找

静态文件

在开发中一般将:

  • css
  • js
  • 图片

都会当作静态文件处理。

全部放在 static 中

模板语法加载文件

模板语法

[!note]

单个传参

name = '张三'
return render(request, 'tpl.html', {'name': name})
<h2>{{ name }}</h2>

[!note]

列表传参

roles = ['管理员', '经理', '总监', 'CEO']
return render(request, 'tpl.html', {'name': name, 'roles': roles})
<h2>{{ roles.0 }}</h2>

<h2>{{ roles.1 }}</h2>

<h2>{{ roles.2 }}</h2>

<h2>{{ roles.3 }}</h2>

[!note]

循环模板

{% for role in roles %}
        <h2>{{ role }}</h2>
{% endfor %}

[!note]

字典传参

user_info = {'name': '李四', 'age': 20, 'salary': 10000}

return render(request, 'tpl.html', {user_info': user_info})
<h2>{{ user_info.name }}</h2>
<h2>{{ user_info.age }}</h2>
<h2>{{ user_info.salary }}</h2>


<!-- 获取键值 -->
{% for k, v in user_info.items %}
        <h2>{{ k }}: {{ v }}</h2>
{% endfor %}

<!-- 获取键 -->
{% for key in user_info.keys %}
        <h2>{{ key }}</h2>
{% endfor %}

<!-- 获取值 -->
{% for value in user_info.values %}
        <h2>{{ value }}</h2>
{% endfor %}

[!note]

if 模板

 {% if name == '张三' %}
        <h2>name是张三</h2>
    {% elif name=='小七' %}
        <h2>name是小七</h2>
    {% else %}
        <h2>没人</h2>    
 {% endif %}

获取请求参数

[!note]

获取 GET 请求参数

request.GET

[!note]

获取 POST 请求参数

request.POST
username = request.POST.get('username')
password = request.POST.get('password')

[!note]

返回响应

from django.shortcuts import render, HttpResponse, redirect
return HttpResponse("返回内容")
return render(request,'something.html')
return redirect('url')  #重定向 

[!note]

csrf_token 校验

<form action="/login/" method="post">

        {% csrf_token %}

        <input type="text" name="username" placeholder="用户名">

        <input type="password" name="password" placeholder="密码">

        <input type="submit" value="登录">

    </form>

[!note]

重定向

from django.shortcuts import render, HttpResponse, redirect
return redirect('/info/list/')

[!note]

Json 序列化

from django.http import JsonResponse
return JsonResponse(data_dict)

orm 操作数据库

[!note]

连接数据库

在 settings. py 文件进行配置

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql',

        'NAME': 'django_db',

        'USER': 'root',

        'PASSWORD': '105105',

        'HOST': '127.0.0.1',

        'PORT': 3306,

    }

}

[!note]

创建表【verbose_name 列名注解】

from django.db import models

class UserInfo(models.Model):

    username = models.CharField(【verbose_name】,max_length=32)

    password = models.CharField(max_length=64)

    age = models.IntegerField()

"""
create table app01_userinfo (
    id int primary key auto_increment,
    username varchar(32),
    password varchar(64),
    age int
)
"""

执行命令

python manage.py makemigrations
python manage.py migrate

如果再次想要创建表就再次创建函数,不要的表进行注释。完成动作后执行命令。

[!note]

在已有表中添加列

class UserInfo(models.Model):

    username = models.CharField(max_length=32)

    password = models.CharField(max_length=64)
    # 新建列
    age = models.IntegerField(【default='值'】【null=True,blank=True】)


进行选项:

  • 1 为输入值
  • 2 为退出
  • 也可以在括号内进行 default 设置默认值
  • null=True, blank=True 允许为空

[!note]

添加约束

# 外键关联部门表 to_field='id' 指定关联的列  on_delete=models.CASCADE 级联删除 to 指定关联的表 depart列名自动变成depart_id

depart = models.ForeignKey(to='Department',to_field='id',on_delete=models.CASCADE)

[!note]

级联删除

depart = models.ForeignKey(to='Department',to_field='id',on_delete=models.CASCADE)

[!note]
置空

depart = models.ForeignKey(to='Department', to_field='id', null=True, blank=True, on_delete=models.SET_NULL)

[!note]

选择数据

gender_choices = (
    (1, '男'),
    (2, '女'),
)
gender = models.SmallIntegerField(verbose_name='性别',choices=gender_choices)

[!note]

获取数据排序

models.表.objects.all().order_by('-列')   # 降序

[!note]

获取字典

row_dict = models.order.objects.filter(id=uid).values("id","title").first()

[!note]

获取元组

queryset = models.order.objects.all().values_list("id","title")

[!note]

操作表

class Department(models.Model):
    title = models.CharField(max_length=16)

# 新建数据 insert into app01_department (title) values ("销售部")
Department.objects.create(title="销售部")

在 views 操作

from app01 import models

def orm(request):
    # 新建
    models.Department.objects.create(title='销售部')
    models.Department.objects.create(title='IT部')
    models.Department.objects.create(title='运营部')
    models.UserInfo.objects.create(username='张三', password='123', age=20)
    
    # 删除
    models.Department.objects.filter(title='销售部').delete()
    # 删除全部
    models.Department.objects.all(title='销售部').delete()

    # 获取数据 queryset类型
    data_list = models.Department.objects.all()
    for obj in data_list:
        print(obj.id, obj.title)
    # 获取一行数据,但也是列表
    data = models.UserInfo.objects.filter(id=1)
    # 直接获取对象
    data = models.UserInfo.objects.filter(id=1),first()


    # 更新数据,id=1的age更新为18
    models.UserInfo.objects.filter(id=1).update(age=18)
     # 更新所有数据,age更新为18
    models.UserInfo.objects.all().update(age=18)

    return HttpResponse('成功')
            

[!note]

HTML 模板继承【只需要编写占位符所在位置】

{% extends 'layout.html' %}

{% block content %}
    内容
{% endblock %}

<div>
    <div class="container">
        {% block content %}{% endblock %}
    </div>
</div>

[!note]

获取数字对应字符

gender_choices = (

        (1, '男'),

        (2, '女'),

    )

 obj.get_gender_display()   #get_字段名称_display(),在模板语法中没括号

[!note]

关联外键,跨表查询

obj.deaprt.属性
#根据id自动去关联的表中获取哪一行数据depart对象。

[!note]

搜索数据

models.PrettyNum.objects.filter(mobile=txt_mobile,id=2)

# 等同于

data_dict = {"mobile":txt_mobile,"id":2}
models.PrettyNum.objects.filter(**data_dict)  # 空字典获取所有


# 数字条件判断
models.PrettyNum.objects.filter(mobile=txt_mobile,id=2) # 等于2
models.PrettyNum.objects.filter(mobile=txt_mobile,id__gt=2) # 大于2
models.PrettyNum.objects.filter(mobile=txt_mobile,id__gte=2) # 大于等于2
models.PrettyNum.objects.filter(mobile=txt_mobile,id__lt=2) # 小于2
models.PrettyNum.objects.filter(mobile=txt_mobile,id__lte=2) # 小于等于2
data_dict = {"id__gte":2}
models.PrettyNum.objects.filter(**data_dict)  # 空字典获取所有

# 字符串条件判断
models.PrettyNum.objects.filter(mobile__startwith="1999",id=2) # 以1999开头
models.PrettyNum.objects.filter(mobile__endwith="1999",id=2) # 以1999结尾
models.PrettyNum.objects.filter(mobile__contains="1999",id=2) # 包含1999
data_dict = {"mobile__contains":"1999"}
models.PrettyNum.objects.filter(**data_dict)  # 空字典获取所有

[!note]

分页截取

model.PrettyNum.objects.all()[start:end]

[!note]

后端文本转化 html

from django.utils.safestring import mark_safe

page_str_list = []

    for i in range(1,21):

        ele = '<li><a href="?page={}">{}</a></li>'.format(i,i)

        page_str_list.append(ele)

    page_string = mark_safe("".join(page_str_list))

组件

Form 组件

  1. view. py
    class MyForm(Form):
      user = forms.CharField(widget=forms.Input)
      pwd = form.CharFiled(widget=forms.Input)
      email = form.CharFiled(widget=forms.Input)
    
    
    def user_add(request):
        """新建用户"""
        if request.method == "GET":
    

        # 获取所有部门列表供选择

        return render(request, "user_add.html", {"from":from})

```
2. user_add. html

<pre data-language=HTML><code class="language-markup line-numbers"><from method="post">
{% for field in form %}
{ field }
{% endfor %}
<from>

```

```html
<from method="post">
{{ from.user }}
{{ from.pwd }}
{{ from.email }}
<from>

```

### ModelForm 组件

1. models. py

```python
class UserInfo(models.Model):
    """员工表"""
    name = models.CharField(verbose_name='姓名', max_length=16)
    password = models.CharField(verbose_name='密码', max_length=64)
    age = models.IntegerField(verbose_name='年龄')
    account = models.DecimalField(verbose_name='账户余额', max_digits=10, decimal_places=2, default=0)
    create_time = models.DateTimeField(verbose_name='入职时间')
    # 外键关联部门表 to_field='id' 指定关联的列  on_delete=models.CASCADE 级联删除 to 指定关联的表  depart列名自动变成depart_id
    depart = models.ForeignKey(to='Department', to_field='id', on_delete=models.CASCADE)
    gender_choices = (
        (1, '男'),
        (2, '女'),
    )
    gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
</code></pre>

<ol start="2">
<li>view. py

<pre><code class="language-python line-numbers">from django import forms

class MyForm(forms.ModelForm):
class Meta:
model = UserInfo
fields = ["name", "password", "age"]
# fields = "__all__" 所有字段
# exclude ['level'] 排除某字段,其他都带
widgets = {
# 定义生成插件类名
"name": forms.TextInput(attrs={"class":"form-control"}),
"password": forms.TextInput(attrs={"class":"form-control"}),
}
################## 高级加类名 ########################
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环找到所有插件class添加form-control
for name, field in self.field.items():
# 跳过指定字段
#if name=="password":
# continue
field.widget.attrs = {"class":"form-control","placeholder":"field.label"}

def user_add(request):
    """新建用户"""
    if request.method == "GET":
</code></pre></li>
</ol>

        # 获取所有部门列表供选择

        return render(request, "user_add.html", {"from":from})

```
3. user_add. html

<from method="post">
{% for field in form %}
    { field.lable } { field }
{% endfor %}
<from>

   ```

   ```html
<from method="post">
{{form.user.Label}} {{ from.user }}  #关联model中字段
{{form.password.Label}} {{ from.pwd }}
{{form.email.Label}} {{ from.email }}
<from>

   ```

魔法方法定制返回内容
![](//images.weserv.nl/?url=https://gitee.com/SherryBX/img/raw/master/202503271454184.png)


获取 POST 请求表单

```python
form = UserModelForm(data=request.POST)
if form.is_valid():
    print(form.cleaned_data) # 输出表单数据
    form.save() # 表单存储到表
else:
    print(form.errors)
{{field.errors.0}}  # 错误信息

设置中文错误提示

显示默认值

form = UserModelForm(instance=row_object)

修改数据

row_object = models.UserInfo.objects.filter(id=nid).first()
form = UserModelForm(data=request.POST, instance=row_object)
if form.is_valid():
# 默认保存的是用户输入的所有数据,如果想要再用户输入以外增加一点值
    # form.instance.字段名 = 值 
    form.save()

表单数据不可修改

mobile = forms.CharField(

        label='手机号',
        disabled=True

    )

去除浏览器表单自带验证

<form novalidate>
  <!-- 表单内容 -->
</form>

浏览器自动填充

<label for="email">Email:</label>
<input name="email" id="email" type="email" autocomplete="off" />

<label for="firstName">First Name:</label>
<input name="firstName" id="firstName" type="text" autocomplete="on" />

利用正则判断

from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

# 方式一
class PrettyModelForm(forms.ModelForm):

    # 定义字段验证规则

    mobile = forms.CharField(

        label='手机号',

        validators=[],  # 自定义验证器列表

        widget=forms.TextInput(attrs={

            'class': 'form-input',

            'placeholder': '请输入11位手机号',

            'validators': [RegexValidator(r'^1[3-9]\d{9}$', '手机号格式不正确')]

        })

    )


# 方式二
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"] # 获取用户传入值
        if len(txt_mobile) != 11:
            raise ValidationError("格式错误")
        return txt_mobile   

判断值是否重复

# 添加页面


def clean_mobile(self):
    txt_mobile = self.cleaned_data["mobile"] # 获取用户传入值
    model.PrettyNum.objects.filter(mobile=text_mobile).exists() # 判断是否存在
    if exists:
        raise ValidationError("手机号已存在")
    return txt_mobile


# 编辑页面

def clean_mobile(self):
    txt_mobile = self.cleaned_data["mobile"] # 获取用户传入值
    # 排除id=2,手机号是否重复
    model.PrettyNum.objects.filter(mobile=text_mobile).exclude(id=2)



获取当前 id

def clean_mobile(self):
    print(self.instance.pk)

获取验证过的所有值

if form.is_valid():
    print(form.cleaned_data)

##########################################

# 验证密码

from django.core.exceptions import ValidationError
def clean_confirm_password(self):
    pwd = self.cleaned_data.get("password")
    confirm = self.cleaned_data.get("confirm_password")
    if confirm != pwd:
        raise ValidationError("密码不一致")
    return confirm

报错保留值

widgets = {
    "password":forms.PasswordInput(render_value=True)
}

分页类

class Pagination:
    def __init__(self, request, queryset, page_size=10, page_param="page", plus=2):
        """
        初始化分页类
        :param request: 请求对象
        :param queryset: 查询集合
        :param page_size: 每页显示数量
        :param page_param: URL中传递的分页参数,例如 ?page=1 中的 page
        :param plus: 显示当前页码前后的页码个数
        """
        # 获取页码参数
        page = request.GET.get(page_param, "1")
        if page.isdecimal():
            page = int(page)
        else:
            page = 1
        self.page = page
        self.page_size = page_size
        # 计算起始位置
        self.start = (page - 1) * page_size
        self.end = page * page_size
        # 切片查询
        self.page_queryset = queryset[self.start:self.end]
        # 计算总页数
        total_count = queryset.count()
        total_page_count, div = divmod(total_count, page_size)
        if div:
            total_page_count += 1
        self.total_page_count = total_page_count
        self.plus = plus
        # 保存request对象,用于构建URL
        self.request = request
    def html(self):
        """生成分页HTML"""
        # 基础样式定义
        base_style = "display:inline-flex; align-items:center; justify-content:center; min-width:32px; height:32px; padding:0 12px; margin:0 2px; font-size:13px; border-radius:4px; cursor:pointer; text-decoration:none; transition:all 0.3s;"
        normal_style = f"{base_style} background:#f4f4f5; color:#606266; border:none;"
        active_style = f"{base_style} background:#409eff; color:white; font-weight:500; border:none;"
        disabled_style = f"{base_style} background:#f4f4f5; color:#c0c4cc; cursor:not-allowed; border:none;"
        # 计算页码范围
        if self.total_page_count <= 2 * self.plus + 1:
            start_page = 1
            end_page = self.total_page_count
        else:
            if self.page <= self.plus:
                start_page = 1
                end_page = 2 * self.plus + 1
            else:
                if (self.page + self.plus) > self.total_page_count:
                    start_page = self.total_page_count - 2 * self.plus
                    end_page = self.total_page_count
                else:
                    start_page = self.page - self.plus
                    end_page = self.page + self.plus
        def build_url(page_num):
            params = self.request.GET.copy()
            params['page'] = page_num
            return '?' + params.urlencode()
        page_str_list = []
        # 容器样式
        container_style = "display:flex; align-items:center; justify-content:center; margin:20px 0; padding:8px 0;"
        page_str_list.append(f'<div style="{container_style}">')
        # 首页
        page_str_list.append(
            f'<a href="{build_url(1)}" style="{normal_style}">首页</a>'
        )
        # 上一页
        if self.page > 1:
            page_str_list.append(
                f'<a href="{build_url(self.page - 1)}" style="{normal_style}">上一页</a>'
            )
        else:
            page_str_list.append(
                f'<span style="{disabled_style}">上一页</span>'
            )
        # 页码
        for i in range(start_page, end_page + 1):
            if i == self.page:
                page_str_list.append(
                    f'<span style="{active_style}">{i}</span>'
                )
            else:
                page_str_list.append(
                    f'<a href="{build_url(i)}" style="{normal_style}">{i}</a>'
                )
        # 下一页
        if self.page < self.total_page_count:
            page_str_list.append(
                f'<a href="{build_url(self.page + 1)}" style="{normal_style}">下一页</a>'
            )
        else:
            page_str_list.append(
                f'<span style="{disabled_style}">下一页</span>'
            )
        # 尾页
        page_str_list.append(
            f'<a href="{build_url(self.total_page_count)}" style="{normal_style}">尾页</a>'
        )
        page_str_list.append('</div>')
        return ''.join(page_str_list)

拼接 url 参数

def build_url(page_num):
            # 复制当前请求的GET参数
            params = self.request.GET.copy()
            # 设置或更新page参数
            params['page'] = page_num
            # 将参数转换为URL查询字符串,例如:'q=123&page=2'
            return '?' + params.urlencode()

#################################################################

import copy
query_dict = copy.deepcopy(request.GET)
query_dict._mutable = True
query_dict.setlist('page',[11])
print(query_dict.urlencode())

加密

md 5

import hashlib
def md5(data_string):
    obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))  # 加盐
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()

Cookie 和 Session

[!tip]

网站生成随机字符串;写到用户浏览器的 cookie 中;在写入到 session 中

request.session["info"]="value"

[!note]

信息存放在 django_session 数据库


内部自动加密

[!note]

判断是否登录

info = request.session.get("info")
if not info:
    return redirect('/login/')

[!note]

注销账号

def logout(request):
    request.session.clear()
    return redirect('/login/')

[!note]

模板获取 session 信息

{ request.session.info.id }
{ request.session.info.name }

[!note]

保存验证码验证

# 调用oillow函数,生成图片
img,code_string = check_code()
# 写入到自己的session中(以便于后续获取验证码再进行校验)
request.session['image_code'] = code_string
# 给Session设置60s超时
request.session.set_expiry(60)

中间件

[!tip]

应用中间件

[!note]

中间件统一判断是否登录

from django.utils.deprecation import MiddlewareMixin

from django.shortcuts import HttpResponse, redirect

class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):

        # 排除不需要登录的页面

        if request.path_info == "/login/":

            return None



        info = request.session.get("info")

        if not info:

            # 如果方法中没有返回值(返回None),继续向后走

            return redirect('/login/')



    def process_response(self, request, response):

        print("中间件响应")

        return response

图片验证码

[!note]

生成图片

from PIL import Image
img = Image.new(name='RGB', size=(120,30), color=(255,255,255))

# 保存本地
with open('code.png','wb') as f:
    img.save(f,format='png')

[!note]

创建画笔绘画

from PIL import Image, ImageDraw, ImageFont

# 创建画布
img = Image.new('RGB', (120,30), color=(255,255,255))

# 创建画笔
draw = ImageDraw.Draw(img)

# 画线
draw.line((0,0, 120,30), fill=(255,0,0), width=2)

# 画点
draw.point((50,50), fill=(0,0,255))

# 画矩形
draw.rectangle((10,10, 110,20), outline=(0,255,0))

# 写文字
font = ImageFont.truetype('arial.ttf', size=16)
draw.text((10,10), "验证码", font=font, fill=(0,0,0))

# 保存图片
img.save('code.png')

[!note]

 图片处理操作

 from PIL import Image

 # 打开图片
 img = Image.open('code.png')

 # 调整大小
 img = img.resize((200, 100))

 # 旋转图片
 img = img.rotate(45)

 # 裁剪图片
 img = img.crop((0, 0, 100, 100))  # (left, top, right, bottom)

 # 图片格式转换
 img = img.convert('L')  # 转为灰度图

 # 保存修改后的图片
 img.save('new_code.png')

[!note]

验证码干扰

from PIL import Image, ImageDraw
import random

def create_lines(draw, width, height):
    """绘制干扰线"""
    for i in range(5):  # 画5条干扰线
        begin = (random.randint(0, width), random.randint(0, height))
        end = (random.randint(0, width), random.randint(0, height))
        draw.line([begin, end], fill=(0,0,0), width=2)

def create_points(draw, width, height, point_chance=5):
"""绘制干扰点"""
# point_chance: 噪点比例
chance = min(100, max(0, point_chance))
for w in range(width):
    for h in range(height):
        if random.randint(0, 100) < chance:
            draw.point((w, h), fill=(0,0,0))

def distort_image(image):
    """图片扭曲变形"""
    width, height = image.size
    new_image = Image.new('RGB', (width, height), (255,255,255))

    # 横向扭曲
    for i in range(width):
        for j in range(height):
            offset_x = int(random.uniform(0, 4))
            offset_y = int(random.uniform(0, 2))

            # 确保偏移后的坐标不越界
            new_x = i + offset_x if i + offset_x < width else width - 1
            new_y = j + offset_y if j + offset_y < height else height - 1

            pixel = image.getpixel((i,j))
            new_image.putpixel((new_x,new_y), pixel)

    return new_image

[!note]

完整验证码示例

from PIL import Image, ImageDraw, ImageFont
import random
import string

def generate_verify_code():
    # 创建画布
    width = 120
    height = 40
    image = Image.new('RGB', (width, height), (255,255,255))
    draw = ImageDraw.Draw(image)

    # 生成随机字符
    chars = ''.join(random.choices(string.ascii_uppercase + string.digits, k=4))

    # 添加文字
    font = ImageFont.truetype('arial.ttf', size=30)
    for i, char in enumerate(chars):
        x = 20 + i * 25
        y = random.randint(2, 8)
        # 每个字符颜色随机
        color = (random.randint(0,150), random.randint(0,150), random.randint(0,150))
        draw.text((x, y), char, font=font, fill=color)

    # 添加干扰线
    create_lines(draw, width, height)

    # 添加噪点
    create_points(draw, width, height)

    # 图片扭曲
    image = distort_image(image)

    return image, chars

# 生成验证码
image, code = generate_verify_code()
image.save('verify_code.png')
print(f"验证码内容: {code}")

[!note]

服务器内存验证

from io import BytesIo

import random

def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母   
        :return:
        """
        return chr(random.randint(65, 90))

    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img,''.join(code)


def image_code(request):

    """生成图片验证码"""

    #调用pillow函数,生成图片

    img,code_string = check_code()

    print(code_string)

    stream = BytesIo()

    img.save(stream,'png')

    return HttpResponse(stream.getvalue())

Ajax

  • 通过 jQuery 发送 Ajax

  • 编写 Ajax

    $.ajax({
    url:"发送的地址",
    typr:"get|post"
    data:{
        n1:123,
        n2:456
    },
    success:function(res){
        console.log(res);
    }
    })
    

[!note]

发送 post 免除 csrf

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def task_ajax(request):
    print(request.POST)
    return HttpResponse("成功了")

[!note]

反序列化 json

$.ajax({
    url:"发送的地址",
    typr:"get|post"
    data:{
        n1:123,
        n2:456
    },
    dataType:"JSON",
    success:function(res){
        console.log(res.status);
        console.log(res.data);
    }
})

[!note]

自动获取表单数据并打包

$.ajax({
    url:"发送的地址",
    typr:"get|post"
    data:$("#form3").serialize(),   //自动获取打包
    dataType:"JSON",
    success:function(res){
        console.log(res.status);
        console.log(res.data);
    }
})

[!note]

js 实现刷新页面

location.reload();

[!note]

显示模态框

$("#btnAdd").click(function ()
    $('#myModal').modal('show')
});

[!note]

关闭模态框

$("#btnAdd").click(function ()
    $('#myModal').modal('hide')
});

[!note]

循环错误信息

$.each(res.error,function (name,errorlist){
    $("#id_"+name).next()   //错误信息栏在下一个标签
})

[!note]

清空表单

// 清空表单 $("#formAdd")是jQuery对象->$("#formAdd")[0]DOM对象
$("#formAdd")[0].reset()

[!note]

获取当前行 id

/获取当前行的ID并赋值给全部变量」
DELETE_ID = $(this).attr("uid");

图表

  • highchart
  • echarts

[!note]

cdn 引入

<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>

[!tip]

分类

pie : 饼图
bar : 柱形图

[!note]

绘制柱形图

<body>
    <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
    <div id="main" style="width: 600px;height:400px;"></div>

    <script type="text/javascript">
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('main'));  //指定dom

      // 指定图表的配置项和数据
      var option = {
        title: {
          text: 'ECharts 入门示例'
        },
        tooltip: {},
        legend: {
          data: ['销量','业绩']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          },
          {
            name: '业绩',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
           }
        ]
      };

      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    </script>

</body>

[!note]

绘制饼图

option = {
  series: [
    {
      type: 'pie',
      data: [
        {
          value: 335,
          name: '直接访问'
        },
        {
          value: 234,
          name: '联盟广告'
        },
        {
          value: 1548,
          name: '搜索引擎'
        }
      ]
    }
  ]
};

[!note]

绘制折线图

option = {
  xAxis: {
    type: 'category',
    data: ['A', 'B', 'C']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [120, 200, 150],
      type: 'line'
    }
  ]
};

文件上传

[!note]

获取发过来的文件

<form method="post"enctype="multipart/form-data">
    {% csrf_token %}
    <input type="text"name="username">
    <input type="file"name="avatar">
    <input type="submit"value="提交")
</form>
def upload_list(request):
    if request.method =="GET":
        return render(request,'upload_list.html')
    # print(request.POST)       #请求体中数据
    # print(request.FILES)  #请求发过来的文件
    file_object request.FILES.get("avatar")
    print(file_object.name) # 获取文件名

    # 读取文件
    f open('a1.png',mode='wb')
    for chunk in file_object.chunks():
        f.write(chunk)
    f.close()

[!note]

获取传参

def upload_form(request):
    title="Form上传"
    if request.method =="GET":
    form = UpForm()
    return render(request,'upload_form.html',{"form":form,"title":title})
    form = UpForm(data=request.POST,files=request.FILES)

[!note]

启用 media 目录【在 urls. py 中配置】

from django.urls import path,re_path
from django.views.static import serve
from django.conf import settings

re_path(r'media/(?P<path>.*)$',serve,{'document_root'settings.MEDIA_ROOT},name='media'),    
# settings.py

impoct os
MEDIA_ROOT = os.path.join(BASE_DR,"media")
MEDIA_URL = "/media/"

# upload.py

media_path os.path.join(settings.MEDIA_ROOT,image_object.name) # 绝对路径
media_path os.path.join ("media", image_object. name) # 相对路径

[!note]

modelForm 写法

# 上传到 media/city/
img = models.FileField (verbose_name="Logo", max_length=128, upload_to='city/')