一、路由系统 URL

1.模板语言循环字典

1.简单的字典循环


<ul>
{% for i in user_dict %}
<li>{{ i }}</li>
{% endfor %}
</ul> 获取字典中的key <ul>
{% for i in user_dict.keys %}
<li>{{ i }}</li>
{% endfor %}
</ul> 获取字典中的key,通过keys关键字,不要括号 <ul>
{% for i in user_dict.values %}
<li>{{ i }}</li>
{% endfor %}
</ul> 获取字典中的value,通过values关键字,不要括号 <ul>
{% for i in user_dict.items %}
<li>{{ i }}</li>
{% endfor %}
</ul> 获取字典中的key和value,得到的是个元组 <ul>
{% for i,j in user_dict.items %}
<li>{{ i }}---{{ j }}</li>
{% endfor %}
</ul>
分别获取字典中的key,value,

                                                                                                                                          

{% for i in user_dict %}      {% for i in user_dict.keys %}     {% for i in user_dict.values %}          {% for i in user_dict.items %}         {% for i,j in user_dict.items %}

循环字典,和python里是差不多的,就是后面没有括号():

  • 直接dict :循环的是key
  • dict.keys :循环key
  • dict.values :循环values
  • dict.items :循环key和values

2.一条对应关系对应多个页面

这里通过嵌套字典来实现

场景:一个页面会显示很多用户的某个信息,然后我们点击查看详情,会出来用户的多条信息,但是这些用户信息不是在同一个页面上,例如我们看博客园里面的博客,每次查看如,

https://www.cnblogs.com/mlq2017/p/10054661.html        /10054661这个值,换一篇博客,这个nid就会变,

1.通过GET方式实现:

#index.html

<body>
<ul>
{% for i,j in user_dict.items %}
<li><a target="_blank" href="/detail/?nid={{ i }}">{{ j.name}}</a></li>
{% endfor %} </ul>
</body> #detail,html <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息</h1>
<h3>用户名:{{ user_info.name }}</h3>
<h3>年龄:{{ user_info.age }}</h3>
<h3>邮箱:{{ user_info.email }}</h3>
</body>
</html> #urls.py path('index/', views.index),
path('detail/', views.detail), #wiews.py
from django.shortcuts import render
USER_DICT = {
"":{"name":"root1","age":"","email":"123@163.com"},
"":{"name":"root2","age":"","email":"223@163.com"},
"":{"name":"root3","age":"","email":"323@163.com"},
"":{"name":"root4","age":"","email":"423@163.com"},
} def detail(request): #通过GET,html里面用 /?+{{key}}
a = request.GET.get("nid")
user_info = USER_DICT[a]
print(user_info)
return render(request,"detail.html",{"user_info":user_info}) def index(request): return render(request,"index.html",{"user_dict":USER_DICT})

这里用到了如下方法:

1.html里面使用:href="/detail/?nid={{ i }}"   通过?加上字典的key跳转链接

2.views里面的detail里面通过GET获取nid:  然后通过nid获取字典的值,将值传给rengder(),再后在页面获取每个值中的信息

2.通过正则方式实现:

上面的HTML代码只有一个地方需要修改,做成动态匹配

{% for i,j in user_dict.items %}
<li><a target="_blank" href="/detail-{{ i }}.html">{{ j.name }}</a></li>
{% endfor %}

相应的urls.py里面用到正则匹配

from django.contrib import admin
from django.urls import path,re_path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
re_path('detail-(\d+).html', views.detail), ]

既然我们用到正则,那这里就要导入正则,使用正则匹配

from django.urls import re_path
下面的匹配为:
re_path('detail-(\d+).html', views.detail),
这里要注意,之前的视频是很老的,方法不一样,按照视频上说的做,会一直报错

然后是views.py里面的修改了

def detail(request,nid):  #正则匹配
#return HttpResponse(nid)
user_info = USER_DICT[nid]
print(nid)
return render(request,"detail.html",{"user_info":user_info})

这里我们需要在detail里面再给一个形参,接收传过来的动态参数:这里返回的是字典的key,

然后通过nid直接获取字典的第一层值

建议使用正则的时候用下面的方法

re_path('detail-(?P<nid>\d+).html', views.detail),

将形参进行绑定,就不用考虑传入的位置

