5.模型-单表

一、ORM介绍

ORM解决的开发人员不需要编写sql代码,即数据模型的设计不需要依赖特定的数据库,减轻开发人员的工作量,ORM是“对象—关系—映射”Object Relational Mapping)的简称。django的orm功能很强大,但是没有分离出来让别人使用。

类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎。

image-20200813173828507

二、数据库

1、配置开发数据库

Django自带的sqlite3不适合做项目的数据库,在setting.py文件中修改DATABASES配置

DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.mysql',
       'NAME': 'django_ts',
       'USER': 'root',
       'PASSWORD': '123',
       'HOST': '127.0.0.1',
       'PORT': '3306',
   }
}

ENGINE: 配置使用的数据库引擎;
NAME: 数据库名;
USER: 用户名;
PASSWORD: 密码;
HOST: 服务器地址;
PORT: 端口;

修改配置之后启动服务器会报错

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?

这是由于MySQLdb不支持python3,所以django要安装mysqlclient。

pip install mysqlclient

或者在项目的__init__.py文件中配置

import pymysql
pymysql.install_as_MySQLdb()

2、INSTALLED_APPS中应用的数据库迁移

manage.py的migrate命令用于将应用的模型定义或修改同步到数据库,migrate命令会检查INSTALLED_APPS里的配置的应用列表,依次迭代为每个应用的模型定义或修改同步到数据库。Django对于数据库的迁移工作通过两个命令来实现的。

python manage.py makemigrations
python manage.py migrate

makemigrations命令会检测目录下是否存在migrations目录,没有则创建,首先,根据应用的表结构定义会生成一个0001_initial.py文件,里面定义了数据表的Schema,在执行migrate命令就可以创建数据表了。

对于将来应用的每一次表结构定义修改,都需要再次执行makemigrations命令,Django会重新生成一个新的数据库迁移文件,记录表结构之间的差异,命名规则会对上一个迁移文件加1,如0002_xxx,0003_xxx,之后再执行migrate命令让新的迁移文件生效,完成表结构加定义的修改

为了保证迁移不会重复执行,Django会把每一次的数据库迁移记录记录到django_migrations表中,每一次执行migrate前都比较迁移文件是否记录在表中,没出现的才会执行。

3、查看原生的sql语句

方式一:

# setting.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level': 'DEBUG',
        },
    }
}

方式二:

from django.db import connection
print(connection.queries)

三、单表操作

1、创建表

在app的目录下的models.py文件中创建模型,

from django.db import models

class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)
    # create table userinfo(id int primary_key auto_increment,name varchar(16),age int,current_date date)
    name = models.CharField(max_length=16)
    age = models.IntegerField()
    current_date = models.DateField() 

2、字段

1)CharFiled 字符串字段,用于较短的字符串,CharFiled必须有一个参数maxlength,用于从数据层和Django校验层限制该字段所允许的最大字符数。

2)IntegerField 用于保存一个整数

3)DecimalField 一个浮点数,必要的两个参数

参数 描述
max_digits 总位数(不包括小数点和符号)
decimal_palce 小数位数

4)AutoField 一个IntegerFiled,添加记录时自动增长

5)EmailField 一个带有检查Email合法性的CharField,不接受maxlength参数。

6)DateField 一个日期字段,共有下列额外的可选参数,

​ Argument:描述;

​ auto_now:当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间,通常用于表示 "last-modified" 时间戳。

​ auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.(仅在admin中有意义)

7)DateTimeField 一个日期时间字段,类似DateField支持同样的附加选项。

8)ImageField 类似FileField,校验上传对象是否是一个合法图片,有两个可选参数:height_field和width_field如果提供这两个参数,则图片将按提供的高度和宽度规格保存。

9)FileField 一个文件上传字段,

3、参数

1)null

​ 如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

2)blank

​ 如果为True,该字段允许不填。默认为False。
​ 要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
​ 如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。

3)default

​ 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值

4)primary_key

​ 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。

5)unique

​ 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

6)choices
一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,前端显示的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

