安装
1
| E:\proj>python -m pip install Django
|
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的路径:

1
| python manage.py runserver 0.0.0.0:8000
|
1
| http://127.0.0.1:8000/admin/
|

数据库选择
- 我们在项目的 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', # 数据库密码 } }
|
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
|
1
| E:\proj\StudyDjangoAdmin>python manage.py migrate
|
如果报错,MySQLclient 目前只支持到 Python3.4,因此如果使用的更高版本的 python,把D:\app\Python37\Lib\site-packages\django\db\backends\mysql\base.py
对应报错代码注释
迁移知识点:迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据,改变模型需要这三步:

1 2 3 4 5 6
| 执行: python manage.py createsuperuser -- 提示输入创建的账号: admin -- 邮箱:可直接回车不输 -- 密码、输两次
|

修改为中文的界面
找到settings.py里的LANGUAGE_CODE 和TIME_ZONE修改:
1 2
| LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai
|
新增用户

创建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
|

应用实例
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))
|
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"),
]
|
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

登录限制
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))
|
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>
|
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>
|
1 2 3
| <p><a href="/logout">退出登录</a></p> {% if latest_book_list %} ...
|
- 当没有登录的情况下,访问书籍主页,直接就调整到了登录界面

- 这里登录限制实现后,有个不灵活的地方,就是每次都要手动加上@login_required,之前文章里面实现过,可以参考这里
新增
1 2 3
| <div> <a href="{% url 'event_add' %}">新增</a> </div>
|
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"),
]
|
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/')
|


编辑
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})
|
1 2
| path("book_edit/<int:book_id>/", views.book_edit, name="book_edit"),
|

明细页面
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 %}
|
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 })
|
1
| path("book_detail/<int:book_id>/", views.book_detail, name="book_detail"),
|
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>
|
删除
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 %}
|
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')
|
1
| path("book_delete/<int:book_id>/", views.book_delete, name="book_delete"),
|
查询
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 %} ....
|
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 })
|
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>
|

退出登录
1 2
| <p><a href="/logout">退出登录</a></p>
|
1 2
| url(r'^logout/', views.logout_func),
|
1 2 3 4 5
| @login_required def logout_func(request): auth.logout(request) return HttpResponse("退出登录成功,<a href='/login'>重新登录</a>")
|