1.当html后面接很多-(\d+)时,如,re_path('detail-(\d+)-(\d+).html', views.detail),在detail里面传多个形式参数的时候def detail(request,nid,uid): 位置就要一一对应,

      如果用上面的方式,我们就可以任意写形参的位置

2.当我们写正则的时候绑定了多个参数,在detail里面传多个形式参数的时候def detail(request,nid,uid,……):,我们就要穿多个参数,对于这样的,我们可以使用

def detail(request,*args,**kwargs):

这样,我们通过re_path('detail-(\d+)-(\d+).html', views.detail) 这样的就会把值传给*args

   我们通过re_path('detail-(?P<nid>\d+).html', views.detail),这样的就会吧值传给**kwargs

总结实战:

a.

  re_path('detail-(\d+)-(\d+).html', views.detail),

  def func(request,nid,uid):

  def func(request,*args):

  args = (2,9)

  def func(request,*args,**kwargs):

b.

  re_path('detail-(?P<nid>\d+)-(?P<nid>\d+).html', views.detail),

  def func(request,nid,uid):

  def func(request,*args,**kwargs):

  *args为空,**kwargs为字典接受上面的值

三.关于url中的name

1.当频繁修改url,就要修改urls.py,又要修改html页面,

  我们可以在urls里面给url进行绑定,之后无论怎么修改,只需要修改url里面的值就行

在绑定是添加name进行绑定
path('index/', views.index,name = "indexx"), 在detail.html里面,提交表单时,这么改
<form action="{% url "indexx" %}">
<input type="text" />
</form>

2.当我们还要在绑定的url后面继续使用匹配,如:path('index/(\d+)/', views.index,name = "indexx"),

  此时我们无法通过action="{% url "indexx" %}" 进行提交,可以用

  1.跳转到固定的页面

<form action="{% url "indexx" 11 %}">

  后面这个11,表示只能跳转到这个页面,并且这种模板语言,后面只能写固定值

  2.跳转到当前页面

    这里要用到request中的path_info,获取当前页面的url

在detail函数里面,我们试着打印request.path_info

print(request.path_info) # 输出结果为:/detail-1.html

  distail里面不需要改,因此我们可以在action里面这么写

<form action="{{ request.path_info }}">

用 path() 方法实现捕获参数

课上讲的是旧版本,现在Django已经2.0了,url()方法被path()方法替代,用法也有区别。
re_path() 可以看做是2.0里向下兼容的一个方法,也就是旧的1.0的 url() 方法。在2.0里用 path() 方法也可以实现捕获组的对应关系。使用 path() 方法需要注意:

    1. 要捕获一段url中的值,需要使用尖括号,而不是之前的圆括号;
    2. 可以转换捕获到的值为指定类型,比如int。默认情况下,捕获到的结果保存为字符串类型,不包含 '/' 这个特殊字符;
    3. 匹配模式的最开头不需要添加 '/' ,因为默认情况下,每个url都带一个最前面的 '/' ,既然大家都有的部分,就不用浪费时间特别写一个了。
 path('detail2-<int:nid>-<int:uid>.html', views.detail2),

上面的例子,就是捕获一个0或正整数,并且返回一个int类型,再用冒号把命名也完成了。除了int,还有下面这些。
默认情况下,Django内置下面的路径转换器:

    • str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的;
    • int:匹配0和正整数,返回一个int类型;
    • slug:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如’ building-your-1st-django-site‘ ;
    • uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象;
    • path:匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。

小结

上面各种实现的方法由浅入深,并且一个比一个好,推荐用最后面的实现方式:

  • 基于正则的url比使用get方式获取参数的好
  • 命名捕获组比普通捕获组好
  • 推荐还是用最后的 path() 方法来实现,如果是1.x的版本,那么就是推荐基于正则的命名捕获组的方法。

另外,在定义函数的时候也可以写成这种万能的模式: def detail2(request, *args, **kwargs): ,这样的话,要使用 args[0] (普通捕获组)或 kwargs['nid'] (命名捕获组)来取值。

路由分发

之前所有的url对应关系都写在了项目名下的urls里面,当我们的项目有多个app的时候,所有的页面都写在一起也不好。应该是每个app各自管理自己的那部分url。这就需要路由分发。

1.首先我们在项目名下的urls里面导入include,

