week11 day7 模型层

模型层

一、django测试环境的搭建

  1. pycharm链接数据库都需要自己先下载驱动的。
  2. 自带的sqlite3对日期格式不敏感。如果后续业务需要使用日期辅助筛选数据,那么不推荐使用自带数据库,而使用MySQL。

django默认是一个整体,不会让你单独操作其中的某一个模块。所以需要配置或者利用django自带的python console。

"""
当你只是想测试django中的某一个py文件内容,那么你可以不同书写前后端交互的形式而是直接写一个测试脚本即可
"""

测试环境的准备:

第一种方法:把manage.py里面的前四行代码拷贝过来,后面加上import djangodjango.setup()

import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoDBCURD.settings")
    # 配置django项目的环境变量(os模块是配置系统变量的模块),将暴露给用户的配置信息加载好
    import django
    django.setup()  # 使用环境变量中的配置文件,跑django项目
    from app01 import models
    res = models.Userinfo.objects.all()

第二种方法:利用django自带的python console

二、单表查询关键字(精准查找)

# 方式一:queryset对象.query查看内部query语句
v1 = models.User.objects.values_list('name')  # <QuerySet [('jason',), ('wc',)]>可以理解成列表套元组
print(v1)
print(v1.query)  # SELECT `app01_user`.`name` FROM `app01_user`

# 方式二:添加配置信息
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

必知必会13条

# 增
	# 方法一:利用create自动保存
    res = models.User.objects.create(name='jason', age=20, register_time='2002-1-21')
    print(res)

    import datetime
    ctime = datetime.datetime.today()
    # 方法二:手动创建数据对象,手动保存
    user_obj = models.User(name='evan', age=30, register_time=ctime)
    user_obj.save()
    print(user_obj)

# 删
	# 方法一:批量删除
    res = models.User.objects.filter(pk=2).delete()
    print(res)  # (1, {'app01.User': 1})
    """
    pk会自动查找到当前表的主键字段,指代的是当前表的主键字段
    用了pk之后,你就不需要知道按当前表的主键字段到底叫什么了
    """
    # 方法二:获取到数据对象,针对性删除
    user_obj = models.User.objects.filter(pk=1).first()
    user_obj.delete()
    print(user_obj)

# 改
    user_obj = models.User.objects.filter(pk=4).update(name='wc')
    print(user_obj)
    user_obj = models.User.objects.get(pk=7)  # 不推荐
    """
    get方法返回地直接就是当前数据对象,但是该方法不推荐使用,一旦数据不存在该方法会直接报错
        app01.models.DoesNotExist: User matching query does not exist.
    而filter不会直接报错
    """

    # 必知必会13条
    # 1.all() 查询所有数据【QuerySet对象】
    # 2.filter() 带有过滤条件的查询【QuerySet对象】
    # 3.get() 直接拿数据对象,但是条件不存在直接报错【数据对象or报错】
    # 4.first() 拿queryset里面的第一个元素【数据对象】
    res = models.User.objects.all().first()
    print(res.name)
    # 5.last() 拿queryset里面的最后一个元素【数据对象】
    res1 = models.User.objects.all().last()
    print(res1.register_time)

    # 6.values() 可以指定获取的数据字段,该方法的返回结果是列表套字典【QuerySet对象】
    v = models.User.objects.values('name')
    print(v)  # <QuerySet [{'name': 'jason'}, {'name': 'wc'}]>可以理解成列表套字典

    # 7.values_list() 列表套元组【QuerySet对象】
    v1 = models.User.objects.values_list('name') # <QuerySet [('jason',), ('wc',)]>可以理解成列表套元组
    print(v1)
    print(v1.query)  # SELECT `app01_user`.`name` FROM `app01_user`
    """
    上述查看SQL语句的方式,只能用于queryset对象
    """
    # 8.distinct()【QuerySet对象】
    res = models.User.objects.values('name', 'age').distinct()
    print(res)  # 可以理解成列表套元组
    """
    去重一定要是一模一样的数据
    如果带有主键那么肯定不一样,在往后的查询中一定不要忽略主键!!!
    """
    # 9.order_by()【QuerySet对象】
    res = models.User.objects.values('name').order_by('-name')
    print(res)
    # 默认升序。前面加一个 - 就变成了降序
    # 10.reverse() 反转的前提是数据已经排序过了【QuerySet对象】
    res = models.User.objects.all()
    res1 = models.User.objects.values('name', 'age').order_by('age').reverse()
    print(res1)
    # 11.count() 统计当前数据的个数
    res = models.User.objects.count()
    print(res)
    # 12.exclude()【QuerySet对象】
    res = models.User.objects.exclude(name='wc')
    print(res)
    # 13.exists() 基本用不到,返回的是布尔值【boolean值】
    res = models.User.objects.filter(pk=4).exists()
    print(res)
