Djiango 使用教程

目录

一. 视图层

1.1 HttpResponse,render,redirect

  • HttpResponse
  • render
  • redirect
"""
HttpResponse
返回字符串类型
render
返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
重定向
""" #01 正常写法
def func(repuest):
return HttpResponse('func')
return render(request,'home.html')
return redirect('https://www.baidu.com/')
return redirect('/home/') #重定向本项目 #02 如何不返回则报错
def index(request):
pass #03 结论:视图函数必须要返回一个HttpResponse对象 正确 研究三者的源码即可得处结论
The view app01.views.index didn't return an HttpResponse object. It returned None instead. 视图app01.views.index未返回HttpResponse对象。它返回None。 #04 render简单内部原理
from django.template import Template,Context
res = Template('<h1>{{ user }}</h1>')
con = Context({'user':{'username':'jason','password':123}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)

1.2 JsonResponse

"""
json格式的数据有什么用?
前后端数据交互需要使用到json作为过渡 实现跨语言传输数据 前端序列化
JSON.stringify() json.dumps()
JSON.parse() json.loads()
""" #01 代码 json方式
import json
def ab_json(request):
user_dict = {'username':'张宇宙 fufu','password':123445}
#转为json 字符串 ensure_ascii=False 非ASCII字符直接保存到json格式中 显示中文 不被解析
json_str = json.dumps(user_dict,ensure_ascii=False)
#将字符串返回
return HttpResponse(json_str) #02 代码 JsonResponse方式传值 字典
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'张宇宙 fufu','password':123445}
#JsonResponse 方法 默认只能传字典 json_dumps_params = {'ensure_ascii':False} 显示中文不被解析 return JsonResponse(user_dict,json_dumps_params = {'ensure_ascii':False}) #03 列表JsonResponse方式传值
from django.http import JsonResponse
def ab_json(request):
l = [111,222,444]
# 传列表需要设置 safe=False
return JsonResponse(l, safe=False) ##默认只能序列化字典 序列化其他需要加safe参数

1.3 form表单上传文件及后端保存

"""
form表单上传文件类型的数据
1.method必须指定成post
2.enctype必须换成formdata """ #01 视图层
def ab_file(request):
if request.method == 'POST':
print(request.POST) #只能获取普通的简直对数据 文件不行
print(request.FILES) #获取文件数据 # 获取文件对象 取出 file
# <MultiValueDict: {'file': [<TemporaryUploadedFile: IMG_6543.JPG (image/jpeg)>]}>
file_obj = request.FILES.get('file')
#获取文件名称
print(file_obj.name) #保存文件 ../file_obj.name 路径 path+文件名称 默认在当前html 同级目录
with open(file_obj.name,'wb') as f:
#.chunks() 切片保存,不写也一样
for line in file_obj.chunks():
f.write(line) return render(request,'file.html') #02 前端页面
<body>
<form action="" method="post" enctype="multipart/form-data" >
<p>username:<input type="text" name="username" ></p>
<p>file:<input type="file" name="file"></p>
<input type="submit" >
</form>
</body>

1.4. request 方法

"""

request.method			#获取请求方法
request.POST #获取post请求数据
request.GET #获取get请求
request.FILES #获取文件数据
request.body #原生的浏览器发过来的二进制数据 后面详细的讲
request.path
request.path_info
request.get_full_path() 能过获取完整的url及问号后面的参数 """
print(request.path) # /app01/ab_file/
print(request.path_info) # /app01/ab_file/
print(request.get_full_path()) # /app01/ab_file/?username=jason

二. FBV和CBV

2.1 CBV语法格式

