模型层-单表操作

1 测试脚本

当只是想测试django中的某一个py文件内容(大部分是modles.py),那么可以不用书写前后端交互的形式,而是直接写一个测试脚本。比如想测试orm表相关的各种操作比较复杂,测试时就不要涉及前后端交互。

脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以。

测试环境的准备 去manage.py中拷贝前四行代码,然后自己写两行

import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dgtest.settings")
    import django
    django.setup()
    # 在这个代码块的下面写测试脚本
    from app01 import models

    books = models.Book.objects.all()
    print(books)

 

2 查看内部sql语句的方式

# 方式1
res = models.User.objects.values_list('name','age')  # <QuerySet [('jason', 18), ('egonPPP', 84)]>
print(res.query)
queryset对象才能够点query查看内部的sql语句


# 方式2:所有的sql语句都能查看
# 去配置文件中配置一下即可
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

 

4 ORM简介

查询数据层次图解:如果操作mysql,ORM是在pymysq之上又进行了一层封装

  • MVC或者MTV框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
  • ORM是“对象-关系-映射”的简称。

 

5 单表操作

5.1 创建表

5.1.1 创建模型

创建名为book的app,在book下的models.py中创建模型:

from django.db import models

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=64)
    pub_data = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.CharField(max_length=12)
    def __str__(self):
        return self.name

5.1.2 更多字段

每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:

AutoField(Field)
    - int自增列(主键字段),必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

BigAutoField(AutoField)
    - bigint自增列,必须填入参数 primary_key=True
    from django.db import models
    class UserInfo(models.Model):
        # 自动创建一个列名为id的且为自增的整数列
        username = models.CharField(max_length=32)
    class Group(models.Model):
        # 自定义自增列
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        
SmallIntegerField(IntegerField):
    - 小整数 -32768 ~ 32767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
    - 正小整数 0 ~ 32767
    
IntegerField(Field)
    - 整数列(有符号的,对应int) -2147483648 ~ 2147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
    - 正整数 0 ~ 2147483647

BigIntegerField(IntegerField):
    - 长整型(有符号的),一般用于记录手机号,int不够用,也可以使用CharField类型存储 -9223372036854775808 ~ 9223372036854775807
   
BooleanField(Field)
    - 布尔值类型 该字段传布尔值,数据库存0(false)或1(true),应用场景是标识删除数据is_delete

NullBooleanField(Field):
    - 可以为空的布尔值

CharField(Field)
    - 字符类型
    - 必须提供max_length参数, max_length表示字符长度

TextField(Field)
    - 文本类型 该字段可以用来存大段内容,没有字符数限制,如文章、博客

EmailField(CharField):
    - 邮件类型,Django Admin以及ModelForm中提供验证机制
    - 本质是varchar(254),该字段不是给models看的,而是给校验性组件看的,会自动存储邮箱格式

IPAddressField(Field)
    - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

GenericIPAddressField(Field)
    - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
    - 参数:
        protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
        unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"

URLField(CharField)
    - 字符串类型,Django Admin以及ModelForm中提供验证 URL

SlugField(CharField)
    - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

CommaSeparatedIntegerField(CharField)
    - 字符串类型,格式必须为逗号分割的数字

UUIDField(Field)
    - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

FilePathField(Field)
    - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
    - 参数:
            path,                      文件夹路径
            match=None,                正则匹配
            recursive=False,           递归下面的文件夹
            allow_files=True,          允许文件
            allow_folders=False,       允许文件夹

FileField(Field)
    - 文件字段,是字符串类型,存的是文件路径,如 /data/a.txt,路径保存在数据库,文件上传到指定目录
    - 参数:
        upload_to = ""      上传文件的保存路径 给该字段传一个文件对象,会自动将文件保存到指定目录下,然后将文件路径保存到数据库中,数据库只存路径,用户拿到路径再访问对应的资源
        storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

ImageField(FileField)
    - 图片字段,是字符串类型,路径保存在数据库,文件上传到指定目录
    - 参数:
        upload_to = ""      上传文件的保存路径
        storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
        width_field=None,   上传图片的高度保存的数据库字段名(字符串)
        height_field=None   上传图片的宽度保存的数据库字段名(字符串)