获得QuerySet获得数据对象获得数字获得布尔值
方法all、filter、values、values_list、distinct、order_by、reverse、excludefirst、get、lastcountexists

三、神奇的双下划线查询(范围查询)

"""
DateField
DateTimeField
	两个重要参数:
    auto_now:每次操作数据的时候,该字段会自动将当前时间更新
    auto_now_add:在创建数据的时候会自动将当前操作时间记录下来,之后只要不人为修改,那么会一直不变
"""

# 神奇的双下划线查询
    1.年龄大于35岁的数据
    res = models.User.objects.filter(age__gt=20)
    print(res)

    2.年龄小于20岁的数据
    res = models.User.objects.filter(age__lt=20)
    print(res)

    3.大于等于、小于等于
    res = models.User.objects.filter(age__gte=30)
    res1 = models.User.objects.filter(age__lte=30)
    print(res, res1)

    4.年龄是18/32/40
    res = models.User.objects.filter(age__in=[20, 32, 40])
    print(res)

    5.年龄在3040岁之间的  顾头也顾尾
    res = models.User.objects.filter(age__range=[30, 40])
    print(res)

    6.查询出名字里面含有n的数据。默认是区分大小写的,如果想要忽略大小前面加i
    res = models.User.objects.filter(name__contains='n')
    res1 = models.User.objects.filter(name__icontains='n')
    print(res, res1)

    7.名字以xxx开头的/xxx结尾的
    res0 = models.User.objects.filter(name__startswith='mmm')
    res = models.User.objects.filter(name__endswith='mmm')

    8.查询出注册时间。按年year,按月month,按日day
    res = models.User.objects.filter(register_time__day=20)
    res1 = (models.User.objects.filter(register_time__year=1970))
    print(res, res1)

四、图书管理系统的表设计

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')
    # 在django 2.x 版本中,不再默认级联更新和级联删除。需要自己指定
    authors = models.ManyToManyField(to='Author', on_delete=SET_DEFAULT(需要在后面加上default=xxx)/SET/DO_NOTHING/SET_NULL)

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=128)
    email = models.EmailField()  # varchar(254) 该字段类型 不是给models看的,而是给后面校验型组件看的

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 一对一
    author_detail = models.OneToOneField(to='AuthorDetail')

class AuthorDetail(models.Model):
    phone = models.CharField(max_length=12)
    addr = models.CharField(max_length=256)

五、外键字段的增删改查

5.1 一对多绑定外键增删改

# 一对多外键增删改查
    # 增
    # 1.直接写实际字段 id
    models.Book.objects.create(title='三国演义', price=123.23, publish_id=1)
    # 2.虚拟字段  对象
    pub_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='红楼梦', price=666.23, publish=pub_obj)
    
	# 删
    models.Publish.objects.filter(pk=1).delete()
    
    # 修改
    # 1.直接写实际字段 id
    models.Book.objects.filter(pk=3).update(publish_id=2)
    # 2.虚拟字段 对象
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.filter(pk=3).update(publish=publish_obj)

5.2 多对多绑定外键增删改查

