web框架了解
MVC Model View Controller 数据库 模板文件 业务处理 MTV Model Template View 数据库 模板文件 业务处理
django
1、django框架的安装
pip3 install django
2、Pycharm创建django 项目
命令行:# django-admin startproject <工程名称 如s14day19_2>
3、目录结构解析
s14day19_2 - s14day19_2 # 对整个程序进行配置的目录 - init - settings # 配置文件:含模板、静态文件等路径配置 - url # URL对应关系 - wsgi # 遵循WSIG规范,uwsgi + nginx - manage.py # 管理Django程序: - python manage.py - python manage.py startapp xx - python manage.py makemigrations - python manage.py migrate
- templates # 模板存放路径
4、配置文件解析
4.1settings.py
# 模板配置
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')]
.....
# 静态文件路径配置。STATIFILES_DIRS = 元组
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
""" Django settings for s14day19_2 project. Generated by 'django-admin startproject' using Django 3.0.8. For more information on this file, see https://docs.djangoproject.com/en/3.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.0/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'o!n!^7e-u5x#+ziuagcro-gj-96l0_iy9+9z)apw@n%ku7+_3z' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] 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', ] ROOT_URLCONF = 's14day19_2.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , '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', ], }, }, ] WSGI_APPLICATION = 's14day19_2.wsgi.application' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ STATIC_URL = '/static/' # 静态文件路径配置。STATIFILES_DIRS = 元组 STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static'), )
4.2 urls.py 配置请求访问url的对应处理逻辑
如下为urls.py示例;from app01 import views 里面的app01 为具体的应用模块。
app01模块通过命令行下 在项目根目录 执行命令# python manager.py startapp 【工程名称,如app01】创建
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), path(r'login', views.login), ]
5、创建应用
5.1 应用处理逻辑的创建
cd 到django项目根目录,执行命令
python manager.py startapp 【工程名称,如app01】
应用目录生成文件如下
5.2 应用.views 内定义具体的函数,来处理对应url的逻辑
views.py示例如下; 负责对应url的http请求处理逻辑
from django.shortcuts import render from django.shortcuts import redirect from django.shortcuts import HttpResponse # Create your views here. def index(request): return HttpResponse("Hello This is Index") def login(request): if request.method == "GET": return render(request, 'login.html') elif request.method == "POST": user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'zmd' and pwd == '123': return redirect('/index') else: return redirect('/login/') else: return redirect('/index')
5.3模板
django项目根目录内templates目录存放XXX.html 模板
测试模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST"> <p> <input type="text" name="user" placeholder="用户名"> </p> <p> <input type="password" name="pwd" placeholder="密码"> </p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
6、启动访问
或者项目根目录下执行命令启动:
python manage.py runserver 127.0.0.1:800
详解知识点一:请求处理逻辑views
FBV 方式:(F 代表函数;B代表django内部的base.py基础功能,V代表Views)
1、获取post请求单条数据request.POST.get()
def login(request): if request.method == "GET": return render(request, 'login.html') elif request.method == "POST": user = request.POST.get('user') pwd = request.POST.get('pwd') print('user:',user) if user == 'zmd' and pwd == '123': return redirect('/index') else: return redirect('/login/') else: return redirect('/index')
2、获取多个数据例如前端checkbox的值--request.POST.getlist()
返回列表
def login(request): if request.method == "GET": return render(request, 'login.html') elif request.method == "POST": favor = request.POST.getlist('favor') print(favor)
3、获取上传文件
3.1前端form表单需添加enctype="multipart/form-data"
<form action="/login/" method="POST" enctype="multipart/form-data"> <p> <input type="file" name="file1"> </p> </form>
3.2 request获取文件request.FILES.get('前端表单文件标签name')
获取文件对象:upload_file_obj = request.FILES.get('前端表单文件标签name')
print(upload_file_obj) 会打印文件名,但本身他是文件对象,因为该对象内有__str__(self)方法,或者__repr__(self) 方法 ----->这里复习一下。
获取对象文件名:文件对象.name
文件二进制内容:文件对象.chunks()
def login(request): if request.method == "GET": return render(request, 'login.html') elif request.method == "POST": upload_file_obj = request.FILES.get('file1') print('upload_file_obj:',upload_file_obj) print(upload_file_obj.name) upload_file_name = upload_file_obj.name f = open( upload_file_name, 'wb') for item in upload_file_obj.chunks(): f.write(item) f.close()
4、获取请求的其他信息
request.environ 封装了所有用户请求信息
获取客户端信息:request.environ['HTTP_USER_AGENT']
获取Cookie:reqeust.COOKIES.get('username111')
uri路径信息:request.path_info
CBV方式:(C代表类Class;B代表django内部的base.py基础功能,V代表Views)
1、views.py CBV模式 类的写法---继承django.views.View
# CBV 模式 from django.views import View class Home(View): # 继承 from django.views import View # 继承后可以定义的http请求方法有http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'],详见View类 def get(self,request): """定义路径对应的get请求方法""" print('Home GET') return render(request, 'home.html') def post(self,request): print('Home POST') return render(request, 'home.html') # Django 如何找到get post函数做对应url的处理呢? # 采用的是反射,hasattr,这些封装在dispatch函数中 def dispatch(self, request, *args, **kwargs): # 可以重写父类的方法加入自定义的功能,此方法在get post 函数之前执行 print('before in dispatch') # 重写父类的dispatch 方法,等于覆盖了父类方法, # 若需保留父类的方法能够执行,需要super 执行父类的该方法 # 并且最终返回 request_result = super(Home,self).dispatch(request, *args, **kwargs) # 处理完具体的GET POST请求 print('after in dispatch') return request_result
2、对应的urls.py 写法 :类名.as_view()
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), path(r'login/', views.login), # CBV path(r'home/',views.Home.as_view()) ]
3、访问效果
before in dispatch
Home GET
after in dispatch
[11/Jul/2020 13:22:39] "GET /home/ HTTP/1.1" 200 260
before in dispatch
Home POST
after in dispatch
[11/Jul/2020 13:22:48] "POST /home/ HTTP/1.1" 200 260
4、dispatch特殊函数说明
Django 如何找到get post函数做对应url的处理呢?
原理采用的是反射,hasattr,这些封装在dispatch函数中;
此方法在get post 函数之前执行,可以重写父类的dispatch方法加入自定义的功能
# Django 如何找到get post函数做对应url的处理呢? # 采用的是反射,hasattr,这些封装在dispatch函数中 def dispatch(self, request, *args, **kwargs): # 可以重写父类的方法加入自定义的功能,此方法在get post 函数之前执行 print('before in dispatch') # 重写父类的dispatch 方法,等于覆盖了父类方法, # 若需保留父类的方法能够执行,需要super 执行父类的该方法 # 并且最终返回 request_result = super(Home,self).dispatch(request, *args, **kwargs) # 处理完具体的GET POST请求 print('after in dispatch') return request_result
服务端获取值:request的常用方法
request.body ---所有数据内容的原生值
request.POST(request.body)
request.FILES(request.body)
request.GET
request.XXX.getlist
request.META
request.method (值为POST,GET,PUT)
request.path_info
request.COOKIES
request的所有方法
[ "COOKIES", "FILES", "GET", "META", "POST", "__class__", "__delattr__", "__dict__", "__dir__", "__doc__", "__eq__", "__format__", "__ge__", "__getattribute__", "__gt__", "__hash__", "__init__", "__init_subclass__", "__iter__", "__le__", "__lt__", "__module__", "__ne__", "__new__", "__reduce__", "__reduce_ex__", "__repr__", "__setattr__", "__sizeof__", "__str__", "__subclasshook__", "__weakref__", "_current_scheme_host", "_encoding", "_get_full_path", "_get_post", "_get_raw_host", "_get_scheme", "_initialize_handlers", "_load_post_and_files", "_mark_post_parse_error", "_messages", "_read_started", "_set_content_type_params", "_set_post", "_stream", "_upload_handlers", "body", "build_absolute_uri", "close", "content_params", "content_type", "encoding", "environ", "get_full_path", "get_full_path_info", "get_host", "get_port", "get_raw_uri", "get_signed_cookie", "headers", "is_ajax", "is_secure", "method", "parse_file_upload", "path", "path_info", "read", "readline", "readlines", "resolver_match", "scheme", "session", "upload_handlers", "user" ]
服务端返回消息:
return HttpResponse('str...')
return render(request,'xxxx.html', {'key':value,xxx})
return redirect('/xxx')
response = HttpResponse(同上)/render(同上)/rediret(同上)
response['name'] = 'value' -->设置响应头
response.set_cookie('key','value') --->设置cookie
return response
详解知识点二:模板语言
{{ 变量名 }}
循环语句:
{% for xx in <views字典传过来的key> %}
...
{% endfor%}
注意:%和{}之间不允许有空格
模板语言内部变量:
{{ forloop.counter }} 每循环一次该值自动+1
{{ forloop.first }} True/False 是否第一次循环
{{ forloop.last }} True/False 是否最后一次循环
{{ forloop.revcounter }} 倒序计数器
{{ forloop.revcounter0 }} 倒序计数器结尾为0
{{ forloop.parentloop}} 父循环信息 (嵌套循环时)
1、请求处理逻辑get post ....渲染的时候传递字典
def func(request): return render(request, "index.html", {'current_user': "alex"})
对应的index.html
<div>{{current_user}}</div>
2、请求处理逻辑get post ....渲染的时候传递列表
USER_LIST = [ {'name': 'zmd', 'gender': 1, 'age': '22'}, {'name': 'qjj', 'gender': 0, 'age': '20'}, {'name': 'zmc', 'gender': 0, 'age': '2'}, ] def index(request): return render(request,'index.html',{'user_list': USER_LIST})
对应模板示例index.html
<ul> {% for user in user_list %} <li>{{ user.name }} gender:{{ user.gender }} age: {{ user.age }}</li> {% endfor %} </ul>
3、请求处理逻辑get post... 渲染模板时传递字典
views 传递字典
USER_DICT = { 1: {'name': 'zmd', 'gender': 1, 'age': '22'}, 2: {'name': 'qjj', 'gender': 0, 'age': '20'}, 3: {'name': 'zmc', 'gender': 0, 'age': '2'}, } def index(request): return render(request, 'index.html', {'user_dict': USER_DICT})
对应html 字典可以直接使用python字典的对应方法,但是在html语法中不需要加()
{% for key, value in user_dict.items %} <li>用户{{ key }} 姓名:{{ value.name }} gender:{{ value.gender }} 年龄:{{ value.age }} </li> {% endfor %}
4. 模板内置变量{{ forloop.counter }}
示例:此序号,for循环的次数计数器,展示第几个数据
{% for host in hosts %} <li> 序号: {{ forloop.counter }} 主机名:{{ host.hostname }} 主机IP:{{ host.ip }} 端口{{ host.port }} 业务线ID:{{ host.b_id }}</li> {% endfor %}
示例效果
详解知识点三:urls.py 路由系统(应用场景:页面跳转)
路由系统,URL 1、url(r'^index/$', views.index), url(r'^home/$', views.Home.as_view()), 2、url(r'^detail-(d+).html', views.detail), 3、url(r'^detail-(?P<nid>d+)-(?P<uid>d+).html', views.detail)
注意:
$为匹配的终止符,如果不写:有前缀相同的url,并且短的url在上,则后面的url被截胡,永远无法匹配到。
例如:
url(r'^index'),
url(r'^index_add') 这条就无法匹配到了
1、默认的问号传参,(http://xxxx/detail/?nid=xxxxxxxx),后台获取值request.GET.get('query name')可以以此做详情页面跳转
这种默认传参无需urls.py路由做多余的配置。无论是get 还是post等等...后台直接request.method.get('query name') 或者request.method.getlist('query name') 即可获得。
urls.py 无特殊配置
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), path(r'detail/',views.Detail.as_view()) ]
views.py
from django.shortcuts import render
from django.shortcuts import redirect
USER_DICT = {
1: {'name': 'zmd', 'gender': 1, 'age': '22'},
2: {'name': 'qjj', 'gender': 0, 'age': '20'},
3: {'name': 'zmc', 'gender': 0, 'age': '2'},
}
def index(request):
return render(request, 'index.html', {'user_dict': USER_DICT})
class Detail(View): def get(self,request): userid = request.GET.get('nid') user = USER_DICT.get(userid) return render(request,'detail.html', {'user': user})
index.html 改为a标签,点击跳转
<ul> {% for key, user in user_dict.items %} <li><a href="/detail/?nid={{ key }}" target="_blank">{{ user.name }}</a></li> {% endfor %} </ul>
detail.html
<h1>详细信息</h1> <h6>用户名:{{ user.name }}</h6> <h6>性别:{{ user.gender }}</h6> <h6>年龄:{{ user.age }}</h6>
2、url 正则分组匹配,
2.1 动态url分组匹配处理;对应的views里面的获取分组的值需要有和分组数量相等的形参
urls.py
需要用django.conf.urls import url来匹配
本例urls.py 里面(d+) 为第一个分组
from django.conf.urls import url from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), url(r'detail-(d+).html', views.Detail.as_view()), ]
views.py 需要用一个参数接收请求参数url中的分组,比如使用nid,nid只是代表获取url中分组(即url中的小括号())顺序而已;
class Detail(View): # def get(self,request): # userid = request.GET.get('nid') # user = USER_DICT.get(userid) # return render(request,'detail.html', {'user': user}) # url正则匹配url,的第一个正则匹配值使用nid接收,nid任意起名 def get(self, request, nid): user = USER_DICT.get(nid) return render(request, 'detail.html', {'user': user})
渲染的detail.html内容不做任何变动。只需要把index.html中<a></a>标签的跳转url 即: href="xxxx"更正匹配对应的urls里面的正则路径即可
index.html
<ul> {% for key, user in user_dict.items %}<li><a href="/detail-{{ key }}.html" target="_blank">{{ user.name }}</a></li> {% endfor %} </ul>
2.2 url 正则匹配对分组命名,views里面获取url中的分组时,指定分组命名获取,无需按照顺序。
url分组语法:(?P<变量名>xxxx正则表达式xxxx)
urls.py 本示例:分组两个分别命名为uid和nid
from django.conf.urls import url urlpatterns = [ path('admin/', admin.site.urls), url(r'detail2-(?P<uid>d+)-(?P<nid>d+).html', views.Detail2.as_view()), ]
view.py 示例指定分组变量名获取对应url对应的值,形参名要和url的分组名一致
# CBV 模式 from django.views import View class Detail2(View): def get(self,request, nid, uid): group_test_str = "uid=%s nid=%s" % (uid, nid) return HttpResponse(group_test_str)
2.3 分组数量位置的场景。views.py中使用 *args **kwargs接收
注意:url 正则分组同时有命名分组和非命名分组时,只可以获取到命名分组,也就是只有**kwargs能获取到值
获取未知数量的命名分组:urls.py
from django.contrib import admin from django.urls import path from app01 import views from django.conf.urls import url urlpatterns = [ path('admin/', admin.site.urls), url(r'detail3-(?P<uid>d+)-(?P<nid>d+)-(?P<iDontKonwID>d+).html', views.Detail3.as_view()), ]
views.py
from django.views import View class Detail3(View): def get(self,request, *args, **kwargs): print(args) print(kwargs) all_args = str(args) + str(kwargs) return HttpResponse(all_args)
示例请求:http://127.0.0.1:8000/detail3-3-8-888.html
获取到分组值:(){'uid': '3', 'nid': '8', 'iDontKonwID': '888'}
正则url阶段性总结:
方式1、
url(r'^detail-(d+)-(d+).html', views.detail),
def func(request, nid, uid): pass
def func(request, *args): args = (2,9)
方式2、
url(r'^detail-(?P<nid>d+)-(?P<uid>d+).html', views.detail)
def func(request, nid, uid): pass 或者 def funct(request, **kwargs): kwargs = {'nid': 1, 'uid': 3}
推荐采用方式二哦
3、 url里面的name:对URL路由关系进行命名,(以后可以根据此名称生成自己想要的URL)
等于将url命名:可以直接在views.py 和html模板中直接用别名代替URI,用起来相对比较方便
3.1 views.py使用url别名示例:
url(r'detail-(d+).html', views.Detail.as_view(), name='detail-test'), 该url别名命名为detail-test
url(r'detail2-(?P<uid>d+)-(?P<nid>d+).html', views.Detail2.as_view()), 在Detail2的视图中跳转时指定url别名跳转
如下为Detail2 示例
注意:使用的别名的url中有几个分组,在使用别名过程中要对分组变量传值,如下的nid便是对如上的(d+)分组赋值,用来生成url
class Detail2(View): def get(self,request, nid, uid): group_test_str = "uid=%s nid=%s" % (uid, nid) # return HttpResponse(group_test_str) return redirect('detail-test', nid)
这样访问效果为Detail2 视图,跳转到Detail视图
3.2 模板语言使用url别名示例: {% url "url的别名" 分组传值变量 %}"
urls.py 中有几个分组,在使用url别名时要对分组变量赋值
from django.contrib import admin from django.urls import path from app01 import views from django.conf.urls import url urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), # CBV url(r'detail-(d+).html', views.Detail.as_view(), name='detail-test'), ]
views.py 渲染的index.html文件中使用,
def index(request): return render(request, 'index.html', {'user_dict': USER_DICT})
index.html中使用 {% url "url别名" [分组变量传值 ... ... ] %}
<li><a href="{% url "detail-test" key %}" target="_blank">{{ user.name }}</a></li>
3.3 urls.py中 的url有正则分组时,html模板中传值可以使用 分组名=xxx
urls.py
url(r'detail3-(?P<a>[a-z]+)--(?P<uid>d+)-(?P<nid>d+)-(?P<iDontKonwID>d+).html', views.Detail3.as_view(), name='detail3'),
html文件
<a href="{% url "detail3" a="ccc" uid=1 nid=2 iDontKonwID=3 %}" target="_blank">{{ user.name }}</a>
模板中取当前url路径 {% url request.path_info %}
4、 reverse() 通过uri 别名反向解析URI,如有分组别名使用args=(,..)元组或者kwargs={}字典 来传值
若url没有正则分组,则直接reverse('url别名') 即可
views.py
from django.urls import reverse detail_url = reverse('detail-test', args=(2, ))
对应的urls.py 里面的url别名:
urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), url(r'detail-(d+).html', views.Detail.as_view(), name='detail-test'), ]
对于urls.py命名url内有正则分组命名,views.py使用kwargs传值
urls.py
urlpatterns = [ path('admin/', admin.site.urls), path(r'index', views.index), url(r'detail3-(?P<a>[a-z]+)--(?P<uid>d+)-(?P<nid>d+)-(?P<iDontKonwID>d+).html', views.Detail3.as_view(), name='detail3'), ]
views.py
def index(request): from django.urls import reverse detail_url = reverse('detail-test', args=(2, )) print(detail_url) # detail3_url = reverse("detail3", args=('aaa', 2, 3, 4)) detail3_url = reverse("detail3", kwargs={'a':'bbb','uid':1, 'nid': 2, 'iDontKonwID':3})
对应模板中使用 {% url "url别名" 分组名=值 %}
<a href="{% url "detail3" a="ccc" uid=1 nid=2 iDontKonwID=3 %}" target="_blank">{{ user.name }}</a>
注: target="_blank" 在新标签页打开
5、多级路由
project/urls.py from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^cmdb/', include("app01.urls")), url(r'^monitor/', include("app02.urls")), ] app01/urls.py from django.conf.urls import url,include from django.contrib import admin from app01 import views urlpatterns = [ url(r'^login/', views.login), ] app02/urls.py from django.conf.urls import url,include from django.contrib import admin from app02 import views urlpatterns = [ url(r'^login/', views.login), ]
访问的时候,以cmdb/开头的都由app01下面的urls.py处理分发。例如:http://127.0.0.1:8000/cmdb/index
6、命名空间
定义带命名空间的url之后,使用namespace + name生成URL时候,应该如下:
-
- views.py 里面:v = reverse('namespace名称:name名称', args=(url变量传值,..), kwargs={'pk':11})
- 或者v = reverse('app_name名称:name名称', args=(url变量传值,..), kwargs={'pk':11})
- html模板里面:{% url 'app01:detail' pk=12 pp=99 %}
示例:project.urls.py
urlpatterns = [ path('admin/', admin.site.urls), url(r'cmdb/', include('app01.urls', namespace='hehe')), ]
app01.urls.py 应用路径要配置上app_name='自定义应用名称'
url(r'detail-(d+).html', views.Detail.as_view(), name='detail-test'),
app01.views.py reverse('namespace名称:name名称', args=(url变量传值,..), kwargs={'pk':11}) ;name的url分组未命名,通过args=(,)传值;命名通过kwargs={}传值
reverse('hehe:detail-test', args=(nid, ))
reverse('app01:detail-test', args=(nid, ))
url分组命名传值
# app01.urls.py url(r'detail-(?P<nid>d+).html', views.Detail.as_view(), name='detail-test'), #app01.views.py reverse('hehe:detail-test', kwargs={'nid': nid, })
详解知识点四:models.py 数据库ORM框架
功能:code first 不直接写数据库的sql语句,通过代码创建
django这里通过类创建:继承modules 模块中的Model类
1、基础配置(注册要连接数据库的app;配置数据库引擎)
项目的settings.py 里面配置要找的所有modules.py
1.1 注册要生成数据库的app
主项目下 settings.py 配置要注册数据库连接的所有app;除了自己新建的app01 其余全部是django框架自己所要用到的,勿删!
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', ]
1.2 配置数据库使用的引擎
主项目下 settings.py 内的 DATABASES变量;默认采用的是本地的sqlite3
sqlite3
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
Mysql在此配置即可
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'dbname', 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '', 'PORT': '', } }
# 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替 # 如下设置放置的与project同名的配置的 __init__.py文件中 import pymysql
pymysql.version_info = (1, 3, 13, "final", 0)
pymysql.install_as_MySQLdb()
注意:如果使用mysql 可能报错:django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
解决方案参考:
https://yuntianti.com/posts/fix-django3-mysqlclient-import-error/
https://blog.csdn.net/weixin_45476498/article/details/100098297
2、设计app用的数据库表 models.py
app01 应用目录下 models.py
from django.db import models # Create your models here. # app01_userinfo class UserInfo(models.Model): # id列,自增,主键 # 用户名列,字符串类型,指定长度 username = models.CharField(max_length=32) password = models.CharField(max_length=64)
3、执行命令生成数据库(根据类自动创建数据表)
项目根目录下执行命令生成数据库同步脚本:
python manage.py makemigrations
生成数据表
python manage.py migrate
4、数据库表常用字段类型一览
from django.db import models password = models.CharField(max_length=64) models.BigIntegerField() models.IntegerField() models.EmailField() models.IPAddressField() models.DateField() models.DateTimeField() models.FloatField() models.TimeField()
models.UrlField()
models.AutoField(PrimaryKey=True) # 用来自定义自增列
#等等....除了主要的int CharField DateTime...等数据库默认的以外都是django内部给admin用的
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) - 二进制类型 字段
字段约束:
字段的参数: null -> 字段是否可以为空:True/False default -> 默认值 : primary_key -> 主键 :True/False db_column -> 字段列名 : String db_index -> 索引 : True/False unique -> 唯一索引 :True/False unique_for_date -> unique_for_month unique_for_year auto_now -> 创建时,自动生成时间 auto_now_add -> 更新时,自动更新为当前时间 : 触发条件 查询结果.first() 赋值给某一个对象 ,对象.字段名=新值;对象.save() 才会更新,否则不变。
不支持update()
choices -> 从已有选项中选择,赋值为一个嵌套的元组,元组内的子元组为具体选项 on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为 on_delete=models.CASCADE, # 删除关联数据,与之关联也删除 on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做 on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError # models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True) on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理) # models.ForeignKey('关联表', on_delete=models.SET_DEFAULT, default='默认值') on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理) on_delete=models.SET, # 删除关联数据,
4.1 修改表结构
4.1.1 修改原有字段的约束:直接在应用models.py原有字段内修改,然后项目根目录重新执行python manager.py makemigrations; python manager.py migrate
4.1.2.增加字段:在应用的models.py 对应的表结构类中增加,若增加字段前有数据,在执行python manager.py makemigrations 时;由于字段默认不允许为空会提示对已有数据如何设置。
2.1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 马上对已有数据设置新字段默认值
2.2) Quit, and let me add a default in models.py 返回修改models.py设置字段默认值,例如设置新增字段允许为空:none=True.
4.1.3.删除字段:直接删除,然后执行python manager.py makemigrations ; python manager.py migrate 即可删除。有数据的全部丢失
4.2 特殊字段约束
auto_now -> 创建时,自动生成时间
auto_now_add -> 更新时,自动更新为当前时间 : 触发条件 查询结果.first() 赋值给某一个对象 ,对象.字段名=新值;对象.save() 才会更新,否则不变。
不支持update(fieldname='newValue')
choices -> 从已有选项中选择,赋值为一个嵌套的元组,元组内的子元组为具体选项
choices 使用示例:本例user_type_choice 变量实际则没有写入到数据库,而是在内存中存放
# app01_userinfo class UserInfo(models.Model): # id列,自增,主键 # 用户名列,字符串类型,指定长度 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=3)
说明:文字注释为django admin 后台管理端在管理对应的表数据的时候展示用。如下图
blank=True/False 在django admin中是否可以为空
verbose_name="xxx" 在django admin中显示的中文名称
editable=True/False 在django admin是否可以被编辑,字段写上这个=False,admin管理时不显示该列
error_messages 错误提示
help_text django admin中的提示
validators django form ,自定义错误信息
4.3 表设计:外键关联
示例:models.ForeignKey("UserGroup", to_field='gid', on_delete=models.CASCADE, null=True)
注释:"UserGroup" 关联的表名,
to_field='gid' 具体关联的列,
on_delete=models.CASCADE, 当被关联的表中的字段删除时,本表字段处理动作设置
null=True 是否可以为空
注意:on_delete=models.CASCADE/models.SET_DEFAULT 需要本字段有对应的默认设置。例如是否允许为空。default值等......
外键数据的查询:
查询具体数据的外键值:外面在外表中对应的主键值---> 查询结果数据对象.外键字段名_id
例如:
models.py 中 userinfo表中的user_group字段,外键指向usergroup表中的gid字段
class UserGroup(models.Model): gid = models.AutoField(primary_key=True) caption = models.CharField(max_length=32, unique=True) ctime = models.DateTimeField(auto_now_add=True, null=True) uptime = models.DateTimeField(auto_now=True, null=True) class UserInfo(models.Model): # id列,自增,主键 # 用户名列,字符串类型,指定长度 username = models.CharField(max_length=32, error_messages={'required': '必填项目必填项目'}, help_text='用户名') password = models.CharField(max_length=64) user_type_choices = ( (1, '超级用户'), (2, '普通用户'), (3, '只读用户'), ) user_type_id = models.IntegerField(choices=user_type_choices, default=3) user_group = models.ForeignKey("UserGroup", to_field='gid', on_delete=models.CASCADE, null=True)
views.py中查询语句获取外键的主键值
search_resu = models.UserInfo.objects.filter(username=username).first() print("%s 的用户组ID:%s" % (username, search_resu.user_group_id))
views.py 中查询语句获取外键的表对应数据的其他字段:通过点 外键字段名 点 外键表有的字段即可。
跨表查询举例:本表单条数据.外键字段名.外表字段名
search_resu = models.UserInfo.objects.filter(username=username).first() print("%s 的用户组名:%s" % (username, search_resu.user_group.caption)) #zmd2 的用户组名:运维 #数据 为: #+-----+---------+----------------------------+----------------------------+ #| gid | caption | ctime | uptime | #+-----+---------+----------------------------+----------------------------+ #| 3 | 运维 | 2020-07-13 11:41:33.162318 | 2020-07-13 11:41:33.162318 | #+-----+---------+----------------------------+----------------------------+
5、增删改查操作
5.1 创建数据
方式一:自定义数据库表类.objects.create(字段1='值1', 字段二='值2', .........) 直接写入到数据库中。
示例views.py中创建新数据
from app01 import models class OrmStudy(View): def get(self,request, action, username, pwd): if action == "create": create_resu = models.UserInfo.objects.create(username=username, password=pwd)
# new_data = {'username': username, 'password': pwd}
# create_resu = models.UserInfo.objects.create(**new_data) #这种写法一样
.....
方式二:
1.先依照自定义表结构生成数据对象
data1 = 自定义数据库表类(字段1='值1', 字段二='值2', .........)
2.调用数据对象的save()方法提交到数据库
data1.save()
示例
from app01 import models class OrmStudy(View): def get(self,request, action, username, pwd): data1 = models.UserInfo(username=username, password=pwd) data1.save() #后面省略....
5.1.1 一对多外键创建数据
示例:UserInfo 中的usergroup 字段外键关联UserGroup表中的gid字段,
UserInfo 创建数据有两种方式:
1. user_group=UserGroup对象,例如usergroup=models.UserGroup.objects.filter(gid=xxx).first()
2.user_group_id=xxx (推荐用这种,避免重复向数据库查询)
代码示例:
class OrmStudy(View): def get(self,request, action, username, group_id): if action == "create": data1 = models.UserInfo(username=username, user_group_id=group_id) data1.save() #....省略return........
5.1.2 多对多创建数据
方式一:自定义关系表
例:HostToApp表自己写,两个字段分别外键到要关联的表
from django.db import models # Create your models here. class Business(models.Model): bid = models.AutoField(primary_key=True) business_name = models.CharField(max_length=128, null=False) code = models.CharField(max_length=32, null=True) class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32, db_index=True) ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='bid', null=True, on_delete=models.SET_NULL) class Application(models.Model): name = models.CharField(max_length=32) class HostToApp(models.Model): hobj = models.ForeignKey(to='Host', to_field='nid',null=True, on_delete=models.SET_NULL) aobj = models.ForeignKey(to='Application', to_field='id', null=True, on_delete=models.SET_NULL)
方式二:自动创建关系表---models.ManyToManyField("要关联的表"),自动关联主键
第三张表名:
第三张表字段:只有id,和关联表主键字段生成
#方式二:自动创建关系表 class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32, db_index=True) ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='id') # db_index=True 表示创建索引 class Application(models.Model): name = models.CharField(max_length=32) r = models.ManyToManyField("Host") #生成第三张表,非本表字段 # 第三张表只有三列(id,application_id,host_id) 表名字为application_r #无法直接对第三张表进行操作,需要通过第二张表的某条数据对象.第三张表名.操作 obj = Application.objects.get(id=1) #获取某条数据 obj.name # 第三张表操作 obj.r.add(1) #添加第三张表对应关系 Application_id = 1 host_id = 1 obj.r.add(2) #添加第三张表对应关系 Application_id = 1 host_id = 2 obj.r.add(2, 3, 4) #添加第三张表对应关系 Application_id = 1 host_id = 2; Application_id = 1 host_id = 3; ..... obj.r.add(*[1, 2, 3, 4]) #同上 obj.r.remove(1) obj.r.remove(2, 4) obj.r.remove(*[1, 2, 3]) obj.r.clear() #清除Application.objects.get(id=1)的第三张表里面的所有对应数据 obj.r.set([3, 5, 7]) #清除Application.objects.get(id=1)的第三张表里面的所有对应数据,然后重新设置 # 所有相关的主机对象“列表” QuerySet obj.r.all()
5.2 查询数据
语法:自定义表的类名.objects.方法名()。
返回值:<class 'django.db.models.query.QuerySet'> 可以理解为列表,列表内每个元素为具体的数据对象(自定义数据类的对象)
返回值内部元素:
一般:大部分为对象;
特殊:values(['列名', ...]) 返回数据字典{字段名:值},
value_list(['列名', ...]) 返回数据元组(字段名,字段值,字段名....)
['列名',...] 列名不传,默认返回所有字段。
5.2.1方法名列举:
all() 查询所有数据
filter() 过滤,相当于mysql的sql语法中的where条件
查询所有数据
search_resu = models.UserInfo.objects.all() for data in search_resu: print(data.username)
细化过滤结果:
返回值 QuerySet 对象下属方法:
search_resu.first() 获取第一个数据对象,不存在为Null
search_resu.last() 获取最后一个数据对象,不存在为Null
search_resu.all() 获取所有值
search_resu.get() 获取一个数据对象,不存在就报错
search_resu.count() 查询结果的个数
search_resu.query QuerySet结果对应的Sql语句
search_resu.delete()
search_resu.update()
search_resu.update_or_create()
search_resu.select_for_update()
search_resu.exists()
search_resu.values() 获取值,返回QuerySet列表对象,列表内部元素是字典,每个数据一个字典 例如:[{'id':2, 'username': 'zmd2', 'password': 'xxxx'},{...}...]【常用】
values('id','name', ...)内部可以传递具体想要获取的字段.【常用】
search_resu.value_list() 获取值,返回QuerySet列表,内部元素是元组,例如:(2,'user1','password1')
....
本表字段值查询:
data = search_resu.first()
data.字段名
举例
# Mysql 数据库内容 # +----+----------+----------+--------------+---------------+ # | id | username | password | user_type_id | user_group_id | # +----+----------+----------+--------------+---------------+ # | 2 | zmd2 | 123 | 3 | 3 | # +----+----------+----------+--------------+---------------+ search_resu = models.UserInfo.objects.filter(username=username).first() print('查询结果:用户名:%s 密码:%s' % (search_resu.username, search_resu.password)) # 查询结果:用户名:zmd2 密码:123
5.2.2 filter() 过滤(了不起的双下划线) 增加过滤条件:大于小于、in、isnull、contains、range ...
# 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于 # # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull # Entry.objects.filter(pub_date__isnull=True) # contains # # models.Tb1.objects.filter(name__contains="ven") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似 # # startswith,istartswith, endswith, iendswith, # order by # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc # group by # # from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset # # models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写 # # Entry.objects.get(title__regex=r'^(An?|The) +') # Entry.objects.get(title__iregex=r'^(an?|the) +') # date # # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year # # Entry.objects.filter(pub_date__year=2005) # Entry.objects.filter(pub_date__year__gte=2005) # month # # Entry.objects.filter(pub_date__month=12) # Entry.objects.filter(pub_date__month__gte=6) # day # # Entry.objects.filter(pub_date__day=3) # Entry.objects.filter(pub_date__day__gte=3) # week_day # # Entry.objects.filter(pub_date__week_day=2) # Entry.objects.filter(pub_date__week_day__gte=2) # hour # # Event.objects.filter(timestamp__hour=23) # Event.objects.filter(time__hour=5) # Event.objects.filter(timestamp__hour__gte=12) # minute # # Event.objects.filter(timestamp__minute=29) # Event.objects.filter(time__minute=46) # Event.objects.filter(timestamp__minute__gte=29) # second # # Event.objects.filter(timestamp__second=31) # Event.objects.filter(time__second=2) # Event.objects.filter(timestamp__second__gte=31)
# extra # # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) # Entry.objects.extra(where=['headline=%s'], params=['Lennon']) # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) # F # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q # # 方式一: # Q(nid__gt=10) # Q(nid=8) | Q(nid__gt=10) # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') # 方式二: # con = Q() # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # 执行原生SQL # # from django.db import connection, connections # cursor = connection.cursor() # cursor = connections['default'].cursor() # cursor.execute("""SELECT * from auth_user where id = %s""", [1]) # row = cursor.fetchone() 其他操作
5.2.3 跨表查询
两种方式:
1.外键字段下划线id为在本表数据库中实际存储的字段值(例下面的user_group_id)。
2.外键字段代表为第二张表的具体数据对象。封装了数据该有的字段属性。获取值可以一直.下去......
注:跨表设计通过外键,见(4.3 表设计:外键关联)
例如:
models.py 中 userinfo表中的user_group字段,外键指向usergroup表中的gid字段
class UserGroup(models.Model):
gid = models.AutoField(primary_key=True)
caption = models.CharField(max_length=32, unique=True)
ctime = models.DateTimeField(auto_now_add=True, null=True)
uptime = models.DateTimeField(auto_now=True, null=True)
class UserInfo(models.Model):
# id列,自增,主键
# 用户名列,字符串类型,指定长度
username = models.CharField(max_length=32, error_messages={'required': '必填项目必填项目'}, help_text='用户名')
password = models.CharField(max_length=64)
user_type_choices = (
(1, '超级用户'),
(2, '普通用户'),
(3, '只读用户'),
)
user_type_id = models.IntegerField(choices=user_type_choices, default=3)
user_group = models.ForeignKey("UserGroup", to_field='gid', on_delete=models.CASCADE, null=True)
跨表查询举例:
1)views.py中查询语句获取外键的主键值
search_resu = models.UserInfo.objects.filter(username=username).first()
print("%s 的用户组ID:%s" % (username, search_resu.user_group_id))
2) views.py 中查询语句获取外键的表对应数据的其他字段:
通过点外键字段名 点 外键表有的字段即可。(本表单条数据.外键字段名.外表字段名)
search_resu = models.UserInfo.objects.filter(username=username).first()
print("%s 的用户组名:%s" % (username, search_resu.user_group.caption))
#zmd2 的用户组名:运维
#数据 为:
#+-----+---------+----------------------------+----------------------------+
#| gid | caption | ctime | uptime |
#+-----+---------+----------------------------+----------------------------+
#| 3 | 运维 | 2020-07-13 11:41:33.162318 | 2020-07-13 11:41:33.162318 |
#+-----+---------+----------------------------+----------------------------+
3) html 模板中获取有外键对应的外表字段值
models.py
# Create your models here. class Business(models.Model): bid = models.AutoField(primary_key=True) business_name = models.CharField(max_length=128, null=False) code = models.CharField(max_length=32, null=True) class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32, db_index=True) ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='bid', null=True, on_delete=models.SET_NULL)
views.py
hosts = models.Host.objects.all() return render(request, 'app02_host_list.html', {'hosts': hosts})
html 模板中 获取的结果
{% for host in hosts %} <li> {{ host.hostname }} {{ host.ip }} {{ host.port }} {{ host.b.business_name }}</li> {% endfor %}
5.2.4 跨表查询__指定字段 values() value_list() (神奇的双下划线)
过滤跨表字段都用双下滑线
示例:
通过查询主机,获取主机所属业务线名称(主机所属业务线为外键字段)。
models.py 数据结构
class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32, db_index=True) ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business", to_field='bid', null=True, on_delete=models.SET_NULL)
views.py 查询逻辑
from app02 import models host = models.Host.objects.filter(hostname='host1') host_b = host.values('b__business_name') return HttpResponse(host_b)
5.2.5 反向跨表查询,所在表没有ForeiginKey字段
B表某字段外键到A表
A表内使用django提供的隐藏字段b表名__set 查询B表中哪些字段外键了自己主键。
5.3 删除数据 (查询的结果.delete())
返回值:<class 'tuple'> 为元组 示例:(0, {'app01.UserInfo': 0})
第一个元素为删除的元素个数,第二个为删除删除的数据详细信息
删除示例:
class OrmStudy(View): def get(self,request, action, username, pwd): if action == 'delete': search_resu = models.UserInfo.objects.filter(username=username) delete_resu = search_resu.delete() print(type(delete_resu)) print(delete_resu) return HttpResponse(delete_resu)
5.4 更新数据(查询结果.update(field_name='new_value'))
返回值:<class 'int'> 代表更新的数据条数
代码示例
class OrmStudy(View): def get(self,request, action, username, pwd): # ....... elif action == 'update': search_resu = models.UserInfo.objects.filter(username=username) update_resu = search_resu.update(password=pwd) print(type(update_resu)) return HttpResponse(update_resu)
django 后台管理 admin
1、初始化设置
1. 设置密码:>python manage.py createsuperuser
2.app01/admin.py 添加要管理的数据模块:
from app01 import models admin.site.register(models.UserInfo) admin.site.register(models.UserGroup)
2、登录管理后台
http://127.0.0.1:8000/admin
security-scan17.hb.ksyun