from django.contrib import admin
from django.urls import path,re_path
from django.urls import include #导入include urlpatterns = [
path('admin/', admin.site.urls),
path('app/',include('app.urls')) #添加app的绑定
]

    如果有多个app,就继续在下面添加绑定  path('app1/',include('app1.urls')) 这样的

2.然后在app里面创建一个同样的urls.py

3.导入views,py

from django.urls import path,re_path
from app import views
urlpatterns = [
path('index/', views.index),
]

默认值

命名空间

二、视图

提交数据不仅有POST和GET,还有其他的提交方法,比如:PUT、DELETE、HEAD、OPTION

form表单提交数据

1,request.POST.get()

2,request.GET.get()

3,request.POST.getlist()

4,request.FILES.get()

先上代码:

#login。html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="用户名">
</p>
<p>
<input type="password" name="pwd" placeholder="密码">
</p>
<p>
男:<input type="radio" name="gender" value="">
女:<input type="radio" name="gender" value="">
人妖:<input type="radio" name="gender" value="">
</p>
<p>
男:<input type="checkbox" name="favl" value="">
女:<input type="checkbox" name="favl" value="">
人妖:<input type="checkbox" name="favl" value="">
</p>
<p>
<select name="city" multiple="multiple">
<option value="sz">深圳</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</p>
<p>
<input name="file" type="file">
</p>
<p>
<input type="submit" value="提交">
<span style="color: red">{{ error_msg }}</span>
</p> </form>
</body>
</html> views.py from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request):
if request.method == "POST":
obj = request.POST.get("gender")
print("obj",obj) #输出为1
obj1 = request.POST.get("favl")
obj2 = request.POST.getlist("favl")
print("obj1",obj1) #这里checkbox是多选框,但是只获取到一个值,实际要获取多个,这里不能用get
print("obj2", obj2) #obj2 ['11', '33'] 这里获取到了每个选中的值
obj3 = request.POST.getlist("city")
print("obj3", obj3) #obj3 ['sz', 'bj', 'sh']
obj4 = request.POST.get("file") #obj4 None <class 'NoneType'> 这里没获取到
print("obj4", obj4,type(obj4))
obj5 = request.FILES.get("file")
print("obj5", obj5, type(obj5)) #这里获取到的是文件名和文件,
#from django.core.files.uploadedfile import InMemoryUploadedFile
f = open(obj5.name,"wb")
for data in obj5.chunks():
f.write(data)
f.close()
elif request.method == "GET":
pass
else:
return HttpResponse("index") return render(request,"login.html",) #error_msg = ""
# def login(request):
# if request.method == "POST":
# u = request.POST.get("user")
# p = request.POST.get("pwd")
# if u == 'aaa' and p == '123':
# return HttpResponse("home")
# else:
# error_msg = "用户名或密码错误"
# return render(request,"login.html",{"error_msg":error_msg})
# elif request.method == "GET":
# pass
# else:
# return HttpResponse("index")
#
# return render(request,"login.html",) #urls.py from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
]

注释部分第一节已经写过,都是通过get获取

对于从网页只获取一个值的我们可以用get(text,password,radio),

obj = request.POST.get("gender")
print("obj",obj) #输出为1

但是如果是多选,就需要用getlist(checkbox,select),

 obj1 = request.POST.get("favl")
obj2 = request.POST.getlist("favl")
print("obj1",obj1) #这里checkbox是多选框,但是只获取到一个值,实际要获取多个,这里不能用get
print("obj2", obj2) #obj2 ['11', '33'] 这里获取到了每个选中的值
obj3 = request.POST.getlist("city")
print("obj3", obj3) #obj3 ['sz', 'bj', 'sh']

普通表单无法上传文件,要上传文件,还需要在form标签中加  enctype="multipart/form-data"

另文件我们需要用request.FILES.get(),这样获取到的不是文件的名称,而是一个类,既能获取到文件名,又能获取到文件,文件名通过obj5.name获取,文件内容用 obj5.chunks()

obj4 = request.POST.get("file")  #obj4 None <class 'NoneType'> 这里没获取到
print("obj4", obj4,type(obj4))
obj5 = request.FILES.get("file")
print("obj5", obj5, type(obj5)) #obj5 作业.png <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>这里获取到的是文件名和文件,
#from django.core.files.uploadedfile import InMemoryUploadedFile
f = open(obj5.name,"wb")
for data in obj5.chunks():
f.write(data)
f.close()

