django和adminlte笔记

django

新增环境

1
2
3
apt install python3-venv
python3 -m venv django4
source django4/bin/active

安装Django

1
pip install django  -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple 

创建和初始化项目

创建

1
django-admin startproject mysite

修改settings文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import os # 新增
ALLOWED_HOSTS = ['*'] #修改
# 修改
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')], #修改
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# 以下为修改
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
# 新增static
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]

新增文件夹

1
mkdir 项目目录/templates 项目目录/static

自定义用户模型

新建一个app

1
python manage.py startapp manager

注册到settings.py中

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'manager', #新增
]

修改manager/models.py,根据自己的需要修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.db import models
from django.contrib.auth.models import BaseUserManager,AbstractUser
import uuid
# Create your models here.
# 自定义user,还需要在setting文件中配置AUTH_USER_MODELS
class UserDB(AbstractUser):
uid = models.UUIDField(primary_key=True,default=uuid.uuid4)
username = models.CharField(max_length=15,verbose_name="用户名", unique=True)
phone = models.CharField(max_length=11,verbose_name="手机号码", null=True,blank=True)
email = models.EmailField(verbose_name='邮箱', null=True,blank=True)
create_time = models.DateTimeField('创建时间',auto_now_add=True)
update_time = models.DateTimeField('更新时间',auto_now=True)
class Meta:
verbose_name = '用户信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.username

编辑settings.py文件

1
2
# 新增自定义用户模型
AUTH_USER_MODEL = 'manager.UserDB'

数据库迁移

1
2
3
4
#生成迁移文件
python manage.py makemigrations
#应用迁移文件
python manage.py migrate

创建用户

  • 管理员用户

1
python manage.py createsuperuser
  • 普通用户进入项目shell

1
python manage.py shell
1
2
from django.contrib.auth.models import User(用户数据库,默认为User,如果自定义了这里需要修改)
User.objects.create_user('username', '[email protected]', 'password')

站点验证和登录

除了指定界面,用户访问其他界面时,如果没有登录就跳到登录界面,登录完成后跳转到需要访问的界面比如用户访问:https://51yunwei.top/system/ ,会跳转为 https://51yunwei.top/login/?next=/system/,完成登录后,跳转回https://51yunwei.top/system/
此处代码来自运维咖啡吧,通过中间件的方式进行处理

  • 新建一个目录

1
2
3
4
mkdir system/middleware -p
cd system/middleware
touch __init__.py
touch loginrequired.py
  • 编辑loginrequired.py文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import redirect
from django.conf import settings


class LoginRequiredMiddleware:
def __init__(self, get_response): # 启动django的时候会执行init,用get_resonpse来获取下一个中间件或视图函数的响应
self.get_response = get_response
self.login_url = settings.LOGIN_URL
self.open_urls = [self.login_url] + getattr(settings, 'OPEN_URLS', []) # 登录界面和OPEN_URLS里面的路径都是开放的

def __call__(self, request): # 每次有请求到达时,Django都会调用这个方法。
if not request.user.is_authenticated and request.path_info not in self.open_urls:
return redirect(self.login_url + '?next=' + request.path)

return self.get_response(request) # 每个中间件都有一个get_response函数,这个函数会返回下一个中间件或视图函数的响应。

OPEN_URLS:不需要验证就能访问到的界面
LOGIN_URL:需要验证才能访问到的界面通过在settings中配置变量实现

  • 编辑settings文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'system.middleware.loginrequired.LoginRequiredMiddleware', # 新增,根据自己的路径进行修改
]
> 注意:验证的中间件要放最后面,因为最后一个中间件是第一个执行的。

# 新增全局登录地址
LOGIN_URL='/login/'
OPEN_URLS=[]

在登录的函数中拿到next地址,并返回,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class LoginView(View):
def get(self,request):
if request.user.is_authenticated:
return render(request,'base.html')
return render(request,'login.html')
def post(self,request):
# 判断用户登录
next_path = request.GET.get('next','/') # 虽然是post方法,但还是可以用GET来获取路径的参数,即 ?key=value
username = request.POST.get('username','')
password = request.POST.get('password','')
# print('post next====',request.POST.get('next',''),request.POST)
user = authenticate(username=username,password=password)
if user:
login(request,user)
request.session["username"] = username
request.session.set_expiry(0) # 设置session过期时间为一周后
return redirect(next_path)
login_error_message = '用户名或密码错误'
print('登录失败')
return render(request,'login.html',{'login_error_message':login_error_message})