# 多对多表关系的增删改查,就是在操作第三张表

    # 如何给书籍绑定作者?(增:在原来基础上增加)
    book_obj = models.Book.objects.filter(pk=3).first()
    # print(book_obj.authors)  # 就类似于你已经到了第三张关系表了 app01.Author.None
    # book_obj.authors.add(1)  # 书籍id为 1 的书籍绑定一个主键为 1 的作者
    # book_obj.authors.add(2, 3)

    author_obj = models.Author.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=2).first()
    author_obj2 = models.Author.objects.filter(pk=3).first()
    book_obj.authors.add(author_obj, author_obj1, author_obj2)
    """
    add给第三张表添加数据
    	括号内既可以传数字也可以传对象,并且都支持多个,以逗号隔开即可
    """
    
    # 删
    book_obj.authors.remove(2)
    book_obj.authors.remove(1, 3)
    book_obj.authors.remove(author_obj, author_obj2)
    
    # 改:先清空再设置
    book_obj = models.Book.objects.filter(pk=4).first()
    # book_obj.authors.set(1)  # TypeError: 'int' object is not iterable
    # book_obj.authors.set([1, 2])
    author_obj = models.Author.objects.filter(pk=3).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    book_obj.authors.set([author_obj, author_obj1])
    """
    set
    	括号内必须传一个可迭代对象,该对象内既可以数字也可以对象,并且都支持多个
    """
    
    # 清空
    # 在第三张关系表中清空某个书籍与作者的绑定关系
    book_obj.authors.clear()
    """
    括号内不要放任何值
    """

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

正反向概念:

  1. 外键字段在我手上,我查你就是正向
  2. 外键字段如果不在我手上,我查你就是反向

book >>> 外键字段在书那(正向) >>> publish

publish >>> 外键字段在书那(反向) >>> book

  1. 先查询出一个对象

  2. 基于对象点正反向字段
    正向点外键字段

    ​ 反向点表名小写

# 基于对象的跨表查询
    # 1.查询书籍主键为 3 的出版社名称
        book_obj = models.Book.objects.filter(pk=3).first()
        # 书查出版社,外键在书,正向-----》点主键字段
        res = book_obj.publish
        print(res.name)
    
	# 2.查询书籍主键为3的作者
        book_obj = models.Book.objects.filter(pk=3).first()
        # 书查作者,外键在书,正向-----》点主键字段
        # res = book_obj.authors  # app01.Author.None
        res = book_obj.authors.all()  # <QuerySet [<Author: Author object>, <Author: Author object>, <Author: Author object>]>
        print(res)
    
    # 3.查询作者jason的电话号码
        author_obj = models.Author.objects.filter(name='jason').first()
        res = author_obj.author_detail
        print(res.phone)
        """
        在书写orm语句的时候和书写SQL语句一样的,不要企图一次性把orm语句写完,如果比较复杂,就写一点看一点
        
        正向什么时候需要加.all()
            当你的结果可能有多个的时候就需要加.all()
            如果是一个则直接拿到数据对象
        """
    
    # 4.查询出版社是东方出版社出版的书
        publish_obj = models.Publish.objects.filter(name='东方出版社').first()
        # res = publish_obj.book_set  # app01.Book.None
        res = publish_obj.book_set.all()
        print(res.values('title'))
        
    # 5.查询作者是jason写过的书
        author_obj = models.Author.objects.filter(name='jason').first()
        # res = author_obj.book_set
        res = author_obj.book_set.all()
        print(res)    
        
    # 6.查询手机号110的作者姓名
        author_obj = models.AuthorDetail.objects.filter(phone=110).first()
        res = author_obj.author
        print(res)
    """
    当你的查询结果有多个时候,就必须加_set.all()
    当你的结果只有一个的时候,不需要加_set.all()
    """

七、基于下划线的跨表查询(链表查询)

# 1.查询jason的手机号
    res = models.Author.objects.filter(name='jason').values('author_detail__phone')
    print(res)
    # 反向
    # res = models.AuthorDetail.objects.filter(author__name='jason') # 拿作者姓名是jason的外键字段
    res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
    print(res)
    
# 2.查询书籍主键为3的出版社名称和书的名称
    res = models.Book.objects.filter(pk=3).values('publish__name', 'title')
    print(res)
    # 反向
    res = models.Publish.objects.filter(book__pk=3).values('name', 'book__title')
    print(res)

# 3.查询书籍主键为3的作者姓名
    res = models.Book.objects.filter(pk=3).values('authors__name')
    print(res)
	# 反向
    res = models.Author.objects.filter(book__pk=3).values('name')
    print(res)
    
# 4.查询书籍主键是3的作者的手机号
    res = models.Author.objects.filter(book__pk=3).values('author_detail__phone')
    print(res)
    """
    你只要掌握了正反向的概念以及双下划线
    就可以无限制的跨表
    """