CBV 和 FBV

  • FBV(function base views) 就是在视图里使用函数处理请求。
  • CBV(class base views) 就是在视图里使用类处理请求。

到目前为止,所有的处理都是写在一个函数里的。Django还提供另外一个方式,我们也可以通过类来处理。

之前用FBV,这里也可以把之前的函数改成类处理请求,

#home.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/home/" method="post">
<input type="text" name="user" />
<input type="submit" value="提交" />
</form>
</body>
</html> #views.py from django.shortcuts import render,redirect,HttpResponse #FBV和CBV
from django.views import View class Home(View): def get(self,request): #当输入http://127.0.0.1:8000/home/地址时,是get请求,因此下面的打印的结果为GET
print(request.method)
return render(request,"home.html") def post(self,request):
print(request.method) #当输入内容时,是post请求,执行了这里,打印结果为POST
return render(request,"home.html") #urls.py from app import views
urlpatterns = [
path('admin/', admin.site.urls),
#path('login/', views.login), 提交表单的
path('home/', views.Home.as_view()), ]

以上代码执行和之前使用FBV效果是一样的,我们在类里写上对应的提交方式,当执行是,会自动到类里面去找对应的函数。

CBV的写法为

1.先导入View,在视图里面创建类

from django.views import View

class Home(View):

2.修改urls里面,这里添加url对应关系和FBV不同,应此需要特别注意:中间写上类名,后面固定跟一个 .as_view()

3.在类中定义相应的方法,

先去看看继承的View类里有什么,在源码的base.py这个文件里。首先里面定义了一个公有属性:

点击View会看到

http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

所以除了GET,POST,还可以处理这么多的请求方法,用起来,在类里照着别的一样定义一个同名方法就可以了。

3.在类中可以引入父类,就是Django内部定义的父类,这样我们既能定义自己的类,还可以继承父类,修改父类(装饰器)

继续看源码的View,调用了as_view()方法里面会再调用一个dispatch()方法。这个dispatch()方法里是通过映射获取我们的 request.method 即提交的方法来调用我们的处理方法的。dispatch()的源码如下:

    def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

结论就是,根据不同的请求类型提交到不同的处理方法,是用过dispatch()方法里通过映射来实现的。先执行dispatch()方法然后再调用对应的提交类型的处理方法。

class Home(View):

    def dispatch(self, request, *args, **kwargs):
#调用父类中的dispatch
result = super(Home,self).dispatch(request, *args, **kwargs)
return result def get(self,request): #当输入http://127.0.0.1:8000/home/地址时,是get请求,因此下面的打印的结果为GET
print(request.method)
return render(request,"home.html") def post(self,request):
print(request.method) #当输入内容时,是post请求,执行了这里,打印结果为POST
return render(request,"home.html")

在类中添加dispatch()方法后,执行效果和不添加它的效果是一样的

所以通过继承和重构dispatch()方法,可以在处理方法执行前和执行后自定义一些操作。如果需要的话就在我们的类里继承并重构,

    def dispatch(self, request, *args, **kwargs):
#调用父类中的dispatch
print("before") //处理前执行的操作
result = super(Home,self).dispatch(request, *args, **kwargs)
print("after") //处理后执行的操作
return result

上面看了View代码,我们看到了,当提交方式都不匹配的时候是执行下面的代码,页面是返回405

    def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)

这里我们就可以重构自己的方法了:

def http_method_not_allowed(self, request, *args, **kwargs):
return redirect('/home/')

给views.py分类

默认所有的处理函数都是写在views.py这个文件里的。如果处理函数很多,全部写在一个文件里也会很乱。这是可以考虑创建一个views包来替代原来的views.py文件。然后在views包里创建多个py文件来写我们的处理函数。比如:

  • views/account.py 是用户相关的操作,登录认证之类的
  • views/test.py 是用来测试的处理函数
  • views/order.py 订单相关的操作

三、模板



 

四、ORM操作

django数据库错误相关问题

连接sqlite数据库

默认使用的是sqlite3作为数据库,使用数据库需要一下步骤
一、创建你的数据库表结构

在app01里面的models.py添加类