enroll_status_choices = (('signed', "已报名"),
                         ('unregistered', "未报名"),
                         ('studying', '学习中'),
                         ('paid_in_full', "学费已交齐"))
<!-- 前端页面 -->
{{ consult.get_status_display }}

7)db_index
如果db_index=True 则代表着为此字段设置数据库索引。DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

8)auto_now_add

​ 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

9)auto_now

​ 配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。

10)vserbose_name 起个复杂点的名字,一般作为中文解释用。可以直接在引号中写中文名,在多表关系中如果to关键字没有写,直接写了表名,那么该关键字不可省略

consultant = models.ForeignKey('UserInfo', verbose_name="销售", related_name='customers', blank=True, null=True, on_delete=models.CASCADE )

11)

4、添加表记录

在python中orm的对应关系有三种:

    类 ---------->表

    类对象 ---------->行(记录)

    类属性 ---------->表的字段(重点)

添加表记录的方式有四种:

都需要提前导入models.py

from app01 import models

方式一:

    stu_obj = models.Student(
        name='an',
        age=11,
    )
    stu_obj.save()

方式二:

stu_obj2 = models.Student.objects.create(name='bn', age=12)
stu_obj2.save()

方式三(批量创建):

    objs_list = []
    for i in range(20):
        obj = models.Student(
            name='工具人%s' % i,
            age=15+i,
        )
        objs_list.append(obj)
    models.Student.objects.bulk_create(objs_list)

方式四:

models.Student.objects.update_or_create(
        name='jn',
        defaults={
            'age': 44,
        }
    )

有就更新,没有就创建,

5、更新

更新 update()方法 model方法对象不能调用更新方法 报错信息'Student' object has no attribute 'update',只能queryset调用

models.Student.objects.get(name='gn').update(age=39)  # 报错
models.Student.objects.filter(name='gn').update(age=18)

6、删除

删除,delete() ,queryset集合和model对象都可以调用

models.Student.objects.get(id=3).delete()
models.Student.objects.filter(name='dn').delete()

四、查询接口(方法)

queryset中支持链式操作

book = models.Book.objects().all().order_by('-cid').first()

1、普通的查询方法

下面是一些与单表查询的一些方法

1)all()

查询所有的数据,返回的是queryset集合,

<QuerySet [<Student: Student object (1)>,
<Student: Student object (2)>, <Student: Student object (3)>, <Student: Student object (4)>,
<Student: Student object (5)>]>
all_objs = models.Student.objects.all()
    for i in all_objs:
        print(i.name)
    print(all_objs)

all[0] 返回queryset匹配到中第一个对象,没有报IndexError错误。

2)first()

返回queryset类型数据中的第一个记录,返回时model对象。如果没有匹配到对象则为None,如果queryset没有定义排序,则按主键自动排序,和all[0]的区别就是没有匹配到不会报错。

all_objs = models.Student.objects.all().first()

last() 返回最后一个,同first()

3)filter(**kwargs)

返回包含了与所给筛选条件相匹配的对象,返回的也是queryset集合,查询不到内容不会报错,返回一个<QuerySet []>空的queryset,支持切片操作。

传入的参数用逗号隔开,参数之间是and关系,可以打散传参。

objs = models.Student.objects.filter(id=2)  # 查找id=2的记录
print(objs)

models.Student.object.filter(**{'id'=1, 'name'='an'}).update(age=21)

4)get(**kwargs)

返回与所给筛选条件相匹配的对象,返回models对象,而且get方法有且只有一个结果,如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。 Book.objects.get(id=1)。

obj = models.Student.objects.get(name='an')
print(obj)

关于get()方法的两个错误:

报错1:查询结果超过1个,

app01.models.Student.MultipleObjectsReturned: get() returned more than one Student -- it returned 2!

报错2:没有查到任何内容报错,

app01.models.Student.DoesNotExist: Student matching query does not exist.

5)exclude(**kwargs)

排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作,用这个exclude,返回值是queryset类型

query = models.Student.object.exclude(id=1)	# 返回id不等于6的所有的对象,

