使用Django开发需要用到的知识和技巧

Django生产迁移和执行迁移的注意事项

第一次生成和迁移和执行迁移之后,数据库中会有一部分django自带的表,而我们每次迁移完成后,都有对应的迁移记录。如果你在想把迁移之后的表删除,在重新执行迁移,需要先在django_migratios这张表中删除对应的迁移记录,才可以再次迁移这张表。

record.png

在Django自带'django_user'这张表中保存着在控制台创建的超级管理员的账号信息。Django默认会将会话的数据写到表’django_session'表中。

tables.png

模糊查询和多条件查询

参数名 含义
__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查询,他会将相同的记录查询多次。这样就大大降低了程序的性能。

sql.png

这种现象称为一加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查询

sql2.png

使用前端渲染(前后端分离)

在传统的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容