from django.db import models
# Create your models here.
class Userinfo(models.Model):
username = models.CharField(max_length=32);
password = models.CharField(max_length=64);

上面的类等到去数据库创建表的时候,表名是 “app01_userinfo” ,也就是 [app名]_[类名] 。

二、设置settings.py文件
在 INSTALLED_APPS 注册你的app,把你的app追加到这个列表里:

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.AppConfig',
]

注:如果是在pycharm里面创建项目的时候创建的app,默认是已经加上了的;通过命令添加的,需要上面的操作:'app02.apps.AppConfig',

配置数据库连接,默认已经配置好了一个sqlite3,所以不需要修改:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

三、去终端执行2条命令

python manage.py makemigrations
python manage.py migrate

第一条命令会在 app 目录下的 migrations 目录下创建一个文件(0001_initial.py),记录我们对 models.py 所做的变动。
第二条命令是真正去操作数据库了,除了创建我们自己写的表以外,还创建了很多 django 自己的表。
上面两条命令都是作用于全局的,如果要限定作用于只在某个app,可以在最后加上app的名称:

python manage.py makemigrations app01
python manage.py migrate app01

关于SQLite:

SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。
Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。

使用SQLite

安装Navicat Premium 可以连接上SQLite

1.在pycharm里面右键点击SQLite,然后点击 copy path

2.打开Navicat Premium  ,点击连接,选择SQlite,将C:\Users\ZYP\PycharmProjects\day19\db.sqlite3  复制到文件路径里面,给连接取个名字。用户名和密码不用填,点击连接

打开后,效果如下面截图

连接mysql

在settings里面设置如下:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
1
2
3
4
5
6 # 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替 # 如下设置放置的与project同名的配置的 __init__.py文件中 import pymysql
pymysql.install_as_MySQLdb() 

注:上面的数据库名,使用mysql时,需要自己手动创建

数据的增删改查

ORM操作

先添加如下代码:

#在APP01里面的urls.py里面

path("orm/", views.orm),

#views.py
from app01 import models def orm(request): return HttpResponse("orm")

既然要操作数据库,这里肯定要导入models模块,表就用之前讲的userinfo

1.1添加数据,网页上输入http://127.0.0.1:8000/app01/orm/后,使用Navicat Premium 看下表里面的内容,此时已经添加了三条数据

    #添加数据
for row in list:
    list = [
    {"username": "root", "password": "admin"},
    {"username": "admin", "password": "admin"},
    {"username": "user", "password": "123456"},
      ]
models.Userinfo.objects.create(username=row['username'],password=row["password"])
return HttpResponse("ORM")

1.2添加数据

    # 第二种方法
obj = models.Userinfo(username="root",password="")
obj.save()

1.3添加数据

    # 第三种方法
dic = {"username": "root", "password": "admin"}
models.Userinfo.objects.create(**dic)

2.查询数据

    #查询数据all.filter
result = models.Userinfo.objects.all() #查询所有
result = models.Userinfo.objects.filter(username="root").all() #while条件,查询指定的数据
for i in result:
print(i.id,i.username,i.password)

filter()里面还可以传入多个参数,就是多个条件,他们之间的关系是逻辑与(and)。

还有一个first()方法,取出第一个值,这样返回就不是列表而直接就是对象了。可以直接用,也可以用在filter()方法后面

models.UserInfo.objects.first()
models.UserInfo.objects.filter(username='root', password='').first()
# 上面就是一个验证用户登录的逻辑了,返回结果是None或者是找到的第一个对象

QuerySet 对象,分别打印出查询结果和这个对象的query属性:

res = models.UserInfo.objects.all()
print(res) # 结果在下面
# <QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>, <UserInfo: UserInfo object (3)>]>
print(res.query) # 结果写下面
# SELECT "cmdb_userinfo"."id", "cmdb_userinfo"."username", "cmdb_userinfo"."password" FROM "cmdb_userinfo

因此要看到对象的值,需要用for循环每个元素

3.删除数据

    #删数据 delete
models.Userinfo.objects.filter(username="admin").delete()

4.修改数据

    #改数据update
models.Userinfo.objects.filter(username="root").update(password = "")

后台管理实例

修改settings

#settings中的设置

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',
]
去掉注释的部分 绑定mysql DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'testdb',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '',
}
}