返回函数

一些常见的视图返回函数的导入方式:

  1. HttpResponse:这是一个Django内置的HTTP响应类,可以直接从django.http模块导入。

1
from django.http import HttpResponse
  1. HttpResponseRedirect:这也是一个Django内置的HTTP响应类,可以直接从django.http模块导入。

1
from django.http import HttpResponseRedirect
  1. render:这是一个用于渲染模板并返回HTTP响应的函数,可以从django.shortcuts模块导入。

1
from django.shortcuts import render
  1. redirect:这是一个用于返回重定向视图的函数,也可以从django.shortcuts模块导入。

1
from django.shortcuts import redirect
  1. JsonResponse:这是一个用于返回JSON格式HTTP响应的函数,可以从django.http模块导入。

1
from django.http import JsonResponse
  1. Response:这是Django REST Framework中的一个函数,用于返回HTTP响应,需要从rest_framework.response模块导入。

1
from rest_framework.response import Response
  1. reverse

1
from django.urls import reverse

设置url

在项目根urls.py中配置

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path,include # 新增include

urlpatterns = [
path('admin/', admin.site.urls),
path('', include('manager.urls')), # 新增,app名称.urls
]

在app目录下新建urls.py

1
2
3
4
5
6
7
8
9
from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView),
path('user/',views.UserManager.as_view()),
path('login/',views.LoginView.as_view()),
path('user/<uuid:uid>/',views.UserDetail),
path('logout/',views.LogOut),
]

使用url模板标签生成路径

  • 在url中配置name

1
2
3
4
5
6
7
urlpatterns = [
path('', views.IndexView),
path('user/',views.UserManager.as_view(),name='userlist'), # 配置name
path('login/',views.LoginView.as_view()),
path('user/<uuid:uid>/',views.UserDetail),
path('logout/',views.LogOut),
]
  • 在前端文件使用

1
2
3
<a href="{% url 'userlist' %}" class="nav-link">
<!-- 如果需要用到变量,year为变量 -->
{% url 'blog-list' year %}
  • 如果有多个相同名称的name,可以在urls.py中设置一个app_name来区分

1
2
# manager/urls.py
app_name='manager'
1
<a href="{% url 'manager:userlist' %}" class="nav-link">

获取post数据

获取post数据

1
username = request.POST.get('username','')

在post方法中也可以用GET

1
2
3
def post(self,request):
# 判断用户登录
next_path = request.GET.get('next','/') # 虽然是post方法,但还是可以用GET来获取路径的参数,即 ?key=value

登录和退出登录和session

  • 登录和session

1
2
3
4
5
6
7
8
9
10
from django.contrib.auth import authenticate,login,logout
username = request.POST.get('username','')
password = request.POST.get('password','')
# print('post next====',request.POST.get('next',''),request.POST)
user = authenticate(username=username,password=password)
if user:
login(request,user)
request.session["username"] = username # 设置session
request.session.set_expiry(0) # 关闭浏览器就过期
return redirect(next_path)
  • 退出登录

1
2
3
4
5
from django.contrib.auth import authenticate,login,logout
def LogOut(request):
if request.user:
logout(request)
return redirect('/login/')

设置密码和检查密码

设置密码使用set_password

1
user.set_password('xxxx')

检查密码是否和输入的一致

1
2
from django.contrib.auth.hashers import check_password
check_password(request.POST.get('currpassword'),user.password) # 输入的密码,数据库中保存的密码

上传到github

1、生成requirements.txt文件

1
pip freeze > ./requirements.txt文件

设置全局可使用的变量

在template中,我们可以直接使用request变量,现在我们来设置一下可以直接使用的自定义变量
system/systeminfo.py

1
2
3
4
5
6
7
8
9
10
11
from manager.models import SystemInfoDB
def SystemInfo(request):
system_info = SystemInfoDB.objects.all()
if system_info.count():
system_title = system_info.first().system_title if system_info.first().system_title else '未设置的title'
system_name = system_info.first().system_name if system_info.first().system_name else '未设置的系统名称'
else:
system_title = '未设置的title'
system_name = '未设置的系统名称'