#01 FBV是视图层的函数 以上皆为FBV
def index(request):
return HttpResponse('index') #02 CBV是类函数
1)CBV路由层 MyLogin是类名称
url(r'^login/',views.MyLogin.as_view()), 2)视图层 需要import View模块
from django.views import View
class MyLogin(View):
def get(self,request):
return render(request,"file.html")
def post(self,request):
return HttpResponse("post方法") ##结论 方式该路径时候 CBV会自动判断方法类型 get会走get函数 post会走post函数 """ FBV和CBV各有千秋
CBV特点
能够直接根据请求方式的不同直接匹配到对应的方法执行 内部到底是怎么实现的?
CBV内部源码(******) """

2.2 CBV 实现原理

  • URL classonlymethod装饰方法
  • 反射机制
#01 路由层
url(r'^login/',views.MyLogin.as_view()) 1)as_view代码
@classonlymethod
def as_view(cls, **initkwargs):
"""
cls就是我们自己写的类 MyCBV
Main entry point for a request-response process.
"""
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是我们自己写的类
# self = MyLogin(**initkwargs) 产生一个我们自己写的类的对象
return self.dispatch(request, *args, **kwargs)
"""
以后你们会经常需要看源码 但是在看python源码的时候 一定要时刻提醒自己面向对象属性方法查找顺序
先从对象自己找
再去产生对象的类里面找
之后再去父类找
...
总结:看源码只要看到了self点一个东西 一定要问你自己当前这个self到底是谁 这里调用了 dispatch 函数 (对象--对象类--父类里查找)
"""
return view """ 结论:发现 as_view是闭包函数 并被classonlymethod装饰过 并非返回 view函数名称
url(r'^login/',views.MyLogin.as_view()) 等同于 url(r'^login/',views.view) FBV一模一样 """ #02 dispatch方法
def dispatch(self, request, *args, **kwargs):
"""
获取当前请求的小写格式 然后比对当前请求方式是否合法
get请求为例
post请求
内置8个方法 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
判断当前请求是否 包含在内置8个合法请求内 """
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
"""
反射:通过字符串来操作对象的属性或者方法
handler = getattr(自己写的类产生的对象,'get',当找不到get属性或者方法的时候就会用第三个参数)
handler = 我们自己写的类里面的get方法
"""
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
"""
自动调用get方法
"""

三. 模版层

3.1 模版语法传值

  • {{}} 变量相关
  • {%%} 逻辑相关
#01 视图层 列表字段元祖集合 类对象等传值
def index(request):
n = 123
f = 11.11
s = "我在想付付"
b = True
l = ['付付',123,'小爱同学']
t = ('111','222','333')
d = {'username': '付付','age':18,'info':'此情可待成追忆'}
se = {'嘤嘤嘤','嘻嘻','哈哈哈'} def func():
print('我被执行了')
return '只是当时已惘然' class MyClass(object):
def get_self(self):
return 'self' @staticmethod
def get_func():
return 'func' @classmethod
def get_class(cls):
return 'cls' def __str__(self):
return '嘻嘻嘻嘻' obj = MyClass() return render(request,'index.html',locals()) #02 前端取值 {{ 名称 }}
<p>{{ n }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ b }}</p>
<p>{{ l }}</p>
<p>{{ t }}</p>
<p>{{ d }}</p>
<p>{{ se }}</p>
<p>传递函数名会自动加括号调用 但是模版语法不支持给函数传额外的参数:{{ func }}</p>
<p>传类名的时候也会自动加括号调用(实例化){{ MyClass }}</p>
<p>内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行 针对的是函数名和类名</p>
<p>{{ obj.get_class }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_self }}</p> #注意:对象加点 可以直接调用内部方法运行,前端的结果是 return的值,传对象和类 会自动帮你加括号调用 #03 模版语法取值
1) 视图层
l = ['付付',123,'小爱同学']
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]} 2)前端取值
<p>字典取值:{{ d.username }}</p> #付付
<p>列表取值:{{ l.2 }}</p> #小爱同学
<p>字典取值: {{ d.hobby.2.info }}</p> #love #结论:django模版语法的取值 是固定的格式 只能采用“句点符” 即可以点键也可以点索引 还可以两者混用

3.2 过滤器

#01 语法格式
{{数据|过滤器:参数}} #02 过滤器类型
<p>统计长度:{{ d|length }}</p>
<p>默认值(第一个参数布尔值是True就展示第一个参数的值否在展示冒号后面的值):{{ b|default:'前面是假的 会显示这个' }}</p>
<p>文件大小:{{ filesize | filesizeformat }}</p>
<p>时间格式化(时区相差6小时):{{ current_time | date:'Y-m-d H:i:s' }}</p>
<p>切片操作(支持步长 包含三个点):{{ l|slice:'0:8:2' }}</p>
<p>切取字符串(包含三个点):{{ info|truncatechars:9 }}</p>
<p>切取单词(中英文 默认分隔符是空格 3): {{ info|truncatewords:3 }}</p>
<p>切取单词(中英文 默认分隔符是空格 6): {{ egl |truncatewords:6 }}</p>
<p>移除特定字符(问好):{{ msg|cut:'?' }}</p>
<p>移除特定字符(空格):{{ msg|cut:' ' }}</p>
<p>拼接操作:{{ l|join:'$^' }}</p>
<p>拼接操作(数字相加){{ n|add:10 }}</p>
<p>拼接操作(字符串相加){{ s|add:'msg' }}</p> #03 视图层
n = 123
f = 11.11
s = "我在想付付"
b = True
l = ['付付',123,'小爱同学',333,444,555,666]
t = ('111','222','333')
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]}
se = {'嘤嘤嘤','嘻嘻','哈哈哈'}
info = 'Django 框架系列 目录Django 框架系列一HttpResponse,render,red'
egl = 'my name is zhangyuzhou my age is 18 i am from china'
msg = 'fufu i love you is who?'
n = 100 #04 页面结果

3.3 转义

#01 语法结构 把html js语法转义到页面
{{ 变量名称 |safe }} #03 后端转义
hhh = '<h2>付付</h2>'
sss = '<script>alert(123)</script>' from django.utils.safestring import mark_safe
res = mark_safe('<h2>付付2.0</h2>') #使用mark_safe方法 之前传值给前端即可 #02 前端转义 safe
<p>转义html:{{ hhh|safe }}</p>
<p>转义js:{{ sss|safe }}</p>
<p>后端转义:{{ res }}</p>

3.4 标签逻辑系列

1)for循环

#01 语法结构( for + table键 自动补全)
{% for name in l %}
<p>{{ name }}</p> #循环的元素
{% endfor %} #02 for循环内置模版变量
{% for foo in l %}
<p>{{ forloop }}</p>
{% endfor %} {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 7, 'revcounter0': 6, 'first': True, 'last': False} """
forloop.counter 知识for循环已经循环了多少次,从 1 开始计数
forloop.counter() 和forloop.counter一样,只是从 0 开始计数
forloop.revcounter 从倒数开始数,循环的次数,从 1 开始计数
forloop.revcounter()从倒数开始数,循环的次数,从 0 开始计数
forloop.first 是否是第一次循环,如果是返回 True 。否则返回 False
forloop.last 是否是最后一次循环,如果是返回 True 。否则返回 False
forloop.parentloop 嵌套循环时,是一个指向当前循环的上一级循环的forloop对象的引用。如 forloop.parentloop.counter 表示上一级循环循环了多少次 """

2)if判断

#01 语法格式(if+tab 自动补全)

{% if b %}						#判断是否为真
<p>baby</p>
{% elif s %} #判断
<p>爱了</p>
{% else %} #否侧
<p>否侧你完了</p>
{% endif %} #02 视图层数据
s = "我在想付付"
b = False #03 结果
爱了

3)if+for组合

#01 语法结构
l = ['付付','付付2.0','小爱同学','付付3.0',444,555,666] {% for foo in l %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次了</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% empty %}
<p>for循环的可迭代对象内部没有元素,无法循环</p>
{% endfor %} #解释下:empty for循环的可迭代对象内部没有元素,无法循环则会打印这个 ###循环字典方法
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]} #03 默认循环的是 d.keys
{% for foo in d %}
<p>{{ foo }}</p>
{% endfor %}
<hr> #04 循环字典的key
{% for key in d.keys %}
<p>{{ key }}</p>
{% endfor %} #05 循环字典的值
{% for value in d.values %}
<p>{{ value }}</p>
{% endfor %} #06循环字典的k+v
{% for item in d.items %}
<p>{{ item }}</p>
{% endfor %}

4)with别名

#01 语法结构 给love起别名(with+tab)
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]} {% with d.hobby.2.info as fufu %}
<p>{{ fufu }}</p>
{% endwith %}

3.5 自定义标签,过滤器,inclusion_tag

#01 自定义过滤器(最多两个参数)
1)定义
"""
先三步走
1.在应用下创建一个名字”必须“叫templatetags文件夹
2.在该文件夹内创建“任意”名称的py文件 eg:mytag.py
3.在该py文件内"必须"先书写下面两句话(单词一个都不能错)
from django import template
register = template.Library()
""" 2)语法格式
from django import template
register = template.Library() 3)案例数字相加
@register.filter(name='fu')
def my_sum(v1, v2):
return v1 + v2 4)示例:将文本转换为大写
@register.filter(name='my_filter')
def my_filter(value):
return value.upper() 5)前端使用
{% load mytag %}
<p>{{ n |fu:666 }}</p>
<p>{{ 1000 |fu:666 }}</p>
<p>{{ "hello fuuf" | my_filter }}</p> #02 自定义标签(参数可以有多个)
1) 后端
@register.simple_tag(name='puls')
def puls(a,s,d,f):
return '%s-%s-%s-%s'%(a,s,d,f) 2)前端
{% load mytag %}
{% puls 'fufu' 123 'love' 334 %} 3)结果
fufu-123-love-334 #03 自定义inclusion_tag装饰器
1) 后端
@register.inclusion_tag('left.html') #作用html页面 left
def left(n):
data = [f'第{i}项' for i in range(n)]
return locals() 2)前端作用页面
<ul>
{% for foo in data %} # 接收data 并循环
<li>{{ foo }}</li>
{% endfor %}
</ul> 3)调用
{% load mytag %}
{% left 10 %}
  • Inclusion_tag

3.6 模版的继承


"""
# 模版的继承 你自己先选好一个你要想继承的模版页面
{% extends 'home.html' %} # 继承了之后子页面跟模版页面长的是一模一样的 你需要在模版页面上提前划定可以被修改的区域
{% block content %}
模版内容
{% endblock %} # 子页面就可以声明想要修改哪块划定了的区域
{% block content %}
子页面内容
{% endblock %} """ #01 案例书写个网站页面 要求导航条和侧边栏保持不动 主题内容根据功能改变 #02 路由层
url(r'^home/',views.home),
url(r'^loginu/', views.loginu),
url(r'^reg/', views.reg), #03 视图层
def home(request):
return render(request,'home.html')
def loginu(request):
return render(request,'loginu.html')
def reg(request):
return render(request,'reg.html') #04 html文件 给需要修改的区域添加 标签 block confufu {% block css %} {% endblock %} {% block confufu %}
<div class="panel-body">
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
</div>
{% endblock %} {% block js %} {% endblock %} #05 模版的继承
{% extends 'home.html' %} {% block confufu %}
<h1>登入页面</h1>
<form action="">
<p>username: <input type="text" class="form-control"></p>
<p>password: <input type="text" class="form-control"></p>
<input type="submit" class="btn btn-success">
</form>
{% endblock %} #06 一般情况下模版页面上应该至少有三块可以被修改的区域
1.css区域
2.html区域
3.js区域
{% block css %} {% endblock %} {% block content %} {% endblock %} {% block js %} {% endblock %}
# 每一个子页面就都可以有自己独有的css代码 html代码 js代码 -------案例------ {% extends 'home.html' %} {% block css %}
<style>
.h {
color: #31708f;
}
</style>
{% endblock %} {% block confufu %}
<h1 class="h">注册页面</h1>
<form action="">
<p>username: <input type="text" class="form-control"></p>
<p>password: <input type="text" class="form-control"></p>
<input type="submit" class="btn btn-danger">
</form>
{% include 'love.html' %}
{% endblock %} {% block js %}
<script>
alert(1234)
</script>
{% endblock %}
  • 图示

3.7 模版的导入

"""
将页面的某一个局部当成模块的形式
哪个地方需要就可以直接导入使用即可
"""
{% include 'wasai.html' %}

四. 模型层-增删改查

4.1 连接数据库

#01 修改配置文件 settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day64',
'HOST': '192.168.5.9',
'USER': 'root',
'PASSWORD': '0x00NF2001',
'POST': 3306,
'CHARSET': 'utf8'
}
} #02 主项目下 __init__文件内声明该模块
import pymysql
pymysql.install_as_MySQLdb() #03 配置文件templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')] ##其它方式连接信息(和本代码无关 只单纯记录)
conn = pymysql.connect(
user='root',
host='192.168.5.9',
password='0x00NF2001',
port=3306,
database='note',
charset='utf8',
autocommit=True
)

4.2 创建表&准备测试环境

#01 创建表
class User(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
register_time = models.DateField() #年月日
uptime = models.DateTimeField(auto_now=True) #年月日时分秒 """
DateTimeField 年月日 时分秒
DateField 年月日
两个重要参数
auto_now: 每次操作数据的时候 该字段会自动将当前时间更新
auto_now_add: 在创建数据的时候会自动将当前时间记录下来 之后只要不人为修改 那么一直不变 """ #02 运行manage任务 创建表
makemigrations
migrate #03 准备测试环境 tests.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
import django
django.setup() #下方可以写Djiango 查询语句,需要查什么模版 导入即可
from app01 import models
models.User.objects.all() #查询 """
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可 脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
"""

4.3 单表增删改

#01 增加数据
#1.1 方式1
models.User.objects.create(name='付付',age='18',register_time='2000-11-06') #1.2 方式2
import datetime
ctime = datetime.datetime.now()
obj = models.User(name='zhangyuzhou',age='20',register_time=ctime) #支持变量方式
print(obj)
obj.save() #02 删除数据
#2.1 方式1
models.User.objects.filter(pk=2).delete()
models.User.objects.filter(age=18).delete() """
pk=当前表的主键值
pk会自动查找到当前表的主键字段 指代的就是当前表的主键字段 filter(过滤条件) """ #2.2 方式2 获取当前数据对象
user_obj = models.User.objects.filter(pk=6).first()
print(user_obj)
user_obj.delete() #03 修改数据
#3.1 方式1
res = models.User.objects.filter(pk=5).update(name='love') #3.2 方式2
user_obj = models.User.objects.filter(pk=5).first()
user_obj.name = '爱情'
user_obj.save() #3.3 方式3
user_obj = models.User.objects.get(pk=5)
user_obj.age = 19
user_obj.save() """
get方法返回的直接就是当前数据对象
但是该方法不推荐使用
一旦数据不存在该方法会直接报错
而filter则不会
所以我们还是用filter
"""

4.4 单表操作必会13条

#01 all() 查询所有数据
res = models.User.objects.all()
print(res) #02 filte() 带有过滤条件的查询
res = models.User.objects.filter(pk=5).first()
print(res) #03 get() 直接拿数据对象 但是条件不存在直接报错
user_obj = models.User.objects.get(pk=5)
user_obj.age = 19
user_obj.save() #04 拿queryset里面第一个元素
res = models.User.objects.all().first()
print(res) #05 last() 拿queryset里面最后第一个元素
res = models.User.objects.all().last()
print(res) #06 values() 可以指定获取的数据字段 select name,age from ... 列表套字典
res = models.User.objects.values('name','age')
print(res)
print(res.query) #查看SQL语句 只针对QuerySet对象 <QuerySet [{'name': 'zhangyuzhou', 'age': 20}, {'name': '爱情', 'age': 19}]> #07 values_list() 列表套元祖
res = models.User.objects.values_list('name','age')
print(res) <QuerySet [('zhangyuzhou', 20), ('爱情', 19), ('付付', 18), ('汤姆', 33)]> #08 distinct() 去重 并非从表内删除
res = models.User.objects.values('name','age').distinct()
print(res) """
去重一定要是一模一样的数据
如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键 """ #09 order_by() 排序
res = models.User.objects.order_by('age') #正序
res_re = models.User.objects.order_by('-age') #倒序
print(res)
print(res_re) #10 reverse() 反转的前提是 数据已经排过序了 order_by()
res = models.User.objects.order_by('age').reverse()
print(res) #11 count() 统计当前数据个数 可以加条件filter()
res = models.User.objects.filter(name='付付').count()
res = models.User.objects.count()
print(res) #12 排除 exclude() 排除在外
res = models.User.objects.exclude(name='付付')
print(res) #13. exists() 基本用不到因为数据本身就自带布尔值 返回的是布尔值
res = models.User.objects.filter(pk=1).exists()
print(res)

4.5 查看内部SQL语句

#01 方式一 queryset对象才能够点击query查看内部的sql语句
res = models.User.objects.values('name','age')
print(res) 结果如下:
<QuerySet [{'name': 'zhangyuzhou', 'age': 20}, {'name': '爱情', 'age': 19}]> print(res.query) #查看SQL语句 只针对QuerySet对象 结果如下:
SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` #02 修改配置文件 匹配所有查询SQL语句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
} 查询会自动打印SQL语句
res = models.User.objects.values('name','age')
print(res) 结果如下:
(0.005) SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` LIMIT 21; args=()

4.6 双下划线查询

#01 年龄大于20
res = models.User.objects.filter(age__gt=20)
print(res) #02 年龄大于等于20
res = models.User.objects.filter(age__gte=20)
print(res) #03 年龄小于20
res = models.User.objects.filter(age__lt=20)
print(res) #04 年龄小于等于20 并且name等于付付的
res = models.User.objects.filter(age__lte=20,name='付付')
print(res) #05 年龄是 16或者19 或者33的 (在列表) 并按照年龄排序
res = models.User.objects.filter(age__in=[16,19,33]).order_by('age')
print(res) #06 年龄在18到33之间的 并且只取name,age字段 首尾都要
res = models.User.objects.filter(age__range=[18,33]).values('name','age') #07 查出名字内包含 F的
res = models.User.objects.filter(name__contains='F') #__contains 区分大小写
res1 = models.User.objects.filter(name__icontains='F') #__icontains 忽略大小写 #08 查询以什么开头或者结尾的
res = models.User.objects.filter(name__istartswith='F') #__startswith 区分大小写 查询以什么开头
res1 = models.User.objects.filter(name__startswith='F') #__istartswith 忽略大小写 查询以什么开头 res = models.User.objects.filter(name__endswith='U') #__endswith 区分大小写 查询以什么结尾
res1 = models.User.objects.filter(name__iendswith='U') #__endswith 忽略大小写 查询以什么结尾 #09 日期查询 查询出注册时间是 2000 11月
res = models.User.objects.filter(register_time__month=11,register_time__year=2000)
print(res) """
register_time__month = 月
register_time__year = 年
register_time__day = 天
"""

4.7 多表操作&环境创建

#图书表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
publish_data = models.DateField(auto_now_add=True) #书籍和出版社关系表 一对多
publish = models.ForeignKey(to='Publish') #多对多 书籍和作者 多个书籍可以有多个作者
authors = models.ManyToManyField(to='Author') #出版社
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField() #varchar(254) 该字段不是给model看的 而是用于校验组件 #作者表
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField() #一对一 作者对应作者详情
author_dateil = models.OneToOneField(to='AuthorDetail') #作者详情表
class AuthorDetail(models.Model):
phone = models.BigIntegerField() #电话号码
addr = models.CharField(max_length=64) ##运行语句
makemigrations
migrate

4.8 一对多外键增删改查

#01 增加数据 书籍名称 价格 对应的出版社
#1.1 方式1 直接写出版社 ID主键
models.Book.objects.create(title='论语',price=899.11,publish_id=1)
models.Book.objects.create(title='大学',price=799.22,publish_id=2)
models.Book.objects.create(title='中庸',price=699.33,publish_id=1) #1.2 方式2 虚拟字段对象, 先查出出版社数据对象 然后在书籍表内添加
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='红楼梦',price=19.98,publish=publish_obj) #02 删除操作 删除出版社id=1的数据
models.Book.objects.filter(publish_id=1).delete() #03 修改数据
# 3.1 方式一 修改主键是12的书籍表 修改内容:publish_id=2
models.Book.objects.filter(pk=12).update(publish_id=2) # 3.2 方式二 查出出版社对象 然后在书籍表内更新
obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=13).update(publish=obj)

4.9 多对多外键增删改查

#01 通过虚拟字段获取第三张表的数据对象
book_obj = models.Book.objects.filter(pk=12).first()
print(book_obj.authors) #就类似于你已经到了第三张关系表了 #02 给书籍增加作者 无则添加,有则不动
book_obj.authors.add(1) #直接写ID方式
book_obj.authors.add(1,2) authors_obj = models.Author.objects.filter(pk=1).first() #虚拟对象方式
authors_obj1 = models.Author.objects.filter(pk=2).first()
authors_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(authors_obj)
book_obj.authors.add(authors_obj1,authors_obj2) """
add给第三张关系表添加数据
括号内既可以传数字也可以传对象 并且都支持多个
""" #03 删除 书籍表内作者ID等于1的 数据
book_obj.authors.remove(1)
book_obj.authors.remove(2,3) # 删除书籍ID=12 对着作者ID=2的
# 获取作者ID
author_obj = models.Author.objects.filter(pk=2).first() # 调用方法 在书籍表内删除对应ID是2的作者
book_obj.authors.remove(author_obj) """
remove
括号内既可以传数字也可以传对象 并且都支持多个
""" #04 修改 修改书籍表ID是12的关系表 对应作者ID是 1和1 ,有则修改 无则添加 多则删除
book_obj.authors.set([1,2])
book_obj.authors.set([3]) author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj,author_obj1]) # 括号内必须给一个可迭代对象 """
set
括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
""" #05 清空 在第三张关系表中清空某个书籍与作者的绑定关系
book_obj.authors.clear() """
clear
括号内不要加任何参数 """

4.10 多表查询 正向和反向概念

# 正向
# 反向
外键字段在我手上那么,我查你就是正向
外键字段如果不在手上,我查你就是反向 book >>>外键字段在书那儿(正向)>>> publish
publish >>>外键字段在书那儿(反向)>>>book 一对一和多对多正反向的判断也是如此 """
正向查询按字段
反向查询按表名小写
_set
...
"""

4.11 子查询(基于对象的跨表查询)

"""
在书写orm语句的时候跟写sql语句一样的
不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点 正向什么时候需要加.all()
当你的结果可能有多个的时候就需要加.all()
如果是一个则直接拿到数据对象
book_obj.publish
book_obj.authors.all()
author_obj.author_detail
""" ------------正向查询按照外键字段--------------- #01 查询书籍主键为11的出版社 正向查询
book_obj = models.Book.objects.filter(pk=11).first()
res = book_obj.publish
print(res) #02 查询书籍主键为12的作者
# 这里结果有多个 需要加all()
book_obj = models.Book.objects.filter(pk=12).first()
res = book_obj.authors.all()
for i in res:
print(i.name) #03 查询作者 fufu 的电话号码
"""
author_obj.name 作者名称
res.phone 电话号码
"""
author_obj = models.Author.objects.filter(name='fufu').first()
res = author_obj.author_dateil #外键字段 author_dateil
print(author_obj.name,res.phone) ------------反向查询按照表名--------------- ##反向查询
#01 查询出版社是东方出版社的书
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all() #这里用出版社对象.书籍表名_set.all()
print(res) #结果是多个 需要在表名称后加_set.all() #02 查询fufu作者 写过的书
author_obj = models.Author.objects.filter(name='fufu').first()
res = author_obj.book_set.all()
for i in res:
print(author_obj.name,i.title) #03 查询手机号是1314的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=1314).first()
res = author_detail_obj.author #这里结果只有一个 所以不需要加 _set.all()
print(res.name) """
基于对象
反向查询的时候
当你的查询结果可以有多个的时候 就必须加_set.all()
当你的结果只有一个的时候 不需要加_set.all()
"""

4.12 联表查询(基于双下划线的跨表查询)


------------------正向查询基于字段---------------------- # 01 查询fufu的手机号和 作者姓名 地址(正向查询 作者表-->>作者详情表)
res = models.Author.objects.filter(name='fufu').values(
'author_dateil__addr',
'author_dateil__phone',
'name')
print(res) #02 查询书籍名称是红楼梦的 出版社姓名和作者
"""
用书籍表去查询 出版社表和作者表
"""
res = models.Book.objects.filter(title='红楼梦').values('publish__name','authors__name','title')
print(res) #03 查询书籍主键是12的 书籍名称 作者姓名和电话
res = models.Book.objects.filter(pk=12).values(
'title',
'authors__name',
'authors__author_dateil__phone') ------------------反向查询基于表名---------------------- #01 查询手机号是 1314的作者
"""
根据作者详情反向查询作者
反向查询 写表名称
"""
res = models.AuthorDetail.objects.filter(phone=1314).values(
'author__name','phone')
print(res) #02 出版社发布过红楼梦的 出版社名称和书的名称 及书的作者
"""
根据出版社表 反向查询 书籍表和作者表
反向查询 写表名称
"""
res = models.Publish.objects.filter(book__title='红楼梦').values(
'name','book__title','book__authors__name')
print(res) #03 查询书籍主键为12的作者姓名和地址
res = models.Author.objects.filter(book__id=12).values(
'book__title','name','author_dateil__addr')
print(res) #04 查询书籍主键为11的作者姓名和地址 book主主要查询
"""
book关联作者表
作者表关联作者详情
authors__author_dateil__phone
"""
res = models.Book.objects.filter(pk=12).values(
'authors__author_dateil__phone','authors__name','title')
print(res)

五. 模型层-聚合函数

5.1 聚合查询

  • 单独查询需要使用:aggregate
  • Max,Min,Count,Avg,Sum
## 导入聚合模块
from django.db.models import Max,Min,Count,Avg,Sum
from app01 import models
"""
聚合查询通常情况下都是配合分组一起使用的
只要是跟数据库相关的模块
基本上都在django.db.models里面
如果上述没有那么应该在django.db里面
""" #01 统计书籍的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res) #02 统计所有书籍 最大值 最小值 个数 平均值 求和
res = models.Book.objects.aggregate(
Avg('price'),Min('price'),Max('price'),
Sum('price'),Count('price'))
print(res) {'price__avg': 604.41, 'price__min': Decimal('19.98'), 'price__max': Decimal('899.11'), 'price__sum': Decimal('2417.64'), 'price__count': 4}

5.2 分组查询

  • annotate
##聚合查询 导入功能模块
from django.db.models import Max,Min,Count,Avg,Sum #01 统计每一本书的作者数量
#res = models.Book.objects.annotate() # models后面点什么 就是按什么分组
res = models.Book.objects.annotate(authors_num=Count('authors')).values('title','authors_num')
print(res) """
authors_num 是我们自己定义的字段 用接收聚合的结果
authors 书籍表的外键字段 对应的是作者表
values 取值展示 """ #02 统计每个出版社卖的最便宜的书的价格, 每个出版社 最便宜的书价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res) #03 找出图书的作者是多个的
"""
先找出多个作者的书籍
然后进行过滤 找出大于1的 只要你的orm语句得出的结果还是一个queryset对象
那么它就可以继续无限制的点queryset对象封装的方法 """
res = models.Book.objects.annotate(count_author=Count('authors')).filter(count_author__gt=1).values(
'title','count_author'
)
print(res) #04 查询每个作者出的书的 总价格(作者名称 书籍价格)
res= models.Author.objects.annotate(sum_book=Sum('book__price')).values('name','sum_book','count_book')
print(res) """
如果我想按照指定的字段分组该如何处理呢?
models.Book.objects.values('price').annotate()
后续BBS作业会使用 你们的机器上如果出现分组查询报错的情况
你需要修改数据库严格模式
"""

5.3 F和Q查询

  • F查询
## F 查询需要先倒入功能模块

    """
