django是最近比较流行的一种基于Python的web框架,相比于比较有名的ssm好spring boot等java框架,我觉他它的操作更加简单实用。
MVC和MTV模式
著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器©,视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起。
模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器©接受用户的输入调用模型和视图完成用户的请求。
MVC是一种使用最广泛的设计模式,它使得各部分功能都能独立开发,做界面的可以专注于视图界面的开发,做后台的可以专注于业务逻辑开发,互不影响。
djang使用的MTV模式与MVC模式并无本质上区别,核心思想也是将视图和业务逻辑拆分解耦,使得各部分工作可以独立开发,互不影响。
MTV模式为M(模型)T(模板)V(视图)模式,模型负责与数据库交互,模板中定义了显示给用户的HTML界面,视图专注于负责业务逻辑,此外还有一个URL分发器,它的作用在于将不同的url请求分发给view中不同的方法处理。
django相关的命令
django-admin.py startproject project_name #新建Django项目
python manage.py startapp app_name #新建app
#创建数据库时需要执行两条命令
python manage.py makemigrations # 1. 创建更改的文件
python manage.py migrate # 2. 将生成的py文件应用到数据库
python manage.py runserver ip:port #启动服务
python manage.py flush #清空数据库
python manage.py #查询更多命令
基本常识介绍到这里,下面来看需求
功能需求
这个小例子很简单,主要为了实现下面3个需求:
- 用户登录与注册
- 用户登录成功后可以展示所有用户信息
- 如果没有检测到用户登录记录,则跳转到登录界面
为此数据库创建两张表就行了,一张登录信息表,用于保存用户名和密码,另一张用户信息表,用于保存用户基本信息(姓名,年龄,地址,性别)。
数据库使用MySQL,也可以用sqlite,使用是sqlite不需要专门安装数据库引擎,比MySQL方便不少。
页面的话有三个页面,分别是登录界面,注册界面和展示用户基本信息的界面。
这里先把完整的项目结构放上来,后面再慢慢解释每一部分的含义:
好了,首先要先创建一个django项目,使用命令django-admin.py startproject project_name
执行完命令后路径下就多了一个users文件夹,这就是我们创建好的django项目,简单吧!
然后我们使用pycharm打开,后面的操作都在pycharm中进行。
最初的项目是这样的:
manage是执行文件的入口,启动服务,数据库迁移等操作都需要执行这个脚本
urls.py就是之前所说的url分发器,看一下里面的代码:
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
初始状态只有一个默认的url
settings.py用于配置项目的基本配置,包括,数据库引擎,子项目,模板路径等等。主要用到的就是这两个脚本。
然后我们启动服务,看一下效果,在pycharm终端上使用命令python manage.py runserver
看到这句话,就表示服务启动成功了,现在可以通过127.0.0.0:8000访问项目,这里默认的端口号为8000,如果想改可以在命令后面加上表示端口的参数,如:
Python manage.py runserver 127.0.0.1:8080
这样就可以在8080端口访问了
通过urls.py中定义的url就可以访问到相应界面了。
下面来创建自己的app,使用命令Python manage.py startapp User
执行成功后,项目里面会多了一个User的目录:
主要用到的有models.py和views.py两个脚本,分别代表模型(M)和视图(V)。
那还有一个模板(T)跑哪去了?我们可以自己创建一个templates目录,用于保存相关的模板文件。注意需要在settings.py中配置模板路径。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'template')],
'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',
],
},
},
]
同时,也要将创建的项目加入进去,最后一个User就是刚刚创建的app:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'User'
]
如果不考虑数据库的话,User这个APP就已经能用了,接下来先尝试写个简单的views方法,不配置数据库。
在views.py中写下下面代码:
def index(request):
return render(request,'index.html')
request表示用户传回的request请求,return render(request,‘index.html’)这句返回一个HttpResponse对象,就是我们的index.html页面,这里需要导入两个模块
from django.shortcuts import render,HttpResponse
index.html这样写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>12345</h1>
</body>
</html>
然后需要在urls.py中分配url:
from django.contrib import admin
from django.urls import path
from User import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
这里的views.index就是我们在views,py中写好的业务逻辑
访问看看:
这就成功了。
所以总结一下,如果要增加新的url请求的话,至少需要更改三个地方:
- views.py增加业务逻辑
- template增加页面(当然这里也可以直接返回一个HTTPResponse对象,后面再讲)
- urls.py分配url
数据库创建及相关操作
接下来看数据库的配置
我们使用MySQL数据库,这需要先安装MySQL引擎,Python3中需要安装pymysql这个第三方库
pip install pymysql
然后在__init__.py中写入如下代码:
import pymysql
pymysql.install_as_MySQLdb()
简单说一下这里可能出现的错误
- NO module named ‘pymysql’
这种错误的原因有两个,第一个在你的项目目录下并没有安装pymysql,打开File->settings->project users->projext interpreter,查看安装的第三方库。
如果没有的话,在项目目录下重新安装一下就好了。
第二个是因为使用Python2的话会导致吧pymysql安装到Python2的sitepackge目录下,需要把Python2卸载掉。 - mysql django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required;
将site-packages目录下这个脚本的35和36行注释掉,报错解决。 - ‘str’ object has no attribute ‘decode’
将报错脚本中的decode改为encode,报错解决
以上三个报错是我在搭建过程中遇到的错误,花了不少时间才解决,这个MySQL真是挺坑的。
然后开始连接数据库,首先在settings.py中配置MySQL数据库连接:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'mydb', # 你要存储数据的库名,事先要创建之
'USER': 'root', # 数据库用户名
'PASSWORD': '1234', # 密码
'HOST': 'localhost', # 主机
'PORT': '3306', # 数据库使用的端口
}
}
然后在models.py中创建我们要创建的数据库表,这里直接创建对象,继承models.Model就好了,不需要去数据库中创建:
from django.db import models
# Create your models here.
class login_info(models.Model):
username = models.CharField(max_length=50)
password = models.CharField(max_length=50)
class user_info(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
addr = models.CharField(max_length=100)
sex = models.CharField(max_length=10)
这里的CharField表示数据库字段类型
然后执行这两条命令:
python manage.py makemigrations # 1. 创建更改的文件
python manage.py migrate # 2. 将生成的py文件应用到数据库
执行完之后,数据库中就会自动生成以上的表格,最后两张user开头的就是我们创建的表。
这里简单说一下django对数据库的增删改查操作,想要进一步了解的可以看官方文档
增:
s = models.tablename(name='123',age='34')
s.save()
s.save()一定不能丢,否则不会生效
相当于
INSERT INTO tablename SET name = '123', age= '34';
删:
Entry.objects.filter(pub_date__year=2005).delete() #delete from Entry where pub_data__year = '2005'
Entry.objects.all().delete() #这是有可能会坐牢的操作
改:
product = Product.objects.get(name='Venezuelan Beaver Cheese')
product.number_sold += 1
product.save()
#update Product set number_sold = number_sold+1 where name = 'Venezuelan Beaver Cheese'
查:
models.UserInfo.objects.all()
models.UserInfo.objects.all().values('user') #只取user列
models.UserInfo.objects.all().values_list('id','user') #取出id和user列,并生成一个列表
models.UserInfo.objects.get(id=1)
models.UserInfo.objects.get(user='yangmv')
接下来可以写业务逻辑,首先登陆:
views.py
def login(request):
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
if check_passwd(username,password):
global loginFlag
loginFlag = True
return redirect(show)
else:
html = '''
<p>请检查用户名密码是否正确<p>
<a href='/login/'>返回登录<a>
'''
return HttpResponse(html)
else:
return render(request,'login.html')
loginFlag 保存的是登录信息,是全局变量
check_passwd方法是这样的:
def check_passwd(username,password):
try:
result = login_info.objects.get(username=username)
if result.password == password:
return True
except:
return False
解释一下为啥要用try except,因为如果没有查找到username的话,脚本会抱一个异常,而不是返回None值,这会导致代码不能往下执行,所以这里用异常来处理。
template :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form name="Form1" action="/login/" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<a href="javascript:document.Form1.submit();">登录</a>
<a href="/register/">注册</a>
{% csrf_token %}
</form>
</body>
</html>
{% csrf_token %}这个不能丢,这是为了防止跨站攻击的,每个post请求最后都要加上。
urls.py:
path('login/', views.login),
然后注册:
views.py:
def register(request):
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
confirmpasswd = request.POST.get('confirmPwd',None)
age = request.POST.get('age',None)
address = request.POST.get('address',None)
sex = request.POST.get('sex',None)
if password == confirmpasswd:
if add_loginInfo(username,password):
add_userInfo(username,age,address,sex)
html = '''
<p>注册成功<p>
<a href='/login/'>返回登录<a>
'''
return HttpResponse(html)
else:
html = '''
<p>用户名已被注册,请重新输入用户名<p>
<a href='/register/'>返回注册<a>
'''
return HttpResponse(html)
else:
html = '''
<p>密码不一致,请重新输入<p>
<a href='/register/'>返回注册<a>
'''
return HttpResponse(html)
else:
return render(request,'register.html')
add_loginInfo和add_userInfo:
def add_loginInfo(username,password):
try:
result = login_info.objects.get(username=username)
return False
except:
pass
t = login_info(username=username, password=password)
t.save()
return True
def add_userInfo(name,age,addr,sex):
u = user_info(name=name,age=age,addr=addr,sex=sex)
u.save()
template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Register</title>
<style type="text/css">
.s
{
display: block;
float: left;
width: 150px;
height: 20px;
}
</style>
</head>
<body>
<form action="/register/" method="post" name="form2">
<span class="s">username:</span><input type="text" name="username"><br>
<span class="s">password:</span><input type="password" name="password"><br>
<span class="s">confirmPassword:</span><input type="password" name="confirmPwd"><br>
<br><br>
<span class="s">age:</span><input type="text" name="age"><br>
<span class="s">address:</span><input type="text" name="address"><br>
<span class="s">sex:</span><input type="radio" name="sex" value="male">男
<input type="radio" name="sex" value="female">女<br>
<input type="submit" name="提交" value="注册">
{% csrf_token %}
</form>
</body>
</html>
path('register/',views.register),
最后展示用户信息:
views.py:
def show(request):
if not loginFlag:
return redirect(login)
data = {}
UserInfo = user_info.objects.all()
data['UserInfo'] = UserInfo
return render(request,'show.html',context=data)
这里的redirect是一种重定向的方法,如果没有登录就返回登录界面先登录,否则不能查看用户信息。
templates:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Show</title>
</head>
<body>
<ul>
{% for item in UserInfo %}
<li>
<span>{{ item.name }}</span>
<span>{{ item.age }}</span>
<span>{{ item.addr }}</span>
<span>{{ item.sex }}</span>
</li>
{% endfor %}
</ul>
</body>
</html>
这里解释一下for标签的使用,可以看到在show方法中传入了一个context=data参数,这个参数传入的是一个字典对象,用于展示在页面上的数据。这里将从数据库获取到的userInfo数据传入页面中,然后页面中可以用for标签展示,注意这里最好严格按照我的写法:
{% for item in UserInfo %}
{% endfor %}
不要随便的增加或者减少空格,否则会有意想不到的错误发生。
path('show/',views.show)
现在功能基本完成了,来看一下效果:
登录界面
用户名密码输错的情况
输入成功后显示用户信息
注册界面
密码不一致
用户名被注册
注册成功
多了一条信息,就是刚刚注册的那个
如果不登录,直接输入http://127.0.0.1:8000/show/的话,会跳转到登录界面。
这个小例子还有许多可以优化的地方,比如增加退出登录功能,密码保存使用加密算法加密等等,有人如果有兴趣可以当做练习,本人对前端不太了解,所以页面几乎没有css样式,后面如果有时间再做优化。