return {'systeminfo':{'system_title':system_title,'system_name':system_name}}

manager/models.py

1
2
3
4
5
6
7
8
class SystemInfoDB(models.Model):
system_title = models.CharField(max_length=128,verbose_name="网页title")
system_name = models.CharField(max_length=128,verbose_name="站点名称")
class Meta:
verbose_name = '系统信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.system_name

settings.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'system.systeminfo.SystemInfo', # 新增
],
},
},
]

这样就可以直接在html中使用这个变量了

1
<title>{{ systeminfo.system_title }}</title>

adminlte

点击菜单加载页面后,菜单没有高亮

在base.html添加以下代码

1
2
3
4
5
6
7
8
9
10
<script>
// 选择所有class为'nav-sidebar'的ul元素下的li元素
$('ul.nav-sidebar li').each(function(i){
// 检查当前li元素的第一个子元素(即a标签)的href属性值是否为当前路径
if($(this).find('a').attr('href')==='{{ request.path }}'){
// 如果条件满足,则为当前li元素的第一个子元素(即a标签)添加'active'类
$(this).find('a').addClass('active');
}
});
</script>

优化版:比如用户管理一个菜单,里面有编辑用户、查看用户等,url为/user/xxxx/edit/、/user/xxx/detail/,这样的话上面的方法就不管用了,因为菜单是/user/,匹配不到/user/xxx/edit/,所以修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
// 选择所有class为'nav-sidebar'的ul元素下的li元素
$('ul.nav-sidebar li').each(function(i){
// 把patharr切割为列表,如[user,xxxx,eidt]
var patharr = '{{ request.path }}'.split("/");
// 循环列表
for (var i = patharr.length; i > 0; i--) {
// 获取列表的值,并转换为字符串
// 比如一开始获取的值为 /user/xxx/edit/,如果没有匹配,下次循环为/user/xxx/,最后为/user/,只要有一个能匹配上,就把菜单高亮
var newpatharr = patharr.slice(0, i);
var path = newpatharr.join("/")+'/';
console.log(path)
// 检查当前li元素的第一个子元素(即a标签)的href属性值是否为当前路径
if($(this).find('a').attr('href')===path){
// 如果条件满足,则为当前li元素的第一个子元素(即a标签)添加'active'类
$(this).find('a').addClass('active');
break
}
}
});
</script>

data-widget属性

AdminLTE框架中就使用data-widget属性来激活一些插件,如iframe插件、todo list插件等。

以下是一个使用data-widget属性的例子:

1
<div class="col-lg-6" id="calendar_widget" data-widget="chat-init"></div>

在这个例子中,data-widget="chat-init"表示激活了一个名为"chat-init"的插件。

需要注意的是,data-widget属性并不是Bootstrap的标准属性,而是一些第三方框架(如AdminLTE)扩展出来的属性。因此,如果你在使用这些框架时遇到data-widget属性,可以参考相应的文档来了解其具体用法。

ajax提交数据

html代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form id="editUserForm" novalidate="novalidate">
<div class="card-body">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" name="username" class="form-control" id="username" value="{{ username }}" readonly>
</div>
<div class="form-group">
<label for="Email">邮箱地址</label>
<input type="email" name="email" class="form-control" id="Email" value="{{ email }}">
</div>
<div class="form-group">
<label for="phone">手机号码</label>
<input type="text" name="phone" class="form-control" id="phone" value="{{ phone }}">
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary">修改&保存</button>
<a href="{% url 'manager:userlist' %}" class="btn btn-success float-right"></i> 返回列表</a>
</div>
</form>

ajax代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$("#editUserForm button[type='submit']").click(function(e){
e.preventDefault(); // 阻止表单的默认提交行为
var formData = $( "#editUserForm" ).serialize();
$.ajax({
url: "{% url 'manager:useredit' uid %}", // 你的请求地址
type: "POST", // 请求类型,必须是POST
data: formData, // 你的表单数据
dataType: "json", // 返回的数据类型,必须是json
success: function(data){
if (data.result === 0) {
// 请求成功,显示消息
toastr.success(data.message)
} else if (data.result === 1) {
toastr.success(data.message)
}
},
error: function(){
// 请求失败的回调函数
console.log("Error");
}
});
});

