0%

django-admin

安装

  • 安装django
1
E:\proj>python -m pip install Django
  • 使用django-admin 创建项目
1
django-admin startproject StudyDjangoAdmin
  • 查看路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    E:\proj\StudyDjangoAdmin>tree /f
    卷 软件盘 的文件夹 PATH 列表
    卷序列号为 240F-1787
    E:.
    │ manage.py

    └─StudyDjangoAdmin
    asgi.py
    settings.py
    urls.py
    wsgi.py
    __init__.py
    • 在我们创建了Django的项目后,我们在最原始的urls.py中就可以看见关于admin的路径:

image-20250909090838252

  • 启动服务
1
python manage.py runserver 0.0.0.0:8000
  • 如果要访问admin后台我们只需要输入以下网址:
1
http://127.0.0.1:8000/admin/

image-20250909091046888

数据库选择

  • 我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// E:\proj\StudyDjangoAdmin\StudyDjangoAdmin\settings.py
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'runoob1', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}
  • 安装mysql驱动
1
E:\proj\StudyDjango>pip3 install pymysql
  • 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
1
2
3
4
// E:\proj\StudyDjangoAdmin\StudyDjangoAdmin\__init__.py

import pymysql
pymysql.install
  • 默认使用的sqlite,本次选择使用mysql,(win7 mysql安装详解 )

  • 使用heidisql连接本地mysql数据库后,新建一个数据库,名称为:runoob1

  • 生成表结构

1
E:\proj\StudyDjangoAdmin>python manage.py migrate
  • 如果报错,MySQLclient 目前只支持到 Python3.4,因此如果使用的更高版本的 python,把D:\app\Python37\Lib\site-packages\django\db\backends\mysql\base.py 对应报错代码注释

  • 迁移知识点:迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据,改变模型需要这三步:

image-20250909091526544

  • 创建超级用户
1
2
3
4
5
6
执行:
python manage.py createsuperuser

-- 提示输入创建的账号: admin
-- 邮箱:可直接回车不输
-- 密码、输两次

image-20250909093243315

修改为中文的界面

找到settings.py里的LANGUAGE_CODE 和TIME_ZONE修改:

1
2
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai

新增用户

  • 在admin后台新增用户,确保人员状态正确

image-20250911085956081

创建APP

  • Django 规定,如果要使用ORM模型,必须要创建一个 app
1
django-admin startapp TestModel

注册app

  • 接下来在 settings.py 中找到INSTALLED_APPS这一项,加入这个app
1
2
3
4
5
6
7
8
9
10
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'TestModel', # 添加此项

]

模型

  • 我们修改 TestModel/models.py 文件
1
2
3
4
5
6
7
8
from django.db import models


# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
  • 生成数据表
1
2
3
4
# 让 Django 知道我们在我们的模型有一些变更
python manage.py makemigrations TestModel
# 创建表结构
python manage.py migrate TestModel

image-20250911092703957

应用实例

  • 书籍的增删改查

  • TestModel/view

1
2
3
4
5
6
7
8
9

from django.http import HttpResponse
from django.template import loader
from TestModel import models
def book_list(request):
latest_book_list = models.Book.objects.order_by("-pub_date")[:5]
template = loader.get_template("index.html")
context = {"latest_book_list": latest_book_list}
return HttpResponse(template.render(context, request))
  • html模板文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if latest_book_list %}
<ul>
{% for book in latest_book_list %}
<li><a href="/book/{{ book.id }}/">{{ book.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No book are available.</p>
{% endif %}
</body>
</html>
  • 要为 TestModel 应用定义一个 URLconf,创建一个名为 TestModel/urls.py 的文件,并包含以下内容:
1
2
3
4
urlpatterns = [
path("book/", views.book_list, name="book_list"),

]
  • 把TestModel应用的url引入
1
2
3
4
5
6
7
8
9
10
# StudyDjangoAdmin/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('', include('TestModel.urls')),

]

  • 手动在book表中插入一些数据后,然后访问book

image-20250914155359641

登录限制

  • TestModel/view 加入登录检验
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
from django.contrib.auth.decorators import login_required


def login(request):
return render(request, "login.html")


def login_action(request):
if request.method == 'POST':
username = request.POST.get("username", "")
password = request.POST.get("password", "")
#接受2个参数,用户名 密码,在用户名密码正确的情况下返回一个user对象。如果不正确,返回None
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user) # 登录
request.session['user'] = username # 将session信息记录到浏览器
response = HttpResponseRedirect('/event_manage/')
return response
else:
return render(request, "login.html", {'error': '用户名或者密码错误'})

