Django生产迁移和执行迁移的注意事项
第一次生成和迁移和执行迁移之后,数据库中会有一部分django自带的表,而我们每次迁移完成后,都有对应的迁移记录。如果你在想把迁移之后的表删除,在重新执行迁移,需要先在django_migratios这张表中删除对应的迁移记录,才可以再次迁移这张表。
在Django自带'django_user'这张表中保存着在控制台创建的超级管理员的账号信息。Django默认会将会话的数据写到表’django_session'表中。
模糊查询和多条件查询
参数名 | 含义 |
---|---|
__exact | 表示精确查询 |
__contains | 表示包含关系,相当于SQL中' %王%' |
__startswith | 表示以什么什么开头,相当于SQL中' 王%' |
__endswith | 表示以什么什么结尾,相当于SQL中' %王' |
queryset = Record.objects.filter(car__carno__istarswith=carno)
# 查询在Recor模型的模型管理器(objects)里面查询car对象的carno属性(忽略大小写、以什么开头)的记录,返回一个查询集。
在Django中有个Q对象,可以用于多条件查询, 可以使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数 。
from django.db.models import Q
# | 表示‘或者’的意思
# & 表示’并且‘的意思
# ~ 表示‘非’的意思
#例如:
Q(car__carno__startswith='川A') | Q(car__owner__contains='王')
相当于SQL语句:
# select * from tb1 where carno like '川A%' or owner like '%王%';这样的查询语句。
# 车牌或者车主姓名模糊查询查询
queryset = Record.objects.filter(
Q(car__carno__istarswith=carno) |
Q(car__owner__contains=carno)
)
分页器的使用
有的时候我们在页面中展示大量的数据,这样不仅给项目测试增加负担,也会为降低用户的体验感,而Django为我们提供了一个Paginator类来帮助我们管理分页数据。
分页器对象的属性:
属性 | 描述 |
---|---|
object_list | 查询到的数据(列表) |
per_page | 每一页要展示的内容 |
orphans=0 | 这是一个缺省参数,如果最后一页的数据小于这个值,会合并到上一页 |
allow_empty_first_page=True | 允许首页为空,默认为True |
分页器对象的方法
方法 | 描述 |
---|---|
page | 返回一个Page对象 |
count | 返回对象列表(数据)的长度 |
num_pages | 返回总页数 |
page_range | 返回页码列表 |
Page对象
Page对象一般用来指定当前页
注意:Paginator对象是由我们进行示例化的,而Page对象在Paginator对象使用page方法时实现
Page对象的方法:
属性 | 描述 |
---|---|
object_list | 指定对象列表,包含当前页的对象 |
number | 当前页面的页码 |
paginator | 指对应的分页器对象 |
Page对象的属性
方法 | 描述 |
---|---|
has_next | 是否有下一页 |
has_previous | 是否有上一页 |
has_other_pages | 是否有上一页或下一页 |
next_page_number | 返回下一页的页码 |
previous_page_number | 返回上一页的页码 |
start_index | 返回当前页起始的对象序号 |
end_index | 返回当前页结束的对象序号 |
from django.core.paginator import Paginator
from django.db import DatabaseError
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render
from carsearch.models import Record
def search(request):
"""查询"""
context = {
'searched': False,
'current_page': 1,
'total_page': 0
}
if request.method == 'POST':
carno = request.POST.get('carno', '')
carno = carno.replace(' ', '').upper()
context['carno'] = carno
page = int(request.POST.get('page', '1'))
size = int(request.POST.get('size', '4'))
if carno:
context['searched'] = True
context['current_page'] = page
# __exact: 精确查询
# __contains: 字符串两头加%通配符的模糊查询
# __startswith: 字符串后面加%通配符的模糊查询
# __endswith: 字符串前面加%通配符查询
# 注意参数前面加i表示查询的时候忽略大小写
queryset = Record.objects.filter(car__car_no__startswith=carno)
# 返回一个paginator对象,页面显示的记录的条数,和总条数
paginator = Paginator(queryset, size)
# 返回总页面数
context['total_page'] = paginator.num_pages
# 返回一个page对象
context['page_obj'] = paginator.get_page(page)
return render(request, 'index.html', context)
解决1+N查询
我们在调试项目的过程中,项目的性能往往是因为数据库的查询语句(关联查询)导致项目的性能上不去。
应用场景如下:
class Record(models.Model):
"""违章记录模型"""
no = models.AutoField(primary_key=True, verbose_name='编号')
# 与Car模型关联
car = models.ForeignKey(to=Car, on_delete=models.PROTECT, db_constraint=False, db_column='con', verbose_name='车辆')
offend_time = models.DateTimeField(verbose_name='违章时间')
offend_place = models.CharField(max_length=256, verbose_name='违章地点')
offend_reason = models.CharField(max_length=1000, verbose_name='违章原因')
punish = models.CharField(max_length=256, verbose_name='处罚方式')
dealed = models.BooleanField(default=False, verbose_name='是否受理')
class Meta:
db_table = 'tb_record'
verbose_name = "违章记录"
verbose_name_plural = '违章记录'
class Car(models.Model):
"""车辆模型表"""
no = models.AutoField(primary_key=True, verbose_name='编号')
car_no = models.CharField(max_length=10, verbose_name='车牌号', unique=True)
owner = models.CharField(max_length=20, verbose_name='车主')
type = models.IntegerField(
choices=((1, '大型汽车'), (2, '小型汽车'), (3, '专用汽车'), (4, '小型汽车')),
default=2, verbose_name='类型'
)
class Meta:
db_table = 'tb_car'
verbose_name = '车辆'
verbose_name_plural = '车辆'
# view.py
def search(request):
"""查询"""
context = {
'searched': False,
}
if request.method == 'POST':
# 从前端页面的请求中取到carno,取不到给空值
carno = request.POST.get('carno', '')
carno = carno.replace(' ', '').upper()
context['carno'] = carno
if carno:
context['searched'] = True
# 通过模型管理器,按照car对象的car_no(以什么开头)或者按照car对象的__owner属性(以什么开头)的查询Record模型中的记录
queryset = Record.objects.filter(
Q(car__car_no__startswith=carno) |
Q(car__owner__startswith=carno))
return render(request, 'index.html', context)
如果在查询过程中,我们想要显示车主,但是Record这个模型中并没有车主信息,这个时候就会产生一加N查询,他会将相同的记录查询多次。这样就大大降低了程序的性能。
这种现象称为一加N查询要消除这种现象,只需要在查询的后面上
.select_related('关联字段') 用于一对多查询,
.prefetch_related('关联字段')用于多对多查询
queryset = Record.objects.filter(
Q(car__car_no__startswith=carno) |
# 解决一加N查询,在查询后面加上.select_related()方法,一次性将Car关联的对象都放入返回的集合中
Q(car__owner__startswith=carno)).select_related('car').order_by('-offend_time')
这样我们就解决了1+N查询
使用前端渲染(前后端分离)
在传统的Web应用开发中,大多数的程序员会将浏览器作为前后端的分界线。浏览器中为用户进行页面展示的部分叫前端,而将运行在服务器,为前端提供业务逻辑和数据准备的所有代码统称为后端。而前后端的分离开发,实际上时指前后端工程师约定好数据交互接口,并行的进行开发和测试,后端只提供数据,不做渲染。前端通过HTTP请求获取数据并且将数据渲染到页面中,这个工作是交给浏览器中的JavaScript代码来完成的
使用前后端分离开发的诸多好处:
1.提升开发效率。 前后端分离以后,可以实现前后端代码的解耦,只要前后端沟通约定好应用所需接口以及接 口参数,便可以开始并行开发,无需等待对方的开发工作结束。在这种情况下,前后端工程师都可以只专注 于自己的开发工作,有助于打造出更好的团队。除此之外,在前后端分离的开发模式下,即使需求发生变 更,只要接口与数据格式不变,后端开发人员就不需要修改代码,只要前端进行变动即可。
2.增强代码的可维护性。 前后端分离后,应用的代码不再是前后端混合,只有在运行期才会有调用依赖关系, 这样的话维护代码的工作将变得轻松愉快很多,再不会牵一发而动全身。当你的代码变得简明且整洁时,代 码的可读性和可维护性都会有质的提升。
3.支持多终端和服务化架构。 前后端分离后,同一套数据接口可以为不同的终端提供服务,更有助于打造多终 端应用;此外,由于后端提供的接口之间可以通过HTTP(S)进行调用,有助于打造服务化架构(包括微服务)。
返回JSON数据(前面的车辆违章查询实例)
def search(request):
"""查询"""
# 有没有查询过,默认是False,当前页码1,总页数0,records空列表
context = {
'searched': False,
'current_page': 1,
'total_page': 0,
'records':[],
}
# 拿车牌号
carno = request.GET.get('carno', '')
# 处理输入的车牌号,去掉空格,转换成大写
carno = carno.replace(' ', '').upper()
# 拿当前页的页码,如果没有,赋默认值1
page = int(request.POST.get('page', '1'))
# 如果拿到了车牌或者车主的信息
if carno:
# 将searched变为True
context['searched'] = True
# 通过Q对象用车牌号或者车主作为筛选条件查询,并且把Record关联的car对象也一起查询出来(主要是解决一加N查询问题)在利用违章时间(offend_time)进行降序排序。
queryset = Record.objects.filter(
Q(car__car_no__startswith=carno) |
# 解决一加N查询,在查询后面加上.select_related()方法,一次性将Car关联的对象都放入返回的集合中
Q(car__owner__startswith=carno)).select_related('car').order_by('-offend_time')
# django自带的分页器,拿到分页器对象,每页最多显示5条数据
paginator = Paginator(queryset, 5)
# 通过分页器对象的num_pages属性可以拿到总页数
context['total_page'] = paginator.num_pages
# 通过get_page()这个方法,指定页码,就可拿到这一页的对象
page_obj = paginator.get_page(page)
# 通过这个页面对象的number属性 ,可以拿到当前页码
context['current_page'] = page_obj.number
records = []
# 从当前页对象的object_list属性中遍历数据,将每一条记录组装成字典。
for record in page_obj.object_list:
records.append({
'no': record.no,
'carno': record.car.carno,
'owner': record.car.owner,
'time': record.offend_time,
'place': record.offend_place,
'reason': record.offend_reason,
'punish': record.punish,
'dealed': record.dealed
})
# 将records列表添加到context字典里面
context['records'] = records
# 返回json数据
return JsonResponse(context)
在view.py写好数据接口之后在urls.py添加上之后去浏览器查看接口数据,你会得到类似下面的结果.
# 添加url
urlpatterns = [
……
path('search/', search),
]
得到的json数据:
{
"searched": true,
"current_page": 1,
"total_page": 1,
"records": [
{
"no": 6,
"carno": "川A88888",
"owner": "王大锤",
"time": "2019-07-16T23:05:00+08:00",
"place": "春熙路",
"reason": "违章停车",
"punish": "罚款200元人民币,扣除驾照分2分",
"dealed": true
}]
…………
}
这样后端的数据接口就写好了。
远端仓库的使用
如果在创建厂库的时候使用了readme初始化(不推荐使用),那么将本地代码上传到仓库的时候,需要先使用 git pull的命令将远端代码拉下来,并且需要加额外的参数:--allow-unrelated-histories。之后会弹出一个合并文件,不用管,直接wq保存退出就行了。最后在使用 git push -u origin master 将代码推到远端仓库中。
# 将远端仓库的代码拉到本地
git pull --allow-unrelated-histories origin master
# 将本地代码推到远端
git push -u origin master
有时在接手别人项目的时候,这个项目已经有很多个版本了,这时只需克隆最新的版本就行了。
# 从git@gitee.com:Dxes_ld_xh/djangocase.git克隆djangocase,取的别名为djangocase2
git clone --depth=1 git@gitee.com:Dxes_ld_xh/djangocase.git djangocase2
git的常用命令
命令 | 作用 |
---|---|
git init | 初始化本地厂仓库 |
git add 文件/文件夹 / -A / . | 添加文件或者所有本地文件到缓存区中 |
git status | 查看当前git状态 |
git commit -m ‘提交信息' | 将缓存区中的内容全部提交到git本地仓库 |
git log | 查看提交日志 |
git reset --heard HEAD | 让工作目录中的内容和仓库中的内容保持一致 |
git reset --heard HEAD^ | 回到上一个版本 |
git reset --heard HEAD 版本号 | 回到指定的版本 |
git checkout 文件名 | 从暂存区中恢复工作目录中的内容(让工作区中的指定文件,回到上次提交的时候的状态) |
git clone url | 将远程仓库克隆到本地 |
git remote add origin 地址 | 关联远程仓库 |
git push [-u] origin master | 将本地仓库的内容提交到远程仓库的master分之 |
git push origin 分支名 | 将本地仓库的内容提交到远程仓库对应的分支上, 如果分支不存在会自动创建 |
git pull | 将远程仓库中的内容更新到本地仓库和工作区中 |
git branch [-a] | 查看分之 |
git branch 分之名 | 创建分之 |
git checkout 分支名 | 切换分之 |
git checkout -b 分之名 | 切换并创建新的分之 |
git diff 分之1 分之2 | 查看两个分之之间的差异 |
git merge 分之名 | 让当前分之和指定分之进行合并 |
注意: 切换分之、push、pull,这些操作前要保证工作区是干净的clean