python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def post(self,request,uid):
op_user = request.user
if not op_user.is_superuser or op_user.uid != uid: # 如果不是管理员或者修改的不是自己的用户信息
resultdict = {'result':1,'message':'无权限修改其他用户的信息'}
try:
user = UserDB.objects.filter(uid=uid).first()
user.email = request.POST.get('email','')
user.phone = request.POST.get('phone','')
user.save()
resultdict = {'result':0,'message':'修改成功'}
except Exception as err :
resultdict = {'result':1,'message':'修改用户信息失败%s' % err}
finally:
return JsonResponse(resultdict)

吐司

引入文件

1
2
3
4
5
6
<!-- toastr需要导入的css文件 -->
<link rel="stylesheet" href="{% static 'adminlte/plugins/toastr/toastr.min.css' %}">
<link rel="stylesheet" href="{% static 'adminlte/plugins/sweetalert2/sweetalert2.min.css' %}">
<!-- 吐司需要导入的js -->
<script src="{% static 'adminlte/plugins/toastr/toastr.min.js' %}"></script>
<script src="{% static 'adminlte/plugins/sweetalert2/sweetalert2.min.js' %}"></script>

初始化

1
2
3
4
5
6
var Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000 # 多少秒后消失
});

返回上一个url

1
<a href="javascript:history.go(-1);">

在datatable头添加一个按钮,和search平行

1
2
3
4
5
6
7
8
9
10
11
<script>
$(function () {
$("#usertable").DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"buttons": ["copy", "csv", "excel", "pdf", "print", "colvis"],
"sDom": "<'row'<'col-md-6'<'#toolbar'>><'col-md-6'f>>",//这里新增
}).buttons().container().appendTo('#example1_wrapper .col-md-6:eq(0)');
$("#toolbar").append('<a href="{% url "manager:usercreate" %}"><button type="button" class="btn btn-primary"><i class="fa fa-plus-square"></i>&nbsp&nbsp新建主机</button></a>') //这里新增
});
// document.querySelector('div.toolbar').innerHTML = '<b>Custom tool bar! Text/images etc.</b>';
</script>

post后url显示参数,如何修改?

1
2
3
<!-- 在form标签增加以下字段 -->
<form onsubmit="return false" >

提交数据后,通过request.POST获取不到

前端post后,django是通过标签中的name='xxx’获取到数据的,比如,后端通过reqeust.POST.get(‘username’)获取数据所以template一定要设置name属性

自定义模态框的内容

模态框html,id为mymodal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 模态框 -->
<div class="modal fade" id="mymodal" style="display: none;" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="mymodaltitle"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p id="mymodalcontent"></p>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<a id="mymodelhref" style="color: white;" role="button" class="btn btn-primary"></a>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- 模态框结束 -->

js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 这里用的是模态框的id
$('#mymodal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
//注意这里的whatever对应前面html代码中button标签下data-whatever属性的后半段,这里只要定义了data-xxx,就可以通过js获取
// var recipient = button.data('whatever') // Extract info from data-* attributes
// If necessary, you could initiate an AJAX request here (and then do the updating in a callback).
// Update the modal's content. We'll use jQuery here, but you could use a data binding library or other methods instead.
var modal = $(this)
//此处即为修改modal的标题,都是通过class获取
modal.find('.modal-title').text('退出登录')
modal.find('.modal-body p').text("是否确认退出登录?")
modal.find('.modal-footer a').text("确认退出")
modal.find('.modal-footer a').attr("href","{% url 'manager:logout' %}")
})

按钮的html只要设置了data-toggle="modal" data-target="#mymodal"就行,其中data-target是要弹出的模态框

1
<a class="nav-link"  href="/logout/" role="button" id="logoutbtn" data-toggle="modal" data-target="#mymodal">