八、聚合查询aggregate

# 聚合查询  aggregate
    """
    聚合查询通常情况下都是配合分组一起使用的
    只要跟数据库相关的模块:
        基本上都在django.db.models里面
        如果上述没有那么应该在django.db里面
    """
    from django.db.models import Max, Min, Sum, Count, Avg
    # 1.所有书的平均价格
    res = models.Book.objects.aggregate(Avg('price'))
    print(res)  # {'price__avg': 493.316}
    # 加点要求:egon出版书的平均价格
    res = models.Book.objects.filter(author__name='egon').aggregate(Avg('price'))    

    # 2.上述方法一次性使用
    res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'))
    print(res)  
    # {'price__max': Decimal('899.23'), 'price__min': Decimal('123.23'), 'price__sum': Decimal('2466.58'), 'pk__count': 5}
    
    from django.db.models import F, Q
    # 把egon出版的所有图书价格加10
    res = models.Book.objects.filter(authors__name='egon')
    print(res.values('price'))
    res.update(price=F('price')+10)
    print(res.values('price'))


    # 查询名字叫红楼梦或价格大于800的书
    res = models.Book.objects.filter(Q(title='红楼梦') | Q(price__gt=800))
    print(res.values('title'))

    # 查询名字叫红楼梦和价格大于100  或者 pk大于6
    res = models.Book.objects.filter(Q(title='红楼梦', price__gt=900) | Q(pk__gt=6))
    print(res.values('title'))

    # 查询名字不是红楼梦,并且价格大于100的书
    res = models.Book.objects.filter(~Q(title='红楼梦'), price__gt=900)
    print(res.values('title'))
    
# 把聚合查询的结果重命名

九、分组查询annotate

# !!!分组查询使用方法!!!
"""标准 annotate() 内写聚合函数
    values在前,表示group_by的字段
    values在后,表示取字段
    filter在前,表示where条件
    filter在后,表示having条件
    (一时间想不到怎么写就用原生SQL语句书写,再通过逐步转换成ORM语法的写法)
同时,强烈建议使用分组的表作为基表。
"""
# 分组查询 annotate
    """
    MySQL分组查询都有哪些特点?
        分组之后默认只能获取到分组的依据,组内其他字段都无法直接获取了
            严格模式
                ONLY_FULL_GROUP_BY
    """
    # 1.统计每一本书的作者个数
    res = models.Book.objects.annotate()  # models后面点什么 就是按照什么分组
    res1 = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
    """
    author_num是我们自定义的字段,用来存储统计出来的每本书对应的作者个数
    """
    print(res, res1)
    # <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>,
    #   <Book: Book object>]> <QuerySet [{'title': '三国演义', 'author_num': 3}, {'title': '红楼梦', 'author_num': 0},
    #   {'title': '论语', 'author_num': 0}, {'title': '聊斋', 'author_num': 0}, {'title': '老子', 'author_num': 0}]>
    """
    代码没有补全,不要怕,正常写
    补全是pycharm提供的功能,到后面在服务器上直接书写代码,什么补全都没有
    """
    # 2.统计每个出版社卖的最便宜的书得价格
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    print(res)  # <QuerySet [{'name': '东方出版社', 'min_price': Decimal('123.23')}, {'name': '西方出版社', 'min_price': Decimal('444.23')}]>

    # 3.统计不止一个作者的图书
        # 1.先按照图书分组,求每一本书的作者个数
        # 2.过滤出不止一个作者的图书
    res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
    """
    只要你的ORM语句得到的还是一个queryset对象
    那么他就可以继续无限制的点queryset对象封装的方法
    """
    print(res.first().title)  # 三国演义

    # 4.查询每个作者出版的书的总价格
    res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
    print(res)
    # <QuerySet [{'name': 'egon', 'sum_price': Decimal('123.23')}, {'name': 'jason', 'sum_price': Decimal('123.23')},
    #   {'name': 'tank', 'sum_price': Decimal('123.23')}]>

    
    from django.db.models import Max, Min, Count, Sum, Avg
    # 查询出版社id大于1的出版社id,以及出书平均价格
    # select id, Avg(book__price) from publish where id>1 group by id
    res = models.Publish.objects.filter(pk__gt=1).values('pk').annotate(avgprice=Avg('book__price')).values('id', 'avgprice')
    print(res)
    
	# 查询出版社id大于1的出版社id,以及出书平均价格大于500的
    res = models.Publish.objects.filter(pk__gt=1).values('pk').annotate(avg_price=Avg('book__price')).\
        filter(avg_price__gt=500).values('pk', 'avg_price')
    print(res)

    ## 查询每一个出版社出版的书籍个数
    res = models.Publish.objects.values('pk').annotate(count_num=Count('book')).values('pk', 'count_num')
    print(res)

    # 如果没有指定group by的字段,默认就用基表(Publish)主键字段作为group by的字段
    ret=models.Publish.objects.annotate(count=Count('book__id')).values('name','count')
    print(ret)

    # 另一种方式实现
    ret=models.Book.objects.values('publish').annotate(count=Count('id')).\
    values('publish__name','count')
    print(ret)

    #查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
    # 如果不用分组的表作为基表,数据不完整可能会出现问题
    res = models.Author.objects.values('pk').annotate(max_price=Max('book__price')).values('name', 'max_price')
    print(res)
    # 如果有没有绑定作者的书,这里就会出现一个None,表示无作者出版的书里面最贵的书
    res = models.Book.objects.values('authors__pk').annotate(max_price=Max('price')).values('authors__name', 'max_price')
    print(res)

    #查询每一个书籍的名称,以及对应的作者个数
    res = models.Book.objects.values('title').annotate(count_authors=Count('authors')).values('title', 'count_authors')
    print(res)

    #统计不止一个作者的图书
    res = models.Book.objects.values('title').annotate(count_authors=Count('authors')).filter(count_authors__gt=1).values('title', 'count_authors')
    print(res)

    # 统计价格数大于10元,作者的图书
    res = models.Author.objects.filter(book__price__gt=1000).values('name', 'book__title')
    print(res)

    #统计价格数大于10元,作者个数大于1的图书
    res = models.Author.objects.filter(book__price__gt=800).values('book__title').\
        annotate(count_authors=Count('name')).filter(count_authors__gt=1)
    print(res)
    """
    如果我想按照指定的字段分组该如何处理呢?
        models.Book.objects.values('price').annotate()
    后续的BBS作业会使用
    
    
    如果机器上出现了分组查询报错的情况
        去数据库设置严格模式
    """

