开始
建立django文件。
python -m venv venv #创建venv文件
venv/Scripts/activate # 进入activate文件
deactivate 退出虚拟环境
pip install django==4.1 -i Simple Index 下载django资源
建立django项目(django-admin 命令 像pip类似)
运行django demo文件:django-admin startproject demo
cd demo
运行py文件:python manage.py runserver
models
model子类模版
from django.db import models
from utils.basemodel import BaseModel
# Create your models here.
class User(BaseModel):
# primary_key 主键 max_length 字符串长度 null 数据是否可以设置为空值 blank 是否显示为空 unique 是否是唯一值
id = models.IntegerField(primary_key=True)
username = models.CharField('用户名',max_length=30,null=True,blank=True,unique=True)
password = models.CharField('密码',max_length=30)
email = models.EmailField('邮箱',unique=True,null=True,blank=True)
class Meta:
# 给表命名
db_table = 'User'
verbose_name = '用户信息'
basemodel父类模版
from django.db import models
class BaseModel(models.Model):
# auto_now 默认时间 editable 可以填可以不填
Created_at = models.DateTimeField('创建时间', auto_now_add=True,editable=True)
updated_at = models.DateTimeField('更新时间', auto_now=True,editable=True)
# 设置为抽象类
class Meta:
abstract = True
增删改查
python manage.py shell --启动shell
from account.models import User 导入数据表
(1)
user_obj = User(id = '1',username='wjy',passwor='123',email='123@qq.com')
user_obj.save()
(2)
user = User.objects.create(id = '1',username='wjy',passwor='123',email='123@qq.com')
user1 = User(id = '1',username='wjy1',password='123',email='1123@qq.com')
user2 = User(id = '2',username='wjy2',password='123',email='2123@qq.com')
user3 = User(id = '3',username='wjy3',password='123',email='323@qq.com')
list = [user1,user2,user3]
user = User.objects.bulk_create(list)
(1)
user.objects.all()
(2)
User.objects.get(username='wjy')
(3)
User.objects.first()
User.objects.first()
(4)查询多条记录(所有id大于1的记录)
User.objects.filter(id__gt=1)
查询条件利用上面返回1条或者多条数据做条件返回。
多条件查询
User.objects.filter(created_at__minute=44).filter(created_at__minute=11)
修改数据
user = User.objacts.filter(username='wjy').update(password='222',email='123.qq.com')
数据删除
user = User.objacts.filter(username='wjy')
user.delete()
django管理后台
注册管理员:python manage.py createsuperuser
配置后台中文和时间 settings.py
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
展示字段在admin.py中设置
from django.contrib import admin
from account.models import User
# 自定义函数(本表)
# def get_author(obj):
# return obj.user.username
# 自定义函数(关联表中)
# get_author.short_descriton = '作者'
# Register your models here.
class UserAdmin(admin.ModelAdmin):
# 配置展示列表
list_display = ('id','username','email')
# 展示的时候添加关联表的字段 上面还需要做自定义函数
# list_display = ('id',get_author,'username','email')
# 属性可以点击修改
list_display_links = ('id','username','email')
# 配置过滤字段
list_filter = ('username','email')
# 配置搜索信息
search_fields = ('username','email')
# 只读属性
readonly_fields = ('id',)
admin.site.register(User, UserAdmin)
app.py
from django.apps import AppConfig
class AccountConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'account'
verbose_name = '用户管理'
企业官网制作
企业管理项目:
建立项目
根据后台建立文件-----配置文件 setting: INSTALLED_APPS、DATABASES、LANGUAGE_CODE、TIME_ZONE、STATICFILES_DIRS
调试url-------创建index.html
连通数据库------数据迁移
创建管理后台账号密码 createsuperuser
复制静态界面-----更改静态界面配置 {% load static %}
根据静态界面创建后台数据表
创建表:models.py 创建后台上传数据表:admin.py
显示动态照片:
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
补充
创建数据表
class News(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField('标题',max_length=100)
content = RichTextUploadingField()
cover = models.ImageField('封面',upload_to='news')
created_at = models.DateTimeField('创建时间',auto_now_add=True,)
uped_at = models.DateTimeField('创建时间',auto_now=True,editable=True)
# categrory = models.ForeignKey(Categrory, on_delete=models.CASCADE)
categrory = models.ForeignKey(Categrory, on_delete=models.CASCADE)
class Meta:
db_table = 'news'
verbose_name= '新闻管理'
verbose_name_plural = '新闻管理'
def __str__(self):
return self.title
寻找到图片的位置
from django.conf import settings
from django.conf.urls.static import static
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
# MEDIA_ROOT = os.path.join(BASE_DIR,'static')
# 配置media
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 配置ckeditor
CKEDITOR_UPLOAD_PATH = 'upload/'
CKEDITOR_IMAGE_BACKEND = 'pillow'
学生系统
班级管理
grades关键点
实现功能:建立model中的数据表;urls开启三个链接,一个是默认显示列表数据,使用继承django的ListView视图类,一个是新增功能url,继承使用CreateView类,一个是修改类型url,继承UpdateView类。引入form表单,重写继承modelform,可以少些一些参数。
model
from django.db import models
# Create your models here.
class Grade(models.Model):
grade_name = models.CharField(max_length=50, unique=True,verbose_name='班级名称')
grade_number = models.CharField(max_length=10, unique=True, verbose_name='班级编号')
def __str__(self):
return self.grade_name
class Meta:
db_table = 'grade'
verbose_name = '班级'
verbose_name_plural = "班级"
urls
from django.urls import path,include
# 引入类视图
from .views import GradeListView,GradeCreateView,GradeUpdateView,GradeDeleteView
urlpatterns = [
# 引用视图类,并给它命名
path('', GradeListView.as_view(), name='grades_list'),
path('create/',GradeCreateView.as_view(), name='grade_create'),
path('<int:pk>/update/',GradeUpdateView.as_view(), name='grade_update'),
path('<int:pk>/delete/',GradeDeleteView.as_view(), name='grade_delete'),
]
view
from django.shortcuts import render
# 引入列表视图
from django.views.generic import ListView,CreateView,UpdateView,DeleteView
# 引入表
from .models import Grade
from django.db.models import Q
from .forms import GradeForm
# 引入跳转函数
from django.urls import reverse_lazy
# Create your views here.
class GradeListView(ListView):
# 展示表
model = Grade
# 展示html的路径
template_name = 'grades/grades_list.html'
# 展示的表中字段
fields = ['grade_name', 'grade_number']
# 相当于render传递数据
context_object_name = 'grades'
# 设置页面展示数目
paginate_by = 5
# 文件创建:设定总的grades路由,因为有多个访问路径(增/改等链接),所以在grades下面做urls视图,
# 视图继承现有ListView列表类,设置列表类就可以访问template_name下的路径了
def get_queryset(self):
# 继承写queryset方法
queryset = super().get_queryset()
# 判断url获取search参数
search = self.request.GET.get('search')
if search:
# 引入Q函数,判断是否包含search中的内容。
queryset = queryset.filter(
Q(grade_name__icontains=search) |
Q(grade_number__icontains=search)
)
return queryset
class GradeCreateView(CreateView):
model = Grade
template_name = 'grades/grade-form.html'
# 引用表单类
form_class = GradeForm
# 成功后跳转 要求表单提交成功后必须设置
success_url = reverse_lazy('grades_list')
class GradeUpdateView(UpdateView):
model = Grade
template_name = 'grades/grade-form.html'
# 引用表单类
form_class = GradeForm
# 成功后跳转 要求表单提交成功后必须设置
success_url = reverse_lazy('grades_list')
class GradeDeleteView(DeleteView):
model = Grade
template_name = 'grades/grade_delete.html'
success_url = reverse_lazy('grades_list')
form
from django import forms
from .models import Grade
# 重写modelform可以少些一些属性
class GradeForm(forms.ModelForm):
class Meta:
# 两个必要参数
# 表
model = Grade
# 字段
fields = ['grade_name','grade_number']
记录关键部分
搜索框和下拉菜单 79
前端html form表单:
<div class="class-info">
<form method="get" action="/students/">
<span>班级: </span>
<select name="grade">
<option value="" selected="">请选择班级</option>
{% for grade in grades %}
<option value="{{ grade.pk }}" {% if grade.pk|stringformat:"s" == current_grade %} selected {% endif %} >
{{ grade.grade_name }}
</option>
{% endfor %}
</select>
<span>姓名/学号:</span>
<input type="text" name="search">
<input type="submit" value="搜索">
</form>
</div>
{% extends 'base.html' %}
{% load url_utils %}
{% block content %}
<div class="right">
<div class="top">
<div class="tool">
<div class="class-info">
<form method="get" action="/students/">
<span>班级: </span>
<select name="grade">
<option value="" selected="">请选择班级</option>
{% for grade in grades %}
<option value="{{ grade.pk }}" {% if grade.pk|stringformat:"s" == current_grade %} selected {% endif %} >
{{ grade.grade_name }}
</option>
{% endfor %}
</select>
<span>姓名/学号:</span>
<input type="text" name="search">
<input type="submit" value="搜索">
</form>
</div>
<div class="actions">
<button type="button" class="add" id="add">新增</button>
<button type="button" class="del" id="del-all">批量删除</button>
<button type="button" class="import" id="import">导入</button>
<button type="button" class="export" id="export">导出</button>
</div>
</div>
</div>
<div class="bottom">
<table>
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>姓名</th>
<th>班级</th>
<th>学号</th>
<th>性别</th>
<th>生日</th>
<th>联系电话</th>
<!-- <th>地址</th> -->
<th>操作</th>
</tr>
</thead>
<tbody>
{% for student in students %}
<tr>
<td><input type="checkbox" name="student_ids" value="{{ student.pk }}"></td>
<td>{{ student.student_name }}</td>
<td>{{ student.grade }}</td>
<td>{{ student.student_number }}</td>
<td>{{ student.gender }}</td>
<td>{{ student.birthday }}</td>
<td>{{ student.contact_number }}</td>
<td>
<a href="{% url 'student_update' student.pk %}" class="btn btn-primary btn-sm edit">编辑</a>
<a href="{% url 'student_delete' student.pk %}" class="btn btn-danger btn-sm del">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 分页导航 -->
<div class="pagination">
<span class="step-links">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?{% search_url request page=1 %}">« 首页</a>
<a href="?{% search_url request page=page_obj.previous_page_number %} ">上一页</a>
{% endif %}
<span class="current">
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?{% search_url request page=page_obj.next_page_number %}">下一页</a>
<a href="?{% search_url request page=page_obj.paginator.num_pages %}">尾页 »</a>
{% endif %}
</span>
</span>
</div>
</div>
</div>
<script>
// 全选
document.addEventListener('DOMContentLoaded', function () {
// 获取全选复选框元素
const selectAllCheckbox = document.getElementById('select-all');
// 监听全选复选框的点击事件
selectAllCheckbox.addEventListener('change', function () {
// 获取所有 name 为 'student_ids' 的复选框
const studentCheckboxes = document.querySelectorAll('input[name="student_ids"]');
// 根据全选复选框的选中状态设置每个学生复选框的状态
studentCheckboxes.forEach(function(checkbox) {
checkbox.checked = selectAllCheckbox.checked;
});
});
});
// 点击新增
document.getElementById('add').addEventListener('click', function() {
Swal.fire({
position: "top-end",
html: `<iframe src="{% url 'student_create' %}" width="100%", height="800px" frameborder="0" > <iframe>`,
width: 600,
showConfirmButton: false
});
});
// 点击编辑
document.querySelectorAll('.edit').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault(); // 阻止跳转
url = this.getAttribute('href')
Swal.fire({
position: "top-end",
html: `<iframe src="${url}" width="100%", height="800px" frameborder="0" > <iframe>`,
width: 600,
showConfirmButton: false
});
})
})
// 点击删除
document.querySelectorAll('.btn-danger').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault(); // 阻止跳转
url = this.getAttribute('href')
Swal.fire({
title: "确认删除?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "删除",
confirmButtonColor: "#d33",
}).then((result) => {
if (result.isConfirmed) {
// 向后台发送数据
fetch(url, {
method: 'DELETE',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': '{{ csrf_token }}',
},
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
Swal.fire("Deleted!", data.messages, "success");
window.location.reload();
} else {
Swal.fire("Error!", data.messages, "error")
}
})
}
})
})
})
// 批量删除
document.getElementById('del-all').addEventListener('click', function(){
// 是否有学生被选择
const checkboxes = document.querySelectorAll('input[name="student_ids"]:checked');
if (checkboxes.length === 0) {
Swal.fire({
title: "错误",
text: "请先选择要删除的学生信息",
icon: "error",
confirmButtonText: "好的"
});
return ;
}
// 如果有被选中,则fetch发送请求
Swal.fire({
title: "确认删除选中的数据?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "删除",
confirmButtonColor: "#d33",
})
.then((result) => {
if (result.isConfirmed) {
// 创建一个表单对象
const formData = new FormData()
// 遍历所有选择的数据
checkboxes.forEach((checkbox) => {
formData.append('student_ids', checkbox.value)
})
fetch("{% url 'student_bulk_delete' %}", {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': '{{ csrf_token }}',
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
Swal.fire("Deleted!", data.messages, "success");
window.location.reload();
} else {
Swal.fire("Error!", data.messages, "error")
}
})
}
})
})
// 导入excel
document.getElementById('import').addEventListener('click', function() {
Swal.fire({
title: '上传学生信息Excel',
input: 'file',
inputAttributes: {
'accept': '.xlsx',
'aria-label': 'Upload your Excel file'
},
showCancelButton: true,
confirmButtonText: 'Upload',
showLoaderOnConfirm: true,
preConfirm: (file) => {
// 处理文件上传的逻辑,例如使用 FormData 和 fetch API 上传文件
const formData = new FormData();
formData.append('excel_file', file);
return fetch('{% url "upload_student" %}', {
method: 'POST',
headers: {
'X-CSRFToken': "{{ csrf_token }}" // 确保添加 CSRF 令牌
},
body: formData,
})
.then(response => response.json())
.then(data => {
if (data.status === 'error') {
throw new Error(data.messages); // 直接抛出错误,让 catch 块处理
}
})
.catch(error => {
console.log(error);
Swal.showValidationMessage(`${error.messages || error }`);
});
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Uploaded!',
text: '上传成功.'
})
window.location.reload();
}
});
})
// 导出excel
document.getElementById('export').addEventListener('click', function() {
// 检查是否选择了班级
var select = document.querySelector('select[name="grade"]');
var value = select.value;
var gradeText = select.options[select.selectedIndex].text;
if (!value) {
Swal.fire({
title: '错误!',
text: '请选择一个班级!',
icon: 'error',
confirmButtonText: '确定'
});
return ;
}
// 提交请求
Swal.fire({
title: '确认',
text: '导出【' + gradeText + '】学生信息',
icon: 'warning',
showCancelButton: true,
confirmButtonText: '确认',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
// 发送请求
fetch('{% url "export_excel" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({grade: value})
})
.then(response => {
if (!response.ok) {
response.json().then(result => {
Swal.fire({
title: '下载失败',
text: '服务器错误: ' + result.messages,
icon: 'error',
confirmButtonText: '关闭'
});
});
throw new Error('网络或服务器错误');
}
return response.blob();
})
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = gradeText + '.xlsx';
document.body.appendChild(a);
a.click();
// 清理并移除元素
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
})
}
})
})
</script>
{% endblock %}
view方法:
def get_queryset(self):
queryset = super().get_queryset()
grade_id = self.request.GET.get('grade') # 获取班级
keyword = self.request.GET.get('search') # 获取搜索的内容
if grade_id:
queryset = queryset.filter(grade__pk=grade_id)
if keyword:
queryset = queryset.filter(
Q(phone_number=keyword) |
Q(teacher_name=keyword)
)
return queryset
def get_context_data(self):
context = super().get_context_data()
# 获取所有班级并添加到上下文
context['grades'] = Grade.objects.all().order_by('grade_number')
context['current_grade'] = self.request.GET.get('grade', '')
return context
上下页码搜索保存 80
添加一个文件包,url_utils.py
from django import template
from django.http import QueryDict
from urllib.parse import urlencode
register = template.Library()
@register.simple_tag
def search_url(request, **kwargs):
query_params = QueryDict(request.META['QUERY_STRING'], mutable=True)
for key, value in kwargs.items():
if value is None:
query_params.pop(key, None)
else:
query_params.setlist(key, [value])
return urlencode(query_params, doseq=True)
html
<!-- 分页导航 -->
<div class="pagination">
<span class="step-links">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?{% search_url request page=1 %}">« 首页</a>
<a href="?{% search_url request page=page_obj.previous_page_number %} ">上一页</a>
{% endif %}
<span class="current">
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?{% search_url request page=page_obj.next_page_number %}">下一页</a>
<a href="?{% search_url request page=page_obj.paginator.num_pages %}">尾页 »</a>
{% endif %}
</span>
登录界面81-88
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth import update_session_auth_hash
from .forms import LoginForm
from teachers.models import Teacher
from students.models import Student
# Create your views here.
def user_login(request):
# 判断是否是post请求
if request.method == 'POST':
# form表单验证
form = LoginForm(request.POST)
# 验证失败
if not form.is_valid():
return JsonResponse({'status':'error', 'messages': '提交信息有误!'}, status=400, safe=False)
# 验证成功
# 获取账号和密码的输入
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
# 确定角色
role = form.cleaned_data.get('role')
# 判断角色
if role == 'teacher':
try:
# 搜索获得老师的phone_number
teacher = Teacher.objects.get(phone_number=username)
username = teacher.teacher_name + '_' + teacher.phone_number
# 两者进行比较得到结果user
user = authenticate(username=username, password=password)
# teacher.user.username
except Teacher.DoesNotExist:
return JsonResponse({'status':'error', 'messages':'老师信息不存在'}, status=404)
elif role == 'student':
try:
student = Student.objects.get(student_number=username)
username = student.student_name + "_" + student.student_number
user = authenticate(username=username, password=password)
except:
return JsonResponse({'status':'error', 'messages':'学生信息不存在'}, status=404)
else:
try:
user = authenticate(username=username, password=password)
except:
return JsonResponse({'status':'error', 'messages': '管理员信息不存在'}, status=404)
# 验证成功,返回json数据
if user is not None:
# django自带的禁用和激活权限is_active,可以设置保存
if user.is_active:
# django自带的登录模版
login(request, user)
# 保存session信息
request.session['username'] = username.split('_')[0]
request.session['user_role'] = role
return JsonResponse({'status':'success','messages': '登录成功', 'role': role})
else:
return JsonResponse({'status':'error', 'messages': '账户已被禁用'}, status=403)
else:
# 处理登录失败的情况
return JsonResponse({'status':'error', 'messages': '用户名或密码错误'}, status=401)
return render(request, 'accounts/login.html')
def user_logout(request):
"""退出登录"""
# if 'user_role' in request.session:
# del request.session['user_role']
logout(request) # 执行退出
# 删除所有
request.session.flush()
return redirect('user_login')
def error_404_view(request, exception):
return render(request, '404.html', {}, status=404)
def change_password(request):
"""修改密码"""
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user)
return JsonResponse({
'status': 'success',
'messages': '您的密码已成功更新!'
})
else:
errors = form.errors.as_json()
return JsonResponse({
'status': 'error',
'messages': errors
})
return render(request, 'accounts/change_password.html')
form
from django import forms
from django.core.exceptions import ValidationError
ROLE_CHOICES = [
('admin', '管理员'),
('teacher', '老师'),
('student', '学生'),
]
class LoginForm(forms.Form):
username = forms.CharField(
max_length=50,
widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入手机号或学号'}),
)
password = forms.CharField(
max_length=50,
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'}),
)
role = forms.ChoiceField(label='角色', choices=ROLE_CHOICES)
def clean_username(self):
username = self.cleaned_data['username']
if len(username) == 0:
raise ValidationError('用户名长度不能为0。')
return username
def clean_password(self):
password = self.cleaned_data['password']
if len(password) == 0:
raise ValidationError('密码长度不能为0。')
return password
def clean_role(self):
role = self.cleaned_data['role']
if role not in ['admin','student', 'teacher']:
raise ValidationError('请选择一个用户身份')
return role