小谈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才可以,要不然关系清空了,外键所在的表少了一个字段,无法写入数据库
posted @ 2020-10-12 20:17  richard_A  阅读(167)  评论(0编辑  收藏  举报