@login_required
def logout_func(request):
auth.logout(request)
return HttpResponse("退出登录成功,<a href='/login'>重新登录</a>")

@login_required
def event_manage(request):
return render(request, 'event_manage.html')

@login_required
def book_list(request):
latest_book_list = models.Book.objects.order_by("-pub_date")[:5]
template = loader.get_template("index.html")
context = {"latest_book_list": latest_book_list}
return HttpResponse(template.render(context, request))

  • event_manage.html文件
1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html>
<head><title>登录管理</title></head>
<body>
<h1>登录成功</h1>
<p>进入到<a style="font-size:18px" href="/TestModel/book">主页</a><strong</p>
</body>
</html>
  • login.html
1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head><title>Django Page</title></head>
<body><h1>登录</h1>
<form method="POST" action="/login_action/">
<input name="username" type="text" placeholder="username"><br>
<input name="password" type="password" placeholder="password"><br>
{{ error }} <br/>
<button id="btn" type="submit">登录</button>
{% csrf_token %}
</form>
</body>
</html>
  • 书籍主页的html,加入了一个退出登录
1
2
3
<p><a href="/logout">退出登录</a></p>
{% if latest_book_list %}
...
  • 当没有登录的情况下,访问书籍主页,直接就调整到了登录界面

image-20250914141655730

  • 这里登录限制实现后,有个不灵活的地方,就是每次都要手动加上@login_required,之前文章里面实现过,可以参考这里

新增

  • 主页的index.html加入了新增
1
2
3
<div>
<a href="{% url 'event_add' %}">新增</a>
</div>
  • 应用中加入了两个add路由
1
2
3
4
5
6
7
8
urlpatterns = [
path("book/", views.book_list, name="book_list"),
# 新增界面
path("event_add/", views.event_add, name="event_add"),
# 提交新增数据
path("book_add/", views.book_add, name="book_add"),

]
  • view代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@login_required
def event_add(request):
return render(request, 'add.html')

@login_required
def book_add(request):
if request.method == 'POST':
title = request.POST.get("title", "")
price = request.POST.get("price", "")
d_price = float(price)
pub_date = datetime.now().strftime("%Y-%m-%d")
book = models.Book(title=title, price=d_price, pub_date=pub_date)
book.save()
# 保存成功后,调整到首页
return redirect('/book/')

image-20250914175243952

image-20250914175312086

编辑

  • index.html,加入了编辑
1
2
3
4
5
6
7
8
9
{% if latest_book_list %}
<ul>
{% for book in latest_book_list %}
<li>{{book.id}}--<a href="#">{{book.title}}</a>--<a href="{% url 'book_edit' book.id%}">编辑</a></li>
{% endfor %}
</ul>
{% else %}
<p>No book are available.</p>
{% endif %}
  • view代码。如果是get请求,就读取数据并且跳转到edit.html,然后是post请求直接就修改数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@login_required
def book_edit(request, book_id):
if request.method == 'POST':
title = request.POST.get("title", "")
price = request.POST.get("price", "")
d_price = float(price)
pub_date = datetime.now().strftime("%Y-%m-%d")
book = Book.objects.get(pk=book_id)
book.price = d_price
book.pub_date = pub_date
book.title = title
book.save()
return redirect('book_list')
else:
# 处理GET请求,显示编辑表单
book = get_object_or_404(Book, pk=book_id)
return render(request, 'edit.html', {'book': book})
  • url修改
1
2
path("book_edit/<int:book_id>/", views.book_edit, name="book_edit"),

image-20250914195750503

明细页面

  • index.html修改
1
2
3
4
5
6
7
8
9
{% if latest_book_list %}
<ul>
{% for book in latest_book_list %}
<li>{{book.id}}--<a href="{% url 'book_detail' book.id%}">{{book.title}}</a>--<a href="{% url 'book_edit' book.id%}">编辑</a></li>
{% endfor %}
</ul>
{% else %}
<p>No book are available.</p>
{% endif %}
  • view.py