jQuery表单校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<script src="{% static 'adminlte/plugins/jquery-validation/jquery.validate.min.js' %}"></script>
<script src="{% static 'adminlte/plugins/jquery-validation/additional-methods.min.js' %}"></script>
<script>
// ajax提交的时候要验证一下
$("#createUserForm button[type='submit']").click(function(e){
e.preventDefault(); // 阻止表单的默认提交行为
if (!$("#createUserForm").valid()) {
// 如果验证失败,返回false,不提交数据
return false;
};
var formData = $( "#createUserForm" ).serialize();
$.ajax({
url: "{% url 'manager:usercreate' %}", // 你的请求地址
type: "POST", // 请求类型,必须是POST
data: formData, // 你的表单数据
dataType: "json", // 返回的数据类型,必须是json
success: function(data){
if (data.result === 0) {
// 请求成功,显示消息
toastr.success(data.message)
// window.location.href = "{% url 'manager:userlist' %}";
setTimeout(function() {
window.location.href = "{% url 'manager:userlist' %}";
}, 1000);

} else if (data.result === 1) {
toastr.error(data.message)
}
},
error: function(){
// 请求失败的回调函数
console.log("Error");
}
});
});
// 验证规则
$.validator.addMethod("letterswithnumber", function(value,element) {
return this.optional(element) || /^(?=.*[a-z])(?=.*[0-9]).*$/i.test(value);
}, "密码中必须包含字母、数字和特殊字符");