能够帮助你直接获取到表中某个字段对应的数据
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
""" ##字段数据准备
#图书表
class Book(models.Model):
# 库存
kucun = models.IntegerField(default=1000)
# 卖出
maichu = models.IntegerField(default=100) def __str__(self):
return self.title #01 查询卖出大于库存的书籍
from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res) #02 将书籍的价格提升500块
res = models.Book.objects.update(price=F('price') + 500) #03 将所有书名称加上爆款两个字
"""
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
需要使用
from django.db.models.functions import Concat
from django.db.models import Value """
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(title=Concat(F('title'),Value('爆款')))
  • Q查询
## Q查询 导入功能模块
from django.db.models import Q #01 查询书籍卖出数量大于100 或者小于600的
# res = models.Book.objects.filter(Q(price__gt=100),Q(price__lt=600)) # , 默认是 and关系 与
# res = models.Book.objects.filter(Q(price__gt=100)|Q(price__lt=600)) # | 是或者 or 关系 或
# res = models.Book.objects.filter(~Q(price__gt=100)|Q(price__lt=600)) # ~ NOT 前面条件取反 非
print(res) #02 Q的高阶用法 能够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or' #默认是 default = AND 并且关系
q.children.append(('maichu__gt',100))
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q)
print(res)

5.4 djiango中开启事务

##事务的特点
"""
事务
ACID
原子性
不可分割的最小单位
一致性
跟原子性是相辅相成
隔离性
事务之间互相不干扰
持久性
事务一旦确认永久生效 msyql事务的回滚
rollback
msyql事务的确认
commit
""" #开启事务 需要导入功能模块 transaction
from django.db import transaction
try:
with transaction.atomic():
"""
sql1
sql2
在with代码快内书写的所有orm操作都是属于同一个事务
"""
pass
except Exception as e:
print(e)
print('执行其他操作')

5.5 orm中常用字段及参数

AutoField
主键字段 primary_key=True CharField varchar
verbose_name 字段的注释
max_length 长度 IntegerField int
BigIntegerField bigint DecimalField
max_digits=8
decimal_places=2 EmailFiled varchar(254) DateField date
DateTimeField datetime
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了 BooleanField(Field) - 布尔值类型
该字段传布尔值(False/True) 数据库里面存0/1 TextField(Field) - 文本类型
该字段可以用来存大段内容(文章、博客...) 没有字数限制
后面的bbs作业 文章字段用的就是TextField FileField(Field) - 字符类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
/data/a.txt
后面bbs作业也会涉及 # 更多字段
直接参考博客: https://www.cnblogs.com/Dominic-Ji/p/9203990.html # django除了给你提供了很多字段类型之外 还支持你自定义字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
# 调用父类的init方法
super().__init__(max_length=max_length,*args,**kwargs) # 一定要是关键字的形式传入 def db_type(self, connection):
"""
返回真正的数据类型及各种约束条件
:param connection:
:return:
"""
return 'char(%s)'%self.max_length # 自定义字段使用
myfield = MyCharField(max_length=16,null=True) # 外键字段及参数
unique=True
ForeignKey(unique=True) === OneToOneField()
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用 db_index
如果db_index=True 则代表着为此字段设置索引
(复习索引是什么) to_field
设置要关联的表的字段 默认不写关联的就是另外一张的主键字段 on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
"""
django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
"""

六. 数据库查询优化

6.1 单表查询优化

  • only与defer
"""
only与defer
select_related与prefetch_related orm语句的特点:
惰性查询
如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数
那么orm会自动识别 直接不执行 """ #获取书籍表的所有书的名称
#01 正常写法
res = models.Book.objects.values('title')
for i in res:
print(i.get('title')) res = models.Book.objects.all()
print(res)
for i in res:
print(i.title) #all不需要走了 #02 only写法
res = models.Book.objects.only('title') #得到的是个对象
for i in res:
print(i.title) #点击only括号内的字段 不会走数据库
print(i.price) #点击only括号内没有的字段 会重新走数据库查询而all不需要走了 #03 defer 写法
res = models.Book.objects.defer('title')
for i in res:
print(i.title)
print(i.price)
"""
defer与only刚好相反
defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据
而如果查询的是非括号内的字段 则不需要走数据库了
"""

6.2 连表查询优化

  • select_related与prefetch_related
"""
select_related与prefetch_related
"""
## 查询书籍的出版社名称
#01 正常写法
res = models.Book.objects.all()
for i in res:
print(i.publish.name) #all() 每循环一次都会走一次数据库查询 #02 select_related 连表查询 写法
res = models.Book.objects.select_related('publish') #INNER JOIN 连表查询 只会查询一次数据库
for i in res:
print(i.publish.name) """
select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据
全部封装给查询出来的对象
这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了
select_related括号内只能放外键字段 一对多 一对一
多对多也不行
""" #03 prefetch_related 子查询
res = models.Book.objects.prefetch_related('publish')
for i in res:
print(i.publish.name) """
prefetch_related该方法内部其实就是子查询
将子查询查询出来的所有结果也给你封装到对象中
给你的感觉好像也是一次性搞定的
"""

七. choices参数

7.1 数据库字段设计常见

"""
用户表
性别
学历
工作经验
是否结婚
是否生子
客户来源
...
针对某个可以列举完全的可能性字段,我们应该如何存储 只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数