在总urls里面导入include

# 总urls.py
from django.contrib import admin
from django.urls import path
from django.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
path('ORM/', include("ORM.urls")),
] #orm中的urls from django.urls import path,re_path
from ORM import views
urlpatterns = [
path('login/', views.login),
path('home/', views.home),
path('user_info/', views.user_info),
re_path('detail-(?P<nid>\d+)', views.detail),
]

mysql需要在项目名下的项目里面修改__init__

import pymysql

pymysql.install_as_MySQLdb()

添加三个页面

#login

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/ORM/login/" method="post" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="用户名">
</p>
<p>
<input type="password" name="pwd" placeholder="密码">
</p>
<p>
<input type="submit" value="提交">
<span style="color: red">{{ error_msg }}</span>
</p> </form>
</body>
</html> #home <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">后台管理系统</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">用户管理</a>
<a class="menu" href="/ORM/user_group/">用户组管理</a>
</div>
<div class="right"></div>
</body>
</html> #user_info <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">后台管理系统</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">用户管理</a>
<a class="menu" href="/ORM/user_group/">用户组管理</a>
</div>
<div class="right">
<h3>用户列表</h3>
<table border="1px">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
</tr>
{% for row in userlist%}
<tr>
<td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
<td>{{row.username}}</td>
</tr>
{% endfor %}
</thead>
</table>
</div>
</body>
</html> #detail <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">后台管理系统</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">用户管理</a>
<a class="menu" href="/ORM/user_group/">用户组管理</a>
</div>
<div class="right">
<h3>详细信息</h3>
<h5>id:{{ user.id }}</h5>
<h5>用户名:{{ user.username }}</h5>
<h5>密码:{{ user.password }}</h5>
</div>
</body>
</html>

models创建表

from django.db import models

# Create your models here.
class User(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)

去终端执行2条命令

python manage.py makemigrations
python manage.py migrate

views主要代码:

from django.shortcuts import render,HttpResponse,redirect
from ORM import models def initialized_db():
USER = [
{"username": "aaa", "password": ""},
{"username": "bbb", "password": ""},
{"username": "ccc", "password": ""},
{"username": "ddd", "password": ""},
]
find = models.User.objects.first()
print("find",find)
if find == None:
for row in USER:
models.User.objects.create(username =row["username"],password = row["password"])
initialized_db() def login(request):
error_msg = ""
if request.method == "GET":
return render(request,"login.html")
elif request.method == "POST":
user = request.POST.get("user",None)
pwd = request.POST.get("pwd",None)
result = models.User.objects.filter(username = user,password = pwd).first()
#print(result)
if result:
return redirect("/ORM/home/")
else:
error_msg = "用户名或密码错误"
return render(request,"login.html",{"error_msg":error_msg})
else:
return redirect("/login/") def home(request): return render(request,"home.html") def user_info(request):
userlist = models.User.objects.all()
#print(userlist.query) #打印出来的是select语句
return render(request,"user_info.html",{"userlist":userlist}) def detail(request,nid):
a = models.User.objects.filter(id=nid)
user = models.User.objects.filter(id = nid).first() #这里要用first,否则就要用循环
#print(type(a),type(user)) #<class 'django.db.models.query.QuerySet'> <class 'ORM.models.User'>
return render(request,"detail.html",{"user":user})

 增加用户信息

在页面上添加单条用户信息,并立即显示在页面上

#user_info 添加如下:

<h3>添加用户</h3>
<form method="post" action="/ORM/user_info/">
<input type="text" name = "user" placeholder="用户名" />
<input type="password" name = "password" placeholder="密码" />
<input type="submit" value="添加" />
</form> views.py 添加并修改如下: def user_info(request):
if request.method == "GET":
userlist = models.User.objects.all()
#print(userlist.query) #打印出来的是select语句
return render(request,"user_info.html",{"userlist":userlist})
else:
if request.method == "POST":
username = request.POST.get("user")
password = request.POST.get("password")
models.User.objects.create(username = username,password = password)
return redirect("/ORM/user_info/")

删除用户信息