十、F和Q查询

#  F 与 Q 查询
    # 1.查询出卖出数大于库存数的书籍
    # f查询
    """
    能够帮助直接获取表中某个字段对应的数据
    """
    from django.db.models import F
    res = models.Book.objects.filter(maichu__gt=F('kucun'))
    print(res.first().title)

    # 2.将所有书籍的价格提升50块
    models.Book.objects.update(price=F('price')+500)
    # 细化要求,egon出版的书价格提供50块
    models.Book.objects.filter(author__name='egon').update(price=F('price')+50)

    # 3.将所有书的名称后面加上爆款两个字
    """
    在操作字符类型的数据的时候,F不能直接左到字符串的拼接
    """
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    # models.Book.objects.update(title=F('title'), '爆款')  所有的名称都会变成空白

    # Q 查询
    # 1.查询卖出数大于100或者价格小于600的书籍
    res = models.Book.objects.filter(maichu__gt=100, price__lt=600)
    """
    filter括号内多个参数是and关系
    """
    from django.db.models import Q
    res = models.Book.objects.filter(Q(maichu__gt=100), Q(price__lt=600))  # Q包裹逗号分隔,还是and关系
    # sql语句 WHERE (`app01_book`.`maichu` > 100 AND `app01_book`.`price` < 600)
    res1 = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600))  # Q包裹 | 分割,是or关系
    # sql语句 WHERE (`app01_book`.`maichu` > 100 OR `app01_book`.`price` < 600)
    res2 = models.Book.objects.filter(~Q(maichu__gt=100) | Q(price__lt=600))  # Q包裹 ~ 分割,是not关系
    # sql语句 WHERE (NOT (`app01_book`.`maichu` > 100) OR `app01_book`.`price` < 600)
    print(res, res1, res2)


    # Q 的高阶用法    能够将查询条件的左边也变成字符串的形式
    q = Q()
    q.connector = 'or'
    q.children.append(('maichu__gt', 100))
    q.children.append(('price__lt', 600))
    res = models.Book.objects.filter(q)  # 默认还是and关系
    print(res.values('title'))