DateTimeField(DateField)
    - 日期+时间格式 对应数据库datetime YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field)
    - 日期格式  不设置参数,则每次创建数据时,需手动输入格式化日期    YYYY-MM-DD
    - DateField(auto_now_add=True) 创建数据时,会自动将创建时间记录下来,之后只要不人为修改,就一直不变,可用于记录注册日期
    - DateField(auto_now=True)  每次操作数据时,该字段会自动将当前时间更新

TimeField(DateTimeCheckMixin, Field)
    - 时间格式      HH:MM[:ss[.uuuuuu]]

DurationField(Field)
    - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
    - 浮点型

DecimalField(Field)
    - 10进制小数
    - 参数:
        max_digits,小数总长度
        decimal_places,小数位长度(小数点后的长度)

BinaryField(Field)
    - 二进制类型

ORM字段与MySQL字段对应关系

 PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
        '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允许我们自定义新的字段,下面我来自定义对应于数据库的char类型

from django.db import models

# Create your models here.
# Django中没有对应的char类型字段,但是我们可以自己创建

class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
    
#应用上面自定义的char类型
class Class(models.Model):
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)
    class_name=FixCharField(max_length=16)
    gender_choice=((1,'男'),(2,'女'),(3,'保密'))
    gender=models.SmallIntegerField(choices=gender_choice,default=3)

5.1.3 更多参数

(1)null 
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
 
(2)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
 
(3)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
 
(4)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
 
(5)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的  ForeignKey(unique=True) 等价于 OneToOneField()
 
(6)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。

(7)verbose_name
用来写字段的注释  Admin中显示的字段名称

(8)editable 
Admin中是否可以编辑

(9)help_text 
Admin中该字段的提示信息

(10)db_index=True 
代表着为此字段设置索引

5.1.4 class Meta

ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。

class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)
    class Meta:       
        db_table = "table_name"   # ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名
        # 联合索引
        index_together = [
            ("pub_date", "deadline"),
        ]

        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)
        
        # 指定默认按什么字段排序。只有设置了该属性,我们查询到的结果才可以被reverse()。
        ordering = ('name',)

        # admin中显示的表名称
        verbose_name

        # verbose_name加s
        verbose_name_plural

5.1.5 settings配置