修改user_info.html,
#直接修改表格,也可以再添加一列来放删除 <tr>
<td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
<td>{{row.username}} |
<a href="/ORM/dele-{{row.id}}">删除</a>
</td>
</tr> urls.py 添加如下: re_path('dele-(?P<nid>\d+)', views.dele), #views.py 添加如下 def dele(request,nid):
models.User.objects.filter(id = nid).delete()
return redirect("/ORM/user_info/")

修改用户信息

#继承home页面后,并修改右侧内容为

<div class="right">
<h3>编辑用户信息</h3>
<form action="user_edit-{{obj.id}}.html" method="post">
<input style="display: none" type="text" name="id" value="{{obj.id}}"/>
<input type="text" name="username" value="{{obj.username}}"/>
<input type="text" name="password" value="{{obj.password}}"/>
<input type="submit" value="提交"/>
</form>
</div> #urls.py re_path('user_edit-(?P<nid>\d+)', views.user_edit), #views.py def user_edit(request,nid):
if request.method == "GET":
obj = models.User.objects.filter(id = nid).first()
return render(request,"user_edit.html",{"obj":obj})
else:
if request.method == "POST":
nid = request.POST.get("id")
username = request.POST.get("username")
password = request.POST.get("password")
models.User.objects.filter(id = nid).update(username = username,password = password)
return redirect("/ORM/user_info/")

注意:<form action="user_edit-{{obj.id}}.html" method="post">  这里的写法

ORM表结构

修改表结构

1.修改过表结构之后,需要再执行一下下面的2行命令,把新的表结构应用到数据库。

python manage.py makemigrations
python manage.py migrate

2.修改数据长度:当修改数据长度比之前长的没什么影响,但是修改后的数据长度比之前短,就会造成数据丢失

3.删除一列,重新执行命令就后自动删掉了,没什么特别的

4.增加一列:当增加一列

# Create your models here.
class User(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)
email = models.CharField(max_length=32)

  在终端执行第一条命令时,会提示,这是因为Django默认内容不能为空,这时你可以根据提示操作

  1.在终端输入字符串作为默认值,然后再输入第二条命令

  2.在新添加的一列后面添加null = True,此时表格新添加的一列就都可以为空了

    email = models.CharField(max_length=32,null=True)

5.数据类型

  基本数据类型:字符串,数字,时间,二进制

Django的ORM提供了非常多的字段类型,比如:EmailField、URLField、GenericIPAddressField。这些其实都是字符串类型而已,并且确实对我们没任何用(并不能帮我们做数据验证)。这些字段类型的只有在用Django的后台管理页面 admin 的时候才能发挥数据验证的效果。只有通过admin提交数据的时候才会验证你的数据格式是否正确。后面写怎么登录及哪里会显示格式错误

models对象的数据类型

AutoField(Field)
- int自增列,必须填入参数 primary_key=True BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32) class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32) SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647 BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 自定义无符号整数字段 class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED' PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)', BooleanField(Field)
- 布尔值类型 NullBooleanField(Field):
- 可以为空的布尔值 CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度 TextField(Field)
- 文本类型 EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制 GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both" URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字 UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹 FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field)
- 浮点型 DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度 BinaryField(Field)
- 二进制类型

把主键拿出来说下

自增id,之前定义表结构的时候,省略了主键,让Django帮我创建了自增id。也可以自己定义主键和自增id:

id = models.AutoField(primary_key=True),一定要加后面的     primary_key=True
class User(models.Model):
id = models.AutoField(primary_key=True) # 数据类型是自增,并且设为主键
g = models.CharField(max_length=32)

6.字段的参数

null                数据库中字段是否可以为空
db_column 数据库中字段的列名
db_tablespace
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
) 参数

db_column :数据库中字段的列名。默认列明就是我们的变量名,可以通过这个参数设置成不一样的  

class UserInfo(models.Model):
username = models.CharField(max_length=32) # 列名就是变量名 username
password = models.CharField(max_length=64, db_column='pwd') # 数据库中的列名是 pwd

auto_now :自动生成一个当前时间,数据更新时(包括创建),注意更新数据的写法,和前面的不同
auto_now_add :自动生成一个当前时间,数据创建时

