zoukankan      html  css  js  c++  java
  • 小谈Django中的ORM

    ORM

    概述

    ORM : 对象关系映射

    对应关系

    • 类 --> 表
    • 对象 --> 数据行
    • 属性 --> 字段

    优点与缺点

    • 优点 : 同样的语句可以对应不同的数据库 ;
    • 缺点 : 实际运行时Python代码需要转化为数据库语言 , 影响效率 ;

    Django中使用Mysql数据库

    settings.py中的数据库配置

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.数据库类型',
            'NAME': '创建的数据库名称',
            'USER': '数据库用户名',
            'PASSWORD': '密码',
            'HOST': '数据库IP',
            'PORT': 数据库端口,
        }
    }
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'bookmanager',
            'USER': 'root',
            'PASSWORD': '3232',
            'HOST': '127.0.0.1',
            'PORT': 3306,
        }
    }
    

    在Django项目名目录下的__init__.py(与settins.py同级的)

    import pymysql
    
    pymsql.install_as_MySQLdb()
    

    models.py

    字段

    AutoField

    自增字段

    DatetimeField

    • auto_now_add=True 新增数据时,填写上当前的时间 ;
    • auto_now=True 新增或者修改数据时,都会填写当前的时间 ;
    • auto_now_add , auto_now 和 default 三个参数是互斥的 , 不能同时设置 ;

    BooleanField

    # choices=[(数据库中存储的内容1, form组件显示的名字1),(数据库中存储的内容2, form组件显示的名字2)]
    # choices最外面的[],用()也可以
    sex = models.BooleanField(choices=[(True,'男'),(False,'女')])
    sex = models.CharField(choices=(('male','男'),('female','女'),('buxiang','未知')))
    

    TextField

    文本类型 : 大字符串

    ImageField

    上传图片 , 在数据库中只存储路径

    DecimalField

    十进制小数

    from django.db import models
    
    class Person(models.Model):
        
        # 主键 : primary_key=True : 如果没有这个字段会自动生成,而且这个字段只可以有一个
    	id = models.AutoField(primary_key=True)
        
        name = models.CharField()
        age = models.IntegerField() # -21亿 ~ 21亿 十位数,不能存储手机号,手机号可以用CharField 
        sex = models.BooleanField()
        
        # auto_now_add=True 新增数据时,填写上当前的时间;
        # auto_now=True 新增或者修改数据时,都会填写当前的时间;
        # 在数据库中操作时还是需要输入的,因为'自动填写当前时间'指的是Django而不是数据库;如果用models.Person.objects.create(name=xxx,age=xxx)就可以了
        birth = models.DatetimeField(auto_now_add=True)
        
    

    自定义字段

    下面以自定义一个char类型字段为例 :

    • 如果把长度设置为11 , 可以用来存储手机号 , 向数据库中存储时可以存储小于11位的 , 但是大于11位的无法存储 ;
    class MyCharField(models.Field):
        """
        自定义的char类型的字段类
        """
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
     
        def db_type(self, connection):
            """
            限定生成数据库表的字段类型为char,长度为max_length指定的值
            """
            return 'char(%s)' % self.max_length
    

    字段参数

    null=True

    允许数据库中该字段可以为空

    blank=True

    Django中提供的form组件允许用户输入可以为空 ; Admin中允许用户输入为空( 因为Admin使用了form组件 )

    verbose_name

    在form组件中( admin页面中 )显示的字段名字 ; 在CharField字段中指定时 , 可以写在第一参数位置 , 这样可以不写verbose_name , 直接写内容 , 因为在CharField再上一层的class CharField中的双下init中指定的第一参数就是verbose_name ; 但是其他字段的第一参数可能不是verbose_name

    db_column

    在数据库中显示的字段名字 ;

    name = models.CharField(max_length=32, db_column='username')
    # 只是数据库显示为username,实际在Django项目中写代码还是用name
    

    default

    默认值

    primary_key

    主键

    db_index=True

    创建索引 , 在数据库中查询时速度变快

    unique

    唯一约束

    choices

    让用户选择的数据

    # choices=[(数据库中存储的内容1, form组件显示的名字1),(数据库中存储的内容2, form组件显示的名字2)]
    # choices最外面的[],用()也可以
    sex = models.BooleanField(choices=[(True,'男'),(False,'女')])
    sex = models.CharField(choices=(('male','男'),('female','女'),('buxiang','未知')))
    

    表参数

    在models.py的类中再加上一个类 class Meta对数据库中的表进行一些其他配置

    class Person(models.Model)
    	...
    	class Meta:
        	# 表在数据库中的名字,默认是add名称+下划线+类名小写
        	db_table = 'table_name'
            # admin中显示的表名称
            verbose_name = '个人信息'
            # verbose_name加s
            verbose_name_plural = '所有用户信息'
            # 联合索引:在数据库中查询速度会变快,只查name变快,查name+age也变快,但是只查age不会变快 --> 数据库中的遵循最左前缀
            index_together = [
                ("name", "age"), # 应为两个存在的字段
            ]
            # 联合唯一索引:在联合索引的基础上再加上唯一约束,两个字段不能同时重复
            unique_together = (
                ('name', 'age'), # 应为两个存在的字段
            )
    

    Admin的使用

    1. 创建超级用户

    python manage.py createsuperuser
    # 1.提示输入Username,如果输入为空就用计算机用户的名字
    # 2.提示输入Email address:可以不写,直接回车
    # 3.提示输入Password:不能纯数字也不能纯英文,也不显示
    # 然后就可以用浏览器打开ip:端口号/admin
    

    2. 注册model

    在app下的admin.py中输入

    from django.contrib import admin
    from app名字 import models
    # Register your models here.
    admin.site.register(models.字段名)
    

    3. 访问网站登录操作

    有些字段参数涉及到在form组件中的显示效果 , 在admin中也有效果 , 是因为admin使用了Django的form组件

    必知必会13条

    在Django中新建的脚本需要使用Django环境

    # 脚本中
    # 前两句来自于manage.py
    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名.settings")
    # 后两句: Django初始化
    import django
    django.setup()
    

    __str____repr__

    model里的对象直接print的内容 : 先查找这个类的双下str方法 , 没有就去父类找双下str ; 再没有的话就找双下repr方法 , 再没有就去父类找双下repr方法 ;

    model里的对象列表直接print的内容 : 先查找这个类的双下repr方法 , 没有就去父类找双下repr ; 再没有的话就找双下str方法 , 再没有就去父类找双下str方法 ;

    # models.py
    class Person:
        ...
        def __str__(self):
            return self.name
    # 脚本中
    print(models.Person.objects.all()) # 如果这个想输出<QuerySet [bigbao]>;就在model中加上 __repr__ = __str__ 或者重新定义def __repr__(self)就可以了;
    

    必知必会13条

    import os
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
    import django
    
    django.setup()  # 初始化
    
    from app01 import models
    
    # all()  查询所有的数据  QuerySet 对象列表
    ret = models.Person.objects.all()
    
    # filter  获取满足条件的所有的对象  QuerySet 对象列表
    ret = models.Person.objects.filter(name='alex')
    
    # exclude  获取不满足条件的所有的对象  QuerySet 对象列表
    ret = models.Person.objects.exclude(name='alex')
    
    # values()
    #  不指定字段 获取所有字段名和值  QuerySet 对象列表  [ {},{} ]
    #  指定字段  values('pid','name')  获取指定字段名和值  QuerySet 对象列表  [ {},{} ]
    ret = models.Person.objects.all().values()
    ret = models.Person.objects.filter(name='alex').values('pid', 'name')
    
    # values_list()
    #  不指定字段 获取所有的值  QuerySet 对象列表  [ (),() ]
    #  指定字段  values_list('pid','name')  获取指定的值  QuerySet 对象列表  [ (),() ]
    ret = models.Person.objects.all().values_list()
    ret = models.Person.objects.filter(name='alex').values_list('name', 'pid', )
    
    # order_by  排序 默认升序  -降序  可以多个字段排序
    ret = models.Person.objects.all().order_by('age', '-pid')
    
    # reverse  对已经排序的QuerySet做的反转
    ret = models.Person.objects.all().order_by('pid').reverse()
    
    # get    获取满足条件的一个的对象  对象
    ret = models.Person.objects.get(name='alex')
    
    # first  获取第一个元素   对象  获取不到的时候是none
    ret = models.Person.objects.filter(name='xxx').first()
    
    # last  获取最后一个元素   对象  获取不到的时候是none
    ret = models.Person.objects.filter(name='xxx').last()
    
    #  count 计数
    ret = models.Person.objects.all().filter(age=84).count()
    
    # exists 数据是否存在
    ret = models.Person.objects.filter(age=84).exists()
    
    # distinct  去重  数据时完全一样才去重
    ret = models.Person.objects.filter(age=84).values('age').distinct()
    

    单表的双下划线

    import os
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
    import django
    
    django.setup()  # 初始化
    
    from app01 import models
    
    ret = models.Person.objects.filter(pk__gt=3)  # greater than   where pk > 3
    ret = models.Person.objects.filter(pk__gte=3)  # greater than equal   where pk >= 3
    
    ret = models.Person.objects.filter(pk__lt=3)  # less than   where pk < 3
    ret = models.Person.objects.filter(pk__lte=3)  # less than equal   where pk <= 3
    
    ret = models.Person.objects.filter(pk__range=[1,3])  # 1 <= pk <= 3
    ret = models.Person.objects.filter(pk__in=[1,3,7,10,100])  # 成员判断
    
    ret = models.Person.objects.filter(name__contains='bigbao')  # like   不忽略大小写
    ret = models.Person.objects.filter(name__icontains='bigbao')  # like  ignore  忽略大小写
    
    ret = models.Person.objects.filter(name__startswith='b')  # 以什么开头 不忽略大小写
    ret = models.Person.objects.filter(name__istartswith='b')  # 以什么开头 忽略大小写
    
    ret = models.Person.objects.filter(name__endswith='o')  # 以什么结尾 不忽略大小写
    ret = models.Person.objects.filter(name__iendswith='o')  # 以什么结尾 忽略大小写
    
    ret = models.Person.objects.filter(age__isnull=False)   # 字段是否为空
    
    
    ret = models.Person.objects.filter(birth__year='2019')   # 按照年份
    ret = models.Person.objects.filter(birth__contains='2019-12-19')  # 时间包含
    
    print(ret)
    

    外键的操作

    # models.py
    class Publisher(models.Model):
        name = models.CharField(max_length=32)
        
        def __str__(self):
            return self.name
    
        
    class Book(models.Model):
        name = models.CharField(max_length=32)
        # related_name("有关联的名字"),related_name就是让Publisher反向查询时要用的名字,去查一个出版社关联所有它出版的书的对象,所以是books
        pub = models.ForeignFey(Publisher, on_delete=models.CASCADE, related_name='books')
        
        def __str__(self):
            return self.name
    
    # 脚本中
    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名.settings")
    import django
    django.setup()
    
    from app01 import models
    
    # 一, 基于对象的查询
    
      # 1. 正向查询(拿到书的对象后,直接点外键拿到出版社对象)
        # 1.1 直接通过书的类查询书的对象
    book_obj = models.Book.objects.get(pk=3)
        # 1.2 通过书的对象查询出版社对象
    print(book_obj.pub)
    
      # 2. 反向查询(拿到出版社对象后,再去拿出版社出的多有的书)
        # 2.1 直接通过出版社的类查询出版社的对象
    pub_obj = models.Publisher.objects.get(pk=1)
        # 2.2 通过出版社的对象去拿该出版社出版的所有书
          # 2.2.1 没有添加related_name时可以这样操作(外键所在表的小写_set),加上related_name后就不能用这个了,因为related_name已经替换了book_set
    print(pub_obj.book_set) # 关系管理对象 book_set相当于related_name
    print(pub_obj.book_set.all()) # 拿到所有关联的对象
          # 2.2.2 添加related_name后(直接点related_name),直接点related_name就好;因为related_name相当于book_set
    print(pub_obj.books.all())
    
    # 二, 基于字段的查询
    
      # 1. 正向查询,从Book中查询一个出版社出版的所有书籍
    models.Book.objects.filter(pub__name='清华大学出版社')
      # 2. 反向查询
        # 2.1 没有related_name的情况:类名小写__其他字段;从Publisher中查询书籍所属出版社
    models.Publisher.objects.filter(book__name='Python从入门到放弃')
        # 2.2 有related_name的情况:related_name小写__其他字段;从Publisher中查询书籍所属出版社
    models.Publisher.objects.filter(books__name='Python从入门到放弃')
    

    多对多的操作

    # models.py
    class Publisher(models.Model):
        name = models.CharField(max_length=32)
        
        def __str__(self):
            return self.name
    
        
    class Book(models.Model):
        name = models.CharField(max_length=32)
        pub = models.ForeignFey(Publisher, on_delete=models.CASCADE, related_name='books')
        
        def __str__(self):
            return self.name
    
        
    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField(Book)
        
        def __str__(self):
            return self.name
    
    # 脚本中
    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名.settings")
    import django
    django.setup()
    
    from app01 import models
    
    # 一, 基于对象查询
    author_obj = models.Author.objects.get(pk=2)
      # 1. 正向查询:这个作者写了几本书
    print(author_obj.all())
      # 2. 反向查询:书的作者是谁
    book_obj = models.Book.objects.get(pk=1)
    print(book_obj.author_set.all())
    
    # 二, 基于字段查询
      # 1. 正向查询
    models.Author.objects.filter(books__name='Python从入门到放弃')
      # 2. 反向查询
    models.Books.objects.filter(author__name='bigbao')
    
    # 三, set([pk1/对象1, pk2/对象2,...]) 设置多对多关系:类似于新增或者重新设置的意思,设置完成后,以前的多对多关系就取消了
      # 3.1
    author_obj.books.set([3,]) # 列表中放入 要和作者的对象有多对多关系的 书的pk即可;这里是设置为只有pk=3的书是这个作者写的
      # 3.2
    author_obj.books.set(models.Book.object.all()) # 所有的书都设置为是这个作者写的;set(是一个QuerySet,QuerySet就是一个列表,列表里面都是对象)
    
    # 四, add(pk1/对象1, pk2/对象2,...) 新增多对多关系
    author_obj.books.add(1, 2)
    author_obj.books.add(对象1, 对象2)
    author_obj.books.add(*models.Book.object.all()) # 不能是列表,所以是列表时要打散
    
    # 五, remove(pk1/对象1, pk2/对象2,...) 删除多对多关系
    author_obj.books.remove(1, 2)
    author_obj.books.remove(对象1, 对象2)
    author_obj.books.remove(*models.Book.object.filter(pk__in=[1,2])) # 不能是列表,所以是列表时要打散
    
    # 六, clear() 清空多对多关系,相当于删除所有多对多关系
    author_obj.books.clear()
    
    # 七, create() 新增
    # 新增一本书并与作者设置多对多关系: 相当于两步操作(先新增一本书,再设置多对多关系)
    author_obj.books.create(name='爬虫从入门到入狱', pub_id=1)
    
    # 八, 反之亦然
    
    # 上面那些操作都有
    book_obj.author_set.set()
    book_obj.author_set.create()
    
    # 九, 一对多反向查询时只能选择对象,pk不起作用;还有clear时,是清空关系而不是清空数据库中的数据,所以一对多时用clear()必须把外键加上null=True才可以,要不然关系清空了,外键所在的表少了一个字段,无法写入数据库
    
  • 相关阅读:
    JavaScript 学习笔记— —数据类型转换
    JavaScript 学习笔记— —自定义属性
    JavaScript 学习笔记— —类型判断
    JavaScript 学习笔记——Math属性及其方法
    cssText
    谈谈document.ready和window.onload的区别
    JavaScript 学习笔记——DOM
    Sql学习笔记一
    [POI 2000] 病毒
    [HAOI 2010] 计数
  • 原文地址:https://www.cnblogs.com/richard_A/p/13804997.html
Copyright © 2011-2022 走看看