7.2 创建环境

#01 创建表
from django.db import models
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
#性别元祖
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其它'),
)
gender = models.IntegerField(choices=gender_choices)
"""
IntegerField 如果元素第一个是数字 则使用数字字段 该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
那么可以非常轻松的获取到数字对应的真正的内容 1.gender字段存的数字不在上述元祖列举的范围内容
2.如果在 如何获取对应的中文信息 """ #评级 字符串字段 保证字段类型跟列举出来的元祖第一个数据类型一致即可
score_choices = (
('A','优秀'),
('B','良好'),
('c','及格'),
('B','不合格'),
)
score = models.CharField(choices=score_choices,null=True,max_length=32) makemigrations
migrate #02 tests文件 测试数据
from django.test import TestCase import os
import sys if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day66.settings")
import django
django.setup()
from app01 import models

7.3 取choices数据

"""
只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display() 如果没有对应关系 那么字段是什么还是展示什么
"""
#01 创建数据
models.User.objects.create(username='付付',age=18,gender=2)
models.User.objects.create(username='宇宙',age=19,gender=1)
models.User.objects.create(username='东方不败',age=100,gender=3)
models.User.objects.create(username='金木',age=90,gender=4) #02 获取choices 数据
obj = models.User.objects.filter(gender=1).first()
print(obj.get_gender_display()) #03 如果没有对应关系 那么字段是什么还是展示什么
obj = models.User.objects.filter(gender=4).first()
print(obj.get_gender_display()) ##04 添加数据
models.User.objects.filter(gender=1).update(score='A')
models.User.objects.filter(gender=2).update(score='B')
models.User.objects.filter(gender=3).update(score='C')
models.User.objects.filter(gender=4).update(score='D') #03 取数据
obj = models.User.objects.filter(score='A').first()
print(obj.get_score_display()) 男
4
优秀