1
2
3
4
5
6
@login_required
def book_detail(request, book_id):
book = get_object_or_404(Book, pk=book_id)
return render(request, 'detail.html', {
'book': book
})
  • url
1
path("book_detail/<int:book_id>/", views.book_detail, name="book_detail"),
  • detail.html明细页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>详情页面</title>
</head>
<body>
<div>
<p>id:{{ book.id }}</p>
<p>标题:{{ book.title }}</p>
<p>价格:{{ book.price }}</p>
<p>日期:{{ book.pub_date }}</p>
</div>
<a href="{% url 'book_list' %}">Back to List</a>

</body>
</html>

删除

  • index.html 加入了删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% if latest_book_list %}
<ul>
{% for book in latest_book_list %}
<li>{{book.id}}--<a href="{% url 'book_detail' book.id%}">{{book.title}}</a>--<a href="{% url 'book_edit' book.id%}">编辑</a>
<form action="{% url 'book_delete' book.id %}" method="post" style="display:inline;">
{% csrf_token %}
<button type="submit" onclick="return confirm('确定要删除这本书吗?')">删除</button>
</form>
</li>
{% endfor %}
</ul>
{% else %}
<p>No book are available.</p>
{% endif %}
  • view加入删除
1
2
3
4
5
6
@login_required
def book_delete(request,book_id):
if request.method == 'POST':
book = get_object_or_404(Book, pk=book_id)
book.delete()
return redirect('book_list')
  • url
1
path("book_delete/<int:book_id>/", views.book_delete, name="book_delete"),

查询

  • 书籍列表index.html加入搜索
1
2
3
4
5
6
7
8
9
10
11
12
<p><a href="/logout">退出登录</a></p>

<form class="navbar-form" method="get" action="{% url 'book_list' %}">
<div class="form-group">
<input type="text" name="title" value="{{ search_term }}">
</div>
<button type="submit" class="btn btn-success">
搜索
</button>
</form>
{% if latest_book_list %}
....
  • view代码修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@login_required
def book_list(request):
title = request.GET.get("title", "")

if title:
books = Book.objects.filter(
title__contains=title
).order_by("-pub_date")[:5]
else:
books = Book.objects.order_by("-pub_date")[:5]

return render(request, 'index.html', {
'latest_book_list': books,
'search_term': title
})

分页

  • view的book_list加入分页,有自带的Paginator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

@login_required
def book_list(request):
title = request.GET.get("title", "")

if title:
books = Book.objects.filter(
title__contains=title
).order_by("-pub_date")
else:
books = Book.objects.order_by("-pub_date")

# Set up pagination
paginator = Paginator(books, 5) # Show 5 books per page.
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
logger.debug(f"Page number: {page_number}, Page obj: {page_obj}")
return render(request, 'index.html', {
'latest_book_list': page_obj,
'search_term': title
})
  • index.html加入分页样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
!-- Pagination controls -->
<div class="pagination">
<span class="step-links">
{% if latest_book_list.has_previous %}
<a href="?page=1&title={{ search_term }}">« 首页</a>
<a href="?page={{ page_obj.previous_page_number }}&title={{ search_term }}">上一页</a>
{% endif %}

<span class="current">
第 {{ latest_book_list.number }} 页 / 共 {{ latest_book_list.paginator.num_pages }} 页
</span>

{% if latest_book_list.has_next %}
<a href="?page={{ latest_book_list.next_page_number }}&title={{ search_term }}">下一页</a>
<a href="?page={{ latest_book_list.paginator.num_pages }}&title={{ search_term }}">末页 »</a>
{% endif %}
</span>
</div>
  • 查看结果

image-20250914231917746

退出登录

  • index.html
1
2
<p><a href="/logout">退出登录</a></p>

  • 在根目录的url配置
1
2
url(r'^logout/', views.logout_func),

  • 在应用的view中
1
2
3
4
5
@login_required
def logout_func(request):
auth.logout(request)
return HttpResponse("退出登录成功,<a href='/login'>重新登录</a>")