queery = models.Student.object.filter(age=38).exclude(id=6)	# 在queryset基础上调用,

6)order_by(*field)

queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset集合。

query = models.Student.objects.all().order_by('id','-age')

默认按照id升序排列,降序排列在前面加一个负号,order_by('-age'),上面写的是先按照id升序排列,id一样在按照,age降序排列

7)reverse()

queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型。

query = models.Student.objects.all().order_by('age').reverse()

8)count()

queryset类型的数据来调用,返回数据库中匹配查询(Queryset)的对象数量。

9)exists()

queryset类型来调用,如果QuerySet包含数据,就返回True,否则返回False。

空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits。

10)values(*field)

用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。

11)values_list(*field)

它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列。

12)distinct()

values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录。

2、模糊查询

__in 查询在某一范围的

# price值等于这三个里面的任意一个的对象
models.Book.objects.filter(price__in=[100,200,300]) 

__gt 大于

__gte 大于等于

# 大于,大于等于是price__gte=100,别写price>100,这种参数不支持
models.Book.objects.filter(price__gt=100)  

__lt 小于

__lte 小于等于

models.Book.objects.filter(price__lt=100)	# 小于

__exact 等于like 'xxx'

__iexact 等于忽略大小写like 'xxx'

__contains 包含like '%xxx%'

__icontains 包含,忽略大小写like '%xxx%',但是对于sqlite来说,contains的作用效果等同于icontains。

__isnull 判断是否为空

# 查询用户名不为空的学生
models.Student.objects.filter(name__isnull=True)
# 查询用户名不为空的学生
models.Student.objects.filter(name__isnull=False)

不等于/不包含于:

# 查询id不为3的学生
models.Student.objects.filter().excute(id=3)
# 查询年龄不在[10, 20]的学生
models.Student.objects.filter().excute(age__in=[10. 20])

__range sql的between and,大于等于100,小于等于200

models.Book.objects.filter(price__range=[100,200]) 

__contains 包含某个字符串

models.Book.objects.filter(title__contains="python")  #title值中包含python的
models.Book.objects.filter(title__icontains="python") #不区分大小写
models.Book.objects.filter(title__startswith="py") #以什么开头,istartswith  不区分大小写,endswith同理
models.Book.objects.filter(pub_date__year=2012)

3、日期相关

all_books = models.Book.objects.filter(pub_date__year=2016) #找2016年的所有书籍

all_books = models.Book.objects.filter(pub_date__year__gt=2016)#找大于2016年的所有书籍

all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)

找2019年月份的所有书籍,如果明明有结果,却查不出结果,是因为mysql数据库的时区和django的时区不同导致的,将django中的settings配置文件里面的USE_TZ = True改为False,就可以查到结果了,以后这个值就改为False,因为用的mysql数据库才会有这个问题,其他数据库没有这个问题。

# models.py
class Brithday(models.Model):
    name = models.CharField(max_length=16)
    date = models.DateField()
    def __str__(self):
        return self.name
import datetime
current_date = datetime.datetime.now()
# print(current_date)

models.Brithday.objects.create(name='an', date=current_date)
models.Brithday.objects.create(name='bn', date='2020-08-01')

五、ORM字段与数据库实际字段的对应关系

'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',

六、外部文件使用django的models

# 外部文件使用django的models,需要配置django环境
import os
if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SingleTablehw.settings')
    import django
    django.setup()
    from app01 import models
	# 以上是配置
    obj_list = []
    for i in range(10):
        obj = models.Book(
            title='葵花宝典第%s版' % i,
            price=100+i*2,
            pub_date='200%s-01-01' % i,
            publish='笑傲江湖出版社',
        )
        obj_list.append(obj)

    models.Book.objects.bulk_create(obj_list)

七、pycharm使用mysql数据库报错

pycharm连接数据库报错:

Connection to sthw@localhost failed. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.

在URL后面添加?serverTimezone=GMT修改时区。‘

posted @ 2020-09-16 20:01  journeyerxxx  阅读(185)  评论(0编辑  收藏  举报
返回顶部