八. MTV与MVC模型

# MTV:Django号称是MTV模型
M:models
T:templates
V:views
# MVC:其实django本质也是MVC
M:models
V:views
C:controller # vue框架:MVVM模型

九. 多对多三种创建方式



Djiango视图层和模型层的更多相关文章

  1. 再探 Ext JS 6 (sencha touch/ext升级版) 变化篇 (编译命令、滚动条、控制层、模型层、路由)

    从sencha touch 2.4.2升级到ext js 6,cmd版本升级到6.0之后发生了很多变化 首先从cmd说起,cmd 6 中sencha app build package不能使用了,se ...

  2. Django的日常-模型层(1)

    目录 Django的日常-模型层(1) 模型层 django测试环境 ORM查询 Django的日常-模型层(1) 模型层 模型层其实就是我们应用名下的models.py文件,我们在里面写入想要创建的 ...

  3. RGBD动作识别的多视图层融合模型

    摘要 基于视觉的动作识别在实践中遇到了不同的挑战,包括从任何角度识别主题,实时处理数据以及在现实环境中提供隐私.甚至识别基于配置文件的人类动作(基于视觉的动作识别的一个子集),在计算机视觉中也是一个巨 ...

  4. 表现层(jsp)、持久层(类似dao)、业务层(逻辑层、service层)、模型(javabean)、控制层(action)

    转自:http://www.blogjava.net/jiabao/archive/2007/04/08/109189.html 为了实现web层(struts)和持久层(Hibernate)之间的松 ...

  5. 64、django之模型层(model)--建表、查询、删除基础

    要说一个项目最重要的部分是什么那铁定数据了,也就是数据库,这篇就开始带大家走进django关于模型层model的使用,model主要就是操纵数据库不使用sql语句的情况下完成数据库的增删改查.本篇仅带 ...

  6. django之模型层(model)--建表、查询、删除基础

    要说一个项目最重要的部分是什么那铁定数据了,也就是数据库,这篇就开始带大家走进django关于模型层model的使用,model主要就是操纵数据库不使用sql语句的情况下完成数据库的增删改查.本篇仅带 ...

  7. 第一章:模型层model layer -- Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 题外话: Django的教程写到这里,就进入 ...

  8. python 全栈开发,Day70(模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介)

    昨日内容回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...

  9. 模型层model layer

    题外话: Django的教程写到这里,就进入了整体的第二部分,也是最关键的部分.此时有一个问题必须想清楚,那就是,以项目带动内容还是以参考书目的方式展开?为此,我考虑了很久. 我在开始学习Django ...

  10. 模型层(template)

    错误之forbbiddon csrf_token:这个标签用于跨站请求伪造保护 提交数据的时候就会做安全机制,当你点击提交的时候会出现一个forbbiddon 的错误,就是用setting配置里的sc ...