若想将模型转为mysql数据库中的表,需要在settings中配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dgtest',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'ATOMIC_REQUEST': True,
        'OPTIONS': {
            "init_command": "SET storage_engine=MyISAM",
        }
    }
}
'''
'NAME':要连接的数据库,连接前需要创建好
'USER':连接数据库的用户名
'PASSWORD':连接数据库的密码
'HOST':连接主机,默认本机
'PORT':端口 默认3306
'ATOMIC_REQUEST': True, 设置为True同一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败),是全局性的配置。 
如果要对某个http请求放水(然后自定义事务),可以用non_atomic_requests修饰器。

'OPTIONS': {
             "init_command": "SET storage_engine=MyISAM",
            }
设置创建表的存储引擎为MyISAM,INNODB

注意1:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的init,在里面写入: 

import pymysql
pymysql.install_as_MySQLdb()

最后通过两条数据库迁移命令即可在指定的数据库中创建表 :

python manage.py makemigrations
python manage.py migrate

注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "book"
]

注意3:如果报错如下:

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下:

通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql
这个路径里的文件把

if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

注释掉就可以了。

5.2 添加表纪录

方式1

# create方法的返回值book_obj就是当前这个被创建对象本身
book_obj=models.Book.objects.create(title="python葵花宝典",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")

方式2

book_obj=models.Book(title="python葵花宝典",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")
book_obj.save()

5.3 修改表纪录

pk会自动查找到当前表的主键字段,指代的是当前表的主键字段,用了pk就不需要知道当前表的主键字段到底叫id、uid、pid、还是sid

方式1

models.User.objects.filter(pk=4).update(name='egonDSB')   #批量更新

方式2

user_obj = models.User.objects.filter(pk=2).first()  # 取出数据对象,单个更新
user_obj.name = 'egon666'
user_obj.save()

5.4 删除表纪录

在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象

如果不想级联删除,可以设置为:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

方式1

res = models.User.objects.filter(pk=3).delete()  #批量删除,返回的是当前这个sql操作影响的行数

 方式2

user_obj = models.User.objects.filter(pk=7).first()  #filter筛选出来的是queryset对象,是列表,直接删就是批量删除,取出数据对象单独删除
user_obj.delete()

5.5 查询表纪录

必知必会13条 查询API

<1> all():                  查询所有结果,返回值是queryset,列表套数据对象
  
<2> filter(**kwargs):       带有过滤条件的查询,返回值是queryset;括号内多个参数之间逗号隔开,并且默认是and关系
  
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
  
<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象
                            models.User.objects.exclude(name='jason')  查询结果把'name'为jason这个数据排除在外
 
<5> order_by(*field):       对查询结果排序('-id') 返回值是queryset 列表套数据对象
                            models.User.objects.all().order_by('age')  按年龄排序,默认升序
                            models.User.objects.all().order_by('-age') 降序
  
<6> reverse():              对查询结果反向排序 反转的前提是 数据已经排过序了
                            models.User.objects.all().order_by('age').reverse()
  
<8> count():               返回数据库中匹配查询(QuerySet)的对象数量。
                           models.User.objects.count() 统计当前表的数据个数
  
<9> first():                返回第一条记录,拿queryset里面的第一个元素
  
<10> last():                返回最后一条记录,拿queryset里面的最后一个元素
  
<11> exists():              如果QuerySet包含数据,就返回True,否则返回False 基本用不到因为数据本身就自带布尔值
                            models.User.objects.filter(pk=3).exists() 判断某个数据在不在表里
 
<12> values(*field):        取值操作,返回ValueQuerySet,列表套字典
                            models.User.objects.values()        相当于 select * from user
                            models.User.objects.values('name')  相当于 select name from user  按字段查
                            models.User.objects.filter().values('name') 加filter筛选,相当于select name from user where...
                            models.User.objects.values('name').first()  返回字典,从queryset中取出第一个键值对元素
    
<13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列(列表套元祖),values返回的是一个字典序列
 
<14> distinct():            从返回结果中剔除重复纪录
                            models.User.objects.all().distinct()                 主键不同,直接去重没效果,去不了
                            models.User.objects.values('name', 'age').distinct() 按字段取值,取出完全相同的额字段再去重

5.6 单表查询之神奇的双下划线

#1 年龄大于35岁的数据  返回queryset列表套数据对象
res = models.User.objects.filter(age__gt=35)
print(res)

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

#3 大于等于 小于等于
res = models.User.objects.filter(pk__gte=3)
print(res)
res = models.User.objects.filter(pk__lte=8)
print(res)

#4 年龄是18 或者 32 或者40 成员查询in
res = models.User.objects.filter(age__in=[18,32,40])
print(res)

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

#6 查询出名字里面含有s的数据  模糊查询,默认区分大小写
# 原生sql select * from user where name like %s%
res = models.User.objects.filter(name__contains='s')
print(res)

#7 模糊查询 忽略大小写  查询出名字里面含有p的数据  
res = models.User.objects.filter(name__icontains='p')
print(res)

#8 模糊查询 以'j'开头, 以'w'结尾
res = models.User.objects.filter(name__startswith='j')
res1 = models.User.objects.filter(name__endswith='j')
print(res,res1)

#9 以年份查数据 取出表中注册时间是2020年的所有数据
res = models.User.objects.filter(register_time__year='2020')

#10 以月份查数据 取出表中注册时间是1月份的所有数据
res = models.User.objects.filter(register_time__month='1')

#11 date字段
#查询出版日期是2005年1月1日的数据
models.Book.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
#查询出版日期是大于2005年1月1日的数据
models.Book.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
#查询出版日期大于等于2周的数据
models.Book.objects.filter(pub_date__week_day__gte=2)

 

posted @ 2022-12-27 21:17  不会钓鱼的猫  阅读(29)  评论(0编辑  收藏  举报