class UserInfo(models.Model):
# 用户注册时会生成用户名和密码
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
# 创建记录时会生成当前时间存放在ctime里,这个就是用户的注册时间
ctime = models.DateTimeField(auto_now_add=True)
# 用户修改密码会更新uptime的时间,这个就是上次修改密码的时间
uptime = models.DateTimeField(auto_now=True) #创建数据
# models.UserInfo.objects.filter(id=nid).update(password='1234') 这种方法更新是不会刷新 auto_now 的时间的
# 用save()方法更新可以刷新 auto_now 的时间
# obj = models.UserInfo.objects.filter(id=nid).first()
# obj.password = '4321'
# obj.save()

choices :Admin中显示选择框的内容。(用不变动的数据放在内存中从而避免连表操作,连表操作会涉及到性能问题)

class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
# 用户有各种类型
user_type_choices = (
(1, '管理员')
(2, '普通用户')
(3, '访客')
)
# 定义一个用户类型的字段
user_type_id = models.IntegerField(choices=user_type_choices, default=2)
# 这样数据库里是一个整数类型,值是1、2、3。使用字段名取值 obj.user_type_id 获取到的是数值
# 如果要获取后面的内容,使用 get_FOO_display() 方法, 即 obj.get_user_type_id_display()
# 但是我们在admin里看选择框的时候看到的是“管理员”、“普通用户”、“访客”,这就是因为把选项所对应的内容放到了内存中了
# 有了Django这个功能就不用再搞一张表,存放各个数值对应的内容了,还要做外键关联,用的时候还要连表查询
# 即使不用admin,我们也可以在自己的代码里读取这个属性获取到内容,避免连表查询

error_messages :自定义错误信息(字典类型)。字典key:null、blank、invalid、invalid_choice、unique、unique_for_date

class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64, error_messages={'null': "不能为空", 'invalid': '格式错误'})

help_text :Admin中该字段的提示信息。默认没有提示信息,设置后会显示在input框的下方

外键操作

一对多

创建外键关联-修改表结构

usergroup = models.ForeignKey(User_Group,on_delete=models.CASCADE,default=1)
ForeignKey创建外键

第一次创建的时候,我没有添加on_delete=models.CASCADE

在使用python manage.py makeigrations 进行迁移的时候的出错了,报错如下:

经过筛查,在创建多对一的关系的,需要在Foreign的第二参数中加入on_delete=models.CASCADE  主外关系键中,级联删除,也就是当删除主表的数据时候从表中的数据也随着一起删除

on_delete=models.CASCADE 
default=1  设置外键默认值

外键关系创建完后,在user表里面会生成一列usergroup_id,并且生成了一个usergroup类

此时要查询usergroup表中的内容,在html中可以使用 {{row.usergroup.dept}},row为views传过来的user表中所有对象

通过对象获取部门

最新文章

  1. linux apt-get install 错误: Package has no installation candidate
  2. tensor flow入门笔记
  3. ajax 请求如何解决乱码
  4. Excel中显示长数字的方法
  5. Apache Shiro 简介
  6. 【转】SQL注入(通过sqlmap来改变所有事情)
  7. iOS 平台开发OpenGL ES程序注意事项
  8. linux lnmp编译安装
  9. 解决weblogic Managed Server启动非常慢的情况
  10. Gnuradio 学习
  11. Flask学习记录之Flask-Migrate
  12. java byte【】数组与文件读写
  13. Java程序员的必备知识-类加载机制详解
  14. 不能收缩 ID 为 %s 的数据库中 ID 为 %s 的文件,因为它正由其他进程收缩或为空。
  15. 获取Lambda表达式内表达式的值
  16. Fluent动网格【4】:DEFINE_CG_MOTION宏实例
  17. mysql常用语句练习-基于ecshop2.7.3数据库(1)
  18. 【转】学习Linux守护进程详细笔记
  19. 【Leetcode】222. Count Complete Tree Nodes
  20. Oracle 函数 “判断数据表中不存在的数据,才允许通过”

热门文章

  1. hutool http+天气预报
  2. oracle序列的缓存
  3. ETL的两种架构(ETL架构和ELT架构)
  4. python 之 paramiko
  5. vue--钩子函数1
  6. 使用ServletContainerInitializer动态注册组件
  7. Monkey King(左偏树 可并堆)
  8. stringstream的使用
  9. [引]雅虎日历控件 Example: Two-Pane Calendar with Custom Rendering and Multiple Selection
  10. 转:自定义控件三部曲之动画篇——alpha、scale、translate、rotate、set的xml属性及用法