随机推荐

  1. conda创建虚拟环境后文件夹中只有conda-meta文件夹,无法将环境添加到IDE中

    1.问题描述:anaconda的envs的其中一个环境目录下,没有python.exe文件,只有conda-meta和scripts 平时创建虚拟环境都是: conda create -n test ...

  2. C++ CryptoPP使用AES加解密

    Crypto++ (CryptoPP) 是一个用于密码学和加密的 C++ 库.它是一个开源项目,提供了大量的密码学算法和功能,包括对称加密.非对称加密.哈希函数.消息认证码 (MAC).数字签名等.C ...

  3. 驱动开发:应用DeviceIoContro开发模板

    内核中执行代码后需要将结果动态显示给应用层的用户,DeviceIoControl 是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数,如下代码是一个经典的驱动开发模板框架, ...

  4. maven打包报错

    * 系统:macOS* 开发工具:Idea* 问题描述:在idea中执行mvn clean install时报No compiler is provided in this environment. ...

  5. GD库常用实例

      GD库常用实例 一.图片水印 1.实现步骤 打开原图(也叫操作的目标图片) 打开水印图(也叫水印来源图片) 使用 imagecopymerge 将小图合并至大图的指定位置 输出图片 销毁资源 2. ...

  6. 【路由器】电信光猫中兴 F7010C 折腾记录

    目录 问题描述 解锁超管密码 前言 配置安卓抓包环境 抓包获取超管密码 IPv6 配置 光猫拨号 改用 SLAAC 路由器配置 wan6 配置 wan 配置 lan 配置 验证 参考资料 问题描述 近 ...

  7. 部署19c ADG过程中的问题处理

    回忆起来也是有些年没亲自动手搭建ADG了,今天正好有个机会重温,客户环境是19.16,恍惚记得上一次搭ADG还是在11.2.0.4的时代,时光荏苒啊. 正好看下19c的ADG和11g的ADG在部署方面 ...

  8. yapi的安装

    1.官方网站:https://hellosean1025.github.io 2.看官方的文档,部署方法: 3. 根据文档 安装环境先: 4. 开始安装yapi 5. 可见需要启动一下 6.重启一下 ...

  9. 【开工大吉】推荐4款开源、美观的WPF UI组件库

    前言 经常有小伙伴在技术群里提问:WPF有什么好用的UI组件库?,今天大姚给大家推荐4款开源.美观的WPF UI组件库. WPF介绍 WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 ...

  10. SATA学习笔记——Link Layer 加扰/解扰/CRC

    一.故事前传 我们之前说到Link layer的结构,link layer的作用大致可以包括以下几点: Frame flow control CRC的生成与检测 对数据与控制字符的Scrmable/D ...