$.validator.addMethod("specialchar", function(value,element) {
return this.optional(element) || /^(?=.*[!@#\$%\^&\*\(\)\-_=+{}:\,\<\>\?]).*$/i.test(value);
}, "密码中必须包含字母、数字和特殊字符");
$('#createUserForm').validate({
rules: {
email: {
required: false,
email: true,
},
password: {
required: true,
letterswithnumber: true,
specialchar: true,
minlength: 8
},
confirmpassword: {
required: true,
equalTo: "#password"
},
username: {
required: true,
pattern: /^[a-zA-Z0-9]+$/,
minlength: 5
},
},
messages: {
email: {
email: "请输入正确的邮箱"
},
password: {
required: "请提供密码",
etterswithnumber: "密码中必须包含字母和数字",
specialchar: "密码中必须包含特殊字符",
minlength: "密码至少 8 个字符,至少包含三种符号"
},
username: {
required: "请输入用户名",
minlength: "用户名至少 5 个字符,仅允许字母和数字"
},
confirmpassword: {
required: "请提供确认密码",
equalTo: "确认密码与密码不一致"
},
},
errorElement: 'span',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.form-group').append(error);
},
highlight: function (element, errorClass, validClass) {
$(element).addClass('is-invalid');
},
unhighlight: function (element, errorClass, validClass) {
$(element).removeClass('is-invalid');
}
});
</script>

使用datatable

html代码

1
2
3
4
5
6
7
8
9
10
11
12
<table id="usertable" class="table table-bordered table-striped">
<thead>
<tr>
<th>用户名</th>
<th>创建时间</th>
<th>邮箱</th>
<th>手机号码</th>
<th>操作</th>
</tr>
</thead>

</table>

js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$(function () {
$('#usertable').DataTable({
"ajax": "{% url 'manager:userlist' %}",
"columns": [
{"data": "username"},
{"data": "create_time"},
{"data": "email"},
{"data": "phone"},
{"data": "button"},
],
language: {//自定义语言提示
"processing": "处理中...",
"lengthMenu": "显示 _MENU_ 条用户数据",
"zeroRecords": "没有找到相应的结果",
"info": "第 _START_ 至 _END_ 行,共 _TOTAL_ 行",
"infoEmpty": "第 0 至 0 项结果,共 0 项",
"infoFiltered": "(由 _MAX_ 项结果过滤)",
"infoPostFix": "",
"url": "",
"thousands": "'",
"emptyTable": "表中数据为空",
"loadingRecords": "载入中...",
"infoThousands": ",",
"paginate": {
"first": "首页",
"previous": "上页",
"next": "下页",
"last": "末页"
}
},

"sDom": "<'row'<'col-md-6'<'#toolbar'>><'col-md-6'f>>" + //设置表格最上面内容,可以在这里添加按钮等其他设置
"t" + //设置tables
"<'row'<'col-md-5 sm-center'li><'col-md-7 text-right sm-center'p>>",//设置表格最下面显示内容
buttons: [{
text: '<i class="fa fa-download" title="导出execl"></i>',
extend: 'excel', // 导出为execl
title: '用户列表'+Date.now(), // 导出文件的名字
className: 'btn btn-primary', // 按钮样式
exportOptions:{
columns:[1,2,3,4], // 导出哪些列
// rows:[1,2,3,4] //导出哪些行
modifier: {
page: 'all', // 导出哪些页的数据,all为所有,current为当前页
},
Headers: true,// 导出表格表头
footer: true,// 导出表格表尾
extension: ".xlsx", // 导出的文件后缀
}
}]
});
$("#toolbar").append('<a href="{% url "manager:usercreate" %}"><button type="button" class="btn btn-primary"><i class="fa fa-plus-square"></i>&nbsp&nbsp新建用户</button></a>')


});

后端代码,返回的格式UserDict[“data”],data要和js的columns data名称一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def UserIndex(request):
return render(request,'user/index.html')
class UserManager(View):
def get(self,request):
# if not request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest':
# return render(request,'user/index.html')
# print('not ajax request')
UserInfo = UserDB.objects.all()
# print(user)
UserList = []
for User in UserInfo:
username = User.username
create_time =User.create_time
email = User.email
phone = User.phone
button = """<div style="display: block;text-align: center;margin: auto;">
<a class="btn btn-primary btn-sm" href="{0}">
<i class="fas fa-folder">
</i>
详情
</a>
<a class="btn btn-info btn-sm" href="{1}">
<i class="fas fa-pencil-alt">
</i>
编辑
</a>
<a class="btn btn-danger btn-sm" data-url="{2}" data-user='{3}' data-toggle="modal" data-target="#mymodal">
<i class="fas fa-trash">
</i>
删除
</a>
</div>
""".format(reverse('manager:userdetail',kwargs={'uid':User.uid}),reverse('manager:useredit',kwargs={'uid':User.uid}),reverse('manager:userdel',kwargs={'uid':User.uid}),User.username)
UserList.append({'username': User.username,'create_time':User.create_time.strftime('%Y-%m-%d %H:%M:%S'),'email':User.email,'phone': User.phone,'uid':str(User.uid),'button':button})
print(username)

UserDict = {}
UserDict["data"] = UserList

return HttpResponse(json.dumps(UserDict))

urls.py

1
2
path('user/',views.UserIndex,name='userindex'),
path('user/list/',views.UserManager.as_view(),name='userlist'),

刷新datatable的数据

1
$('#usertable').DataTable().ajax.reload();

截断datatable数据

使用DataTables提供的ellipsis插件,它专门用于文本的截断显示。这个插件可以对过长的文本进行截断,并在结尾添加省略号。使用这个插件非常简单,首先需要在HTML中引入相应的JS文件:

1
<script src="https://cdn.datatables.net/plug-ins/1.11.3/dataRender/ellipsis.js"></script>

然后在DataTables的配置中使用render.ellipsis方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('#example').DataTable({
// 其他配置
"columnDefs": [
{
"targets": [ 0 ],
"render": $.fn.dataTable.render.ellipsis( 40 ) // 截断长度为40个字符
}
]
});
```js

## datatables默认排序
```js
"order": [[ 1, "desc" ]]

bootstrap-switch监听按钮变化事件

1
2
3
4
5
$("#userstat_value").on('switchChange.bootstrapSwitch', function (event,state) { 
var status = this.checked ? 'True' : 'False';
console.log('userstat'+status)
$('input[name="userstat"]').val(status);
});

文件上传

html

1
2
3
4
5
6
7
8
9
<!-- 不在form表单中 -->
<div class="card-body" style="display: block;">
<div class="form-group">
<label for="logoimg">站点图标(33x33)</label>
<div class="file-loading">
<input id="logoimg" name="logoimg" type="file">
</div>
</div>
</div>

js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<script src="{% static 'adminlte/plugins/bs-custom-file-input/bs-custom-file-input.js' %}"></script>
<!-- bootstrap 5.x or 4.x is supported. You can also use the bootstrap css 3.3.x versions -->
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" crossorigin="anonymous"> -->

<!-- default icons used in the plugin are from Bootstrap 5.x icon library (which can be enabled by loading CSS below) -->
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" crossorigin="anonymous"> -->

<!-- alternatively you can use the font awesome icon library if using with `fas` theme (or Bootstrap 4.x) by uncommenting below. -->
<!-- link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" crossorigin="anonymous" -->

<!-- the fileinput plugin styling CSS file -->
<link href="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />

<!-- if using RTL (Right-To-Left) orientation, load the RTL CSS file after fileinput.css by uncommenting below -->
<!-- link href="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/css/fileinput-rtl.min.css" media="all" rel="stylesheet" type="text/css" /-->

<!-- the jQuery Library -->
<!-- <script src="https://code.jquery.com/jquery-3.6.0.min.js" crossorigin="anonymous"></script> -->

<!-- buffer.min.js and filetype.min.js are necessary in the order listed for advanced mime type parsing and more correct
preview. This is a feature available since v5.5.0 and is needed if you want to ensure file mime type is parsed
correctly even if the local file's extension is named incorrectly. This will ensure more correct preview of the
selected file (note: this will involve a small processing overhead in scanning of file contents locally). If you
do not load these scripts then the mime type parsing will largely be derived using the extension in the filename
and some basic file content parsing signatures. -->
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/plugins/buffer.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/plugins/filetype.min.js" type="text/javascript"></script>

<!-- piexif.min.js is needed for auto orienting image files OR when restoring exif data in resized images and when you
wish to resize images before upload. This must be loaded before fileinput.min.js -->
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/plugins/piexif.min.js" type="text/javascript"></script>

<!-- sortable.min.js is only needed if you wish to sort / rearrange files in initial preview.
This must be loaded before fileinput.min.js -->
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/plugins/sortable.min.js" type="text/javascript"></script>

<!-- bootstrap.bundle.min.js below is needed if you wish to zoom and preview file content in a detail modal
dialog. bootstrap 5.x or 4.x is supported. You can also use the bootstrap js 3.3.x versions. -->
<!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script> -->

<!-- the main fileinput plugin script JS file -->
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/fileinput.min.js"></script>

<!-- following theme script is needed to use the Font Awesome 5.x theme (`fas`). Uncomment if needed. -->
<!-- script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/themes/fas/theme.min.js"></script -->

<!-- optionally if you need translation for your language then include the locale file as mentioned below (replace LANG.js with your language locale) -->
<script src="https://cdn.jsdelivr.net/gh/kartik-v/[email protected]/js/locales/LANG.js"></script>
<script>
$("#logoimg").fileinput({
showUpload: true, //显示上传按钮
dropZoneEnabled: false,
maxFileCount: 1, //最大选择的文件数
inputGroupClass: "input-group-lg",
allowedFileExtensions: ["png"], // 允许的文件扩展名
language: 'zh',
uploadUrl: "{% url 'manager:system' %}", // 上传的URL

}).on("fileuploaded", function (e,data,previewId,index) {
// 上传成功后触发的事件
toastr.success('修改站点图标成功,当前界面可能有缓存,如未更新,请强制刷新一次')
setTimeout(function() {
location.reload();
}, 3000);
});

ace编辑器

引入

1
2
<!-- 地址 -->
https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.3/ace.min.js

使用

1
2
3
4
5
6
7
8
9
 <div class="form-group">
<label for="password"><a style="color: red;"></a>IP地址(选择黑/白名单时需配置,每行一个IP或网段,如1.1.1.1 或者 1.1.1.0/24)</label>
<div class="col-md-12">
<textarea class="form-control" id="form_content" name="content" rows="5"></textarea>
<pre id="content" style="height:415px"></pre>
</div>
</div>
<script src="{% static 'ace/ace.min.js' %}"></script>

1
2
3
4
5
6
7
8
9
10
// ACE配置
var editor = ace.edit("content");
var textarea = $('textarea[name="content"]').hide();
editor.getSession().on('change', function(){
textarea.val(editor.getSession().getValue());
});
editor.setShowPrintMargin(false);
// 字体大小
editor.setFontSize(18);
editor.resize()

js换行

1
2
var multiLineString = `这是第一行
这是第二行`;