十一、django中开启事务

# 事务
    from django.db import transaction
    with transaction.atomic():
        # sql1        
        # sql2
        # 在with代码块内书写的所有的orm操作都是属于同一个事务
    print('正在执行其他操作')

十二、ORM中常用字段及参数

1.AutoField
	主键字段 primary_key=True
2.CharField	varchar
	verbose_name	字段的注释
	max_length		长度

3.InterField	int
4.BigIntergerField	bigint
5.DecimalField
	max_length=8
    decimal_places=2

6.EmailField	varchar(254)

7.DateField	date
8.DateTimeField	 datetime
	auto_now	每次修改数据的时候都会自动更新当前时间
    auto_now_add	只在创建数据的时候记录创建时间后续不会自动修改了
    
9.BooleanField(Field)		布尔类型
	该字段传布尔值(False/True),数据库里面存01
    
10.TextField	文本类型
	该字段可以用来存大段内容(文章、博客...),没有字数限制
    后面的bbs作业,文章字段用的就是TextField

11.FileField(Field)
	upload_to = "/data" 给该字段传一个文件对象,会自动保存到/data目录下然后将文件路径保存到数据库中
    /data/a.txt 

# django除了给你提供了很多字段类型之外,还支持你自定义字段
null                数据库中字段是否可以为空
db_column           数据库中字段的列名
db_tablespace
default             数据库中字段的默认值
primary_key         数据库中字段是否为主键
db_index            数据库中字段是否建立索引
unique              数据库中字段是否建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引(了解)
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引(了解)
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引(了解)
auto_now()		    每次更新数据都会更新该字段【只对.save()方法有效,update无效】
auto_now_add()      只有在创建记录的时候记录时间,后续操作不更改时时间

verbose_name        Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作

gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
get_gf_display

十三、字段关系 / 手动创建第三张表 / 元信息 / 原生SQL

13.1 字段关系

# 1.ForeignKey:外键字段在ORM中表示外键关联关系,一般把ForeignKey字段设置在 “一对多” 中 “多” 的一方
	# ForeignKey可以和其他表做关联关系同时也可以和自身做好关联关系
"""
to:设置要关联的表
to_field:设置要关联的的表的字段
related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’。(了解即可)
related_query_name:反向查询操作时,使用的连接前缀,用于替换表名。(了解即可)

on_delete:当删除关联表中的数据时,当前表与其关联的行的行为
	models.CASCADE: 删除关联数据,与之关联也删除
	models.DO_NOTHING: 删除关联数据,引发错误integrity error
	models.PROTECT: 删除关联数据,引发错误protected error
	models.SET_NULL: 删除关联数据,与之关联的值设置为默认值null(前提FK字段需要设置可为空)
	models.SET_DEFAULT: 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
	models.SET:删除关联数据,
		a.与之关联的值设置为指定值,设置:models.SET(值)
		b.与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)(下例)
		
db_constraint: 是否在数据库中创建外键约束,默认为True
"""
def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",
        to_field="id",
        on_delete=models.SET(func)
    )
# 2.OneToOneField:一对一字段。一对一关联的关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。
class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    
class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()
    
"""
to:设置要关联的表
to_field:设置要关联的的表的字段
on_delete: 同foreignkey字段
"""
# 3.ManyToManyField: 用于表示多对多的关联关系。在数据库中通过第三张表来建立外键联系
"""
to: 设置要关联的表
related_name: 同ForeignKey字段。
related_query_name: 同ForeignKey字段。
symmetrical: 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。(下例)
through: 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。但我们也可以手动创建第三	张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
through_fields: 设置关联的字段。
db_table: 默认创建第三张表时,数据库中表的名称。
"""
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")
# 此时,person对象就没有person_set属性    
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)
# 此时,person对象现在就可以使用peorson_set的属性来进行反向查询

13.2 手动创建第三张表的三种方式

# 第一种:在建立表的时候添加多对多的字段,让数据库自动创建【最常见】
    # 优点:方便
    # 缺点:创建的字段少,有时可能无法满足我们的需求
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")

# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")
# 第二种:自行创建第三张表【不太使用】
    # 优点:可以手动添加可能需要的字段
    # 缺点:操作麻烦,需要互相建立一对多关系两次。从书去作者需要Book__author2book__author
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")

class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    
# 自己完全手动创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
    author = models.ForeignKey(to='Book')
    book = models.ForeignKey(to='Author')
    # 还可以创建其他字段
# 第三种:设置ManyToManyField并指定自行创建的第三张表【使用频率仅次于自动创建第三张表】
	# 优点:利用了内置方法创建表还可以添加自己需要的字段。无论是基于对象的跨表查还是基于双下划线的跨表查都能继续使用
    # 缺点:原来的多对多操作api是用不了了。add、set、clear、delete。多表操作需要手动添加
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")

# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
	books = models.ManyToManyFiled(to=Book, through='author2book', through_fields=("author","book"))
	# through_fields接受一个二元组("field1","field2") 
    # 其中field1是ManyToManyField所在表去中间表所需要走过的字段
"""
through: 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。但我们也可以手动创建第三	张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
through_fields: 设置关联的字段。
"""
class author2book(models.Model):
    author = models.ForeignKey(to=Author)
    book = models.ForeignKey(to=Book)

13.3 元信息

ORM对应的类里面包含一个meta类,而meta类封装了一些数据库的信息。主要字段如下:

# db_table: ORM在数据库中的表名默认是app_类名,可以通过db_table重命名。
# index_together: 联合索引。
# unique_together: 联合唯一索引。
# ordering: 指定默认按什么字段排序。只有设置了该字段,查询到的结果才可以reverse()操作。
    
class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)

    class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "table_name"

        # 联合索引
        index_together = [("pub_date", "deadline"),]
        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)
        
        ordering = ('name',)
        
        # admin中显示的表名称
        verbose_name='哈哈'

        # verbose_name加s
        verbose_name_plural=verbose_name

13.4 原生SQL

from django.db import connection, connections

cursor = connection.cursor() # connection=default数据
cursor = connections['db2'].cursor()

cursor.execute("""SELECT * from auth_user where id = %s""", [1])

row = cursor.fetchone()
row = cursor.fetchall()
ret = models.Author.objects.raw('select * from app01_author where nid>1')
print(ret)
for i in ret:
    print(i)
print(ret.query)
# 会把book的字段放到author对象中
ret = models.Author.objects.raw('select * from app01_book where nid>1')
print(ret)
for i in ret:
    print(i.price)
    print(type(i))

补充:

  1. 普通函数以__开头

    说明当前函数只在当前模块(py)下使用,尽量不在外部调用

  2. MySQL的 utf8 不同于传统的 UTF-8。
    utf-8:1-4个字节,表示一个字符
    utf8:2个字节表示一个字符
    utf8-md4等同于真正意义上的utf-8

  3. 在django框架顶级(顶格书写)代码在启动django项目的时候都会优先执行

  4. django admin的功能

    1. 后台管理,方便快速的录入书籍
    
    2. 使用方法:
       # 第一步:在admin.py中把要使用的表注册
       from app01 import models
    
       admin.site.register(models.Book)
       admin.site.register(models.Author)
       admin.site.register(models.AuthorDetail)
       admin.site.register(models.Publish)
       # 第二步:创建超级管理员
       python3 manage.py createsuperuser
       输入用户名root,输入密码12345wth
       # 输入密码过程中密码不显示
        (密码的要求:This password is too short. It must contain at least 8 characters.
    This password is entirely numeric.
    )
       # 第三步:登录,录入书籍
       http://127.0.0.1:8000/admin/
    
  1. api:开发者所提供的功能(接口)

    dtl(django template language):django模板语法。在模板中写python代码。

    php:url中index.php。.php 结尾的文件,先经过PHP 解析器翻译为html 标记文本,再被浏览器解析成网页。例:https://www.876p.cn/tags.php/https://www.baidu.com/index.*php*

    java(jsp):url中index.jsp。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。例:
    【具体内容看01 web框架 的补充部分】

  2. 什么时候创建第三张表?
    当第三张表需要额外字段的时候。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值