zoukankan      html  css  js  c++  java
  • Django 框架之 Models

    1. 数据库配置

    • Django默认支持sqlite, mysql, oracle, postgresql 数据库:
      • Django默认使用sqlite数据库,引擎名称:django.db.backends.sqlite3
      • MySQL 引擎名称:django.db.backends.mysql
    • MySQL 驱动程序:
      • MySQLdb(mysql python)
      • mysqlclient
      • MySQL
      • PyMySQL(纯python的mysql驱动程序)
    # 示例: 更改数据库
    # settings.py
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'books',    # 数据库名称
            'USER': 'root',     # 数据库用户名
            'PASSWORD': 'root', # 数据库密码
            'HOST': '',         # 数据库主机,留空默认为localhost
            'PORT': '3306',     # 数据库端口
        }
    }
    
    # 注意:
    #   启动项目时,会报错: no module named MySQLdb
    #   这是因为django默认导入的驱动是 MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是
    #   PyMySQL
    #解决方法:
    #   __init__.py 中加入以下代码:
    #   import pymysql
    #   pymysql.install_as_MySQLdb()
    

    2. ORM(对象关系映射)

    • 表的创建:
      • 作者模型: 一个作者有姓名;
      • 作者详细模型: 把作者的详情放到详情表,包含性别, email地址和出生日期。作者详情模型和作者模型之间
        是一对一的关系(one-to-noe),在大多数情况下,我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
      • 出版商模型: 出版商有名称,地址,所在城市,省,国家和网站;
      • 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和数据的关系
        就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系
        (one-to-many),也被称作外键。
    # 实体类
    # 项目名(orm)/app01/models.py
    from django.db import models
    class Publisher(models.Model):
        name = models.CharField(max_length=30, verbose_name='名称')
        address = models.CharField('地址', max_length=50)
        city = models.CharField('城市', max_length=60)
        state_province = models.CharField(max_length=30)
        country = models.CharField(max_length=50)
        website = models.URLField()
    
        class Meta:
            verbose_name = '出版商'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    class Author(models.Model):
        name = models.CharField(max_length=30)
    
        def __str__(self):
            return self.name
    
    class AuthroDetail(models.Model):
        sex = models.BooleanField(max_length=1, choices=((0, '男'), (1, '女'),))
        email = models.EmailField()
        address = models.CharField(max_length=50)
        birthday = models.DateField()
        author = models.OneToOneField(Author, on_delete=models.CASCADE)
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        authors = models.ManyToManyField(Author)
        publisher = models.ForeignKey(Publisher, null=True, on_delete=models.SET_NULL)
        publication_date = models.DateField()
        price = models.DecimalField(max_digits=5, decimal_places=2, default=10)
    
        def __str__(self):
            return self.title
    
    # 创建表
    # iTerm 中进入 项目名(orm) 下
    python3 manage.py makemigrations
    python3 manage.py migrate
    
    # 备注:
    # 1. on_delete 属性说明
    #       CASCADE: 级联删除
    #       PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出 ProtectedError 错误;
    #       SET_NULL: 置空模式,删除的时候, 外键自动被设置为空, 前提就是 blank=True, null=True;
    #       SET_DEFAULT: 删除的时候,外键自动设置为默认值。定义外键的时候,需要加上一个默认值;
    #       SET(): 自定义一个值,该值当然只能是对应的实体;
    
    # 2. 每个数据模型都是 django.db.models.Model 的子类, 它的父类Model包含了所有必要的和数据库交互的方法,
    #   并提供了一个简洁漂亮的定义数据库字段的语法;
    

    2.1 模型常用的字段类型参数

    # CharField
        # 字符串字段,用于较短的字符串
        # CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数
    
    # InterField: 用于保存一个整数
    # FloatField: 一个浮点数, 必须提供两个参数:
        # max_digits: 总位数(不包括小数点和符号)
        # decimal_places: 小数点位数
        # 例如,要保存最大值为 999(小数点后保存2位)
        #   models.FloatField(..., max_digits=5, decimal_palces=2)
    
    # AutoField
        # 一个 IntegerField, 添加记录时,会自动增长,通常不需要直接使用这个字段
        # 自定义一个主键: my_id=models.AutoField(primary_key=True)
        # 如果不指定主键的话,系统会自动添加一个主键字段到你的 model
    
    # BooleanField
        # admin 用 checkbox 来表示此类字段
    
    # TextField
        # 一个容量很大的文本字段
        # admin 用一个 <textarea> 表示该字段数据
    
    # EmailField
        # 一个带有检查Email合法性的 CharField, 不接受 maxlength 参数
    
    # DateField
        # 一个日期字段, 共有下列额外的可选参数:
        # Argument  描述
        # auto_now  当对象被保存时,自动将该字段的值设置为当前时间, 通常用于表示"last-modified"时间戳
        # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间
    
    # DateTimeField
        # 一个日期时间字段, 类似 DateField 支持同样的附加选项
    
    # FileField
        # 一个文件上传字段
        # 要求一个必须有的参数, upload_to:用于保存上传文件的本地文件系统路径,这个路径必须包含 strftime
        # formatting: 格式化上传文件的 date/time
    
    # ImageField
        # 类似 FileField, 不过要校验上传对象是否是一个合法图片;
        # 两个可选参数: height_field, width_field
        # 如果提供这两个参数,则图片将按提供的高度和宽度保存
    
    # 注意: 在 model 中使用 FileField 或 ImageField, 需要以下步骤,
    #   1) settings.py 中定义一个完整路径给 MEDIA_ROOT,以便让Django在此处保存上传的文件;
    #   (出于性能考虑,这些文件并不保存到数据库),定义 MEDIA_URL,作为该目录的公共 URL,要确保该目录对WEB服务器
    #   用户帐号是可写的
    #   2) 在model添加 FileField 或 ImageField,需要确保定义了 upload_to 选项,以告诉 Django 使用
    #   MEDIA_ROOT 的哪个子目录保存上传文件,数据库要保存的只是文件的路径(相对于 MEDIA_ROOT)
    #   3) 可以使用Django提供的 get_<#fieldname>_url 函数,获取图像的绝对路径:
    #   例如,ImageField 叫作 mug_shot, 就可以在模板中 {{ object.#get_mug_shot_url }} 获取图像
    
    # URLField
        #   用于保存 URL, 若 veriry_exists 参数为 True(默认), 给定的 URL 会预先检查是否存在(即URL 是否
        #   被有效装入且没有返回404)
    
    # NullBooleanField
        #   类似 BooleanField, 不过,允许NULL作为其中一个选项,推荐使用这个字段,而不要用 BooleanField 加
        #   null = True 选项
        # damin 用一个选择框 <select> (三个选择的值: "Unknow", "Yes" 和 "No") 来表示这种字段数据
    
    # SlugField
        # "Slug" 是一个报纸术语, slug 是某个东西的小小标记(短签),只包含字母,数字,下划线和连字符;
        # 它们通常用于 URLs;
        # maxlength 默认值为 50
    
    # XMLField
        # 校验值是否为合法XML的 TextField,
        # 必须提供参数: schema_path
        # 它是一个用来校验文本的 RelaxNGschema 的文件系统路径
    
    # FilePathField
        # 某个特定目录下的文件名,支持三个特殊的参数,其中第一个是必须提供的.
        # path: 必需参数,一个目录的绝对文件系统路径.
        # match: 可选参数, 一个正则表达式,作为一个字符串, FilePathField 将使用它过滤文件名.
        # 这个正则表达式只会应用到 base filename 而不是路径全名
        # recursive 可选参数, 要么True,要么False, 默认值是 False. 是否包括 path 下面的全部子目录.
    
    # IPAddressField
        # 一个字符串形式的 IP 地址, 例如 '127.0.0.1'
    
    # CommaSeparatedIntegerField
        # 用于存放逗号分隔的整数值, 必须要有 maxlength 参数;
    

    2.2 Field 重要参数

    null: 数据库中字段是否可以是空
    
    blank: django 的 Admin 中添加数据时,是否允许空值
    
    default: 设定缺省值
    
    editable: 如果为假, admin 模式下将不能改写, 缺省为真;
    
    primary_key: 设置主键, 如果没有设置,Django 创建表时,会自动加上;
    
    unique: 数据唯一;
    
    verbose_name: Admin 中字段的显示名称;
    
    validator_list: 有效性检查, 非有效产生 django.core.validators.ValidationError 错误
    
    db_column, db_index: 如果为真,将为此字段创建索引
    
    choices: 一个用来选择值的二维数组,第一个值是实际存储的值, 第二个用来备选;
    

    3. 表的操作

    3.1 增(create, save)

    from app01.models import *
    
        # create 方式一: Author.objects.create(name='Jame')
    
        # create 方式二: Author.objects.create(**{'name':'Jame'})
        # 以字典的方式插入,前面需要添加两个星号
    
        # save 方式一:
            author = Author(name='Jame')
            author.save()
    
        # save 方式二:
            author=Author()
            author.name='Jame'
            author.save()
    
    
    # 如何处理外键关系的字段,如一对多的publisher和多对多的authors
    
    # 一对多(ForeignKey):
        # 方式一: 由于绑定一对多的字段,例如publisher,存到数据库中的字段为 publisher_id,
        # 所以我们可以直接给这个字段设定对应值:
        Book.objects.create(title='Python 入门',
                            publisher_id=2,
                            publication_date='2015-4-18',
                            price=98)
    
        # 方式二:
        #      首先,获取绑定的 Publisher对象
        pub_obj = Publisher(name='河北教育出版社',address='保定',city='保定',
                  state_province='河北',country='China',website='http://www.edu.com')
        或
        pub_obj=Publisher.objects.get(id=1)
    
        # 将 publisher_id=2 改为 publisher=pub_obj
    
    # 多对多(ManyToManyField):
    
        author1 = models.Author.objects.get(id=1)
        author2 = models.Author.objects.filter(name='zhangsan')[0]
        book = models.Book.objects.get(id=1)
        book.author.add(author1, author2)       # book.author 表示book绑定的 author 对象集合
    
        # 等同于
        ----------- 正向查询(从拥有外键的表开始查找)
        book.author.add(*[author1, author2])
        book.author.remove(*[author1, author2])
    
        ----------- 反向查询
        book=models.Book.objects.filter(id__gt=1)       # id__gt 表示 id大于
        authors=models.Author.objects.filter(id=1)[0]
        authors.book_set.add(*book)         # Author 类里面没有与 book 相关的属性
        authors.book_set.remove(*book)
    
    
    # 注意: 如果第三张表是通过 models.ManyToManyField() 自动创建的, 那么绑定关系只有上面一种方式
    #      如果第三张表是自己创建的:
        class Book2Author(models.Model):
            author = models.ForeignKey("Author")
            book = models.ForeignKey("Book")
    
            # 绑定联合唯一索引
            class Meta:
                unique_together=["author", "book"]
    
    
        # 保存数据
        s = models.Book2Author.objects.create(author_id = 1, Book_id = 2)
        s.save()
    

    3.2 删除(delete)

    # 单表:
    Book.objects.filter(id=1).delete()
    
    # 备注: 表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在 Book_authors 表中有两条
    #  相关信息,这种删除方式就是 django 默认的级联删除。
    
    
    # 多对多关系: remove() 和 clear() 方法
    # 正向
    book = models.Book.objects.filter(id=1)
    
    # 删除第三张表中和id=1关联的所有关联信息
    book.author.clear()     # 清除与book中 id=1 关联的所有数据
    book.author.remove(*[1, 2, 3, 4])
    
    
    # 反向
    author = models.Author.objects.filter(id=1)
    author.book_set.clear()     # 清除与book中 id=1 关联的所有数据
    

    3.3 修改(update 和 save)

    # 单表:
    # 方式一: update 方法直接设定对应属性
        Book.objects.filter(id=2).update(title='Python')    # 不能用 get(id=2)
    
        # SQL
        #   UPDATE 'app01_book' SET 'title' = 'Python' WHERE 'app01_book'.'id'=2; args=('Python', 2)
    
        # 不能用get的原因是: update 是 QuerySet对象的方法, get返回的是一个model对象,它没有 update 方法,
        # 而 filter 返回的是一个 QuerySet 对象;
    
    
    # 方式二: save 方法会将所有属性重新设定一遍, 效率低
        obj=models.Book.objects.filter(id=2)[0]
        obj.title='Python'
        obj.save()
    
    # SQL:
    # SELECT 'app01_book'.'id','app01_book'.'title','app01_book'.'price','app01_book'.'color',
    # 'app01_book'.'page_num','app01_book'.'publisher_id' FROM 'app01_book'
    # WHERE 'app01_book'.'id'=3 LIMIT 1;
    
    # UPDATE 'app01_book' SET 'title'='Python','price'=22,'color'='red','page_num'=223,
    # 'publisher_id'=1 WHERE 'app01_book'.'id'=3;
    

    3.3.1 控制台输出SQL语句

    # setting 加上日志记录部分
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console':{
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'propagate': True,
                'level': 'DEBUG',
            },
        }
    }
    

    3.4 查询(filter, valued等)

    1. 查询相关API:
      • filter(**kwargs): 包含了与所给筛选条件相匹配的对象;
      • all(): 查询所有结果;
      • get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的
        对象超过一个或者没有都会抛出错误。
    2. 对查询的结果进行再处理(例如,objects.filter.values())
      • values(*field): 返回一个ValueQuerySet,一个特殊的QuerySet,运行后得到的并不是一系列
        model的实例化对象,而是一个可迭代的字典序列;
      • exclude(**kwargs): 包含了与所给筛选条件不匹配的对象;
      • order_by(*field): 对查询结果排序;
      • reverse(): 对查询结果反向排序;
      • distinct(): 从返回结果中剔除重复记录;
      • values_list(*field): 返回一个元组序列;
      • count(): 返回数据库中匹配查询(QuerySet)的对象数量;
      • first(): 返回第一条记录;
      • last(): 返回最后一条记录;
      • exists(): 如果QuerySet包含数据,就返回 True, 否则返回 False。
    # 补充:
    # 扩展查询,有时候Django的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra:
    # extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    
    1) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-12-29'"})
       q = q.extra(order_by=['-is_recent'])     # 负号表示逆序
    
    2) Blog.objects.extra(
        select = SortedDict([('a', '%s'), ('b', '%s')]),
        select_params=('one', 'two'))
    
    3) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    

    3.4.1 QuerySet

    1. 惰性机制: Publisher.objects.all()或者.filter()等都只是返回一个QuerySet(查询结果集对象),
      它并不会马上执行SQL, 而是当调用QuerySet的时候才执行。
    2. QuerySet的特点:
      • 可迭代的
      • 可切片
    # 示例:
    objs = models.Book.objects.all()    #[obj1, obj2, obj3...]
    
    # 可迭代
        for obj in objs:        # 每一个obj,就是一个行对象
            print('obj:',obj)
    
    # 可切片
        print(objs[1])
        print(objs[1:4])
        print(objs[::-1])
    
    
    # QuerySet 是具有cache的
        # 当遍历QuerySet时,所有匹配的记录会从数据库获取,然后转换成Django的model,这被称为执行(evaluation)
        # 这些model会保存在QuerySet内置的cache中,如果再次遍历这个QuerySet,不需要再去数据库查询
        objs=models.Book.Objects.filter(id=3)
    
        for i in objs:
            prin(i)
    
        # SQL 语句只运行一次
    
        for i in objs:
            print(i)
    
    
    # 当QuerySet非常巨大时,cache会成为问题
        # 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的QuerySet可能会锁住系统
        # 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生QuerySet cache, 可以使用 iterator()
        # 方法来获取数据,处理完数据就将其丢弃
        objs=Book.objects.all().iterator()      # 迭代器对象
    
        for obj in objs:
            print(obj.name)
    
        # 当然,使用iterator()方法来防止生成cache, 意味着遍历同一个QuerySet时,会重复执行查询。所以使用
        # iterator()的时候要当心,确保代码在操作一个大的QuerySet时,没有重复执行查询。
    

    3.4.2 单表条件查询 多表条件关联查询

    # 示例:了不起的双下划线(__)之单表条件查询
    
    models.Tb1.objects.filter(id__lt=10, id__gt=1)      # 获取id大于1, 且小于10的值
    
    models.Tb1.objects.filter(id__in=[11, 22, 33])      # 获取id等于11, 22, 33的数据
    models.Tb1.objects.exclude(id__in=[11, 22, 33])     # not in
    
    models.Tb1.objects.filter(name__contains="ven")
    models.Tb1.objects.filter(name__icontains="ven")    # icontains 表示大小写不敏感
    
    models.Tb1.objects.filter(id__range=[1, 2])         # 范围 between and
    
    startswith, istartswith,  endswith,  iendswith
    
    
    # 示例: 多表条件关联查询
    
    # 正向条件查询
    models.Book.objects.filter(title="Python").values("id")
    
    # 一对多
    models.Book.objects.filter(title="Python").values("publisher__city")
    
    # 多对多
    models.Book.objects.filter(title="Python").values("author__name")
    models.Book.objects.filter(author__name="zhangsan").values("title")
    

    3.4.3 聚合查询和分组查询

    # aggregate(*args, **kwargs):
    
    from django.db.models import Avg, Min, Sum, Max
    
    # 查询图书价格的平均值
    Book.objects.all().aggregate(Avg('price'))      # {'price__avg': 20}
    
    # 查询图书价格的最大值,最小值
    Book.objects.all().aggregate(Avg('price'), Max('price'), Min('price'))
    
    # 输出: {'price__avg': 20, 'price__max':Decimal('81.20'), 'price__min': Decimal('12.99')}
    
    # aggregate() 是 QuerySet 的一个终止子句,意思是说, 它返回一个包含一些键值对的字典。键的名称是聚合值
    # 的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
    # 一个名称,可以向聚合子句提供
    Book.objects.all().aggregate(average_price=Avg('price'))
    
    
    # annotate(*args, **kwargs)    分组查询
    # 查询各个作者出的书的总价格,这里就涉及到分组了, 分组条件是 authors__name
    Book.objects.values("authors__name").annotte(Sum("price"))
    
    # 查询各个出版社最便宜的书价是多少
    Book.objects.values("publisher__name").annotate(Min("price"))
    

    3.4.4 F 查询和 Q 查询

    # 示例一: F 查询
    # F 使用查询条件的值,专门取对象中某列值的操作, 例如,将书的价格增加 10
    from django.db.models import F
    models.Book.objects.update(price=F('price')+10)
    
    
    # 示例二: Q 构建搜索条件
    from django.db.models import Q
    
    # Q 对象可以对关键字参数进行封装,从而更好地应用多个查询
    models.Book.objects.filter(Q(title__startswith='P')).all()
    
    # 可以组合使用 &, | 操作符,当一个操作符用于两个Q的对象,它产生一个新的Q对象
    Q(title__startswith='P') | Q(title__startswith='J')
    
    # Q 对象可以用 ~ 操作符放在前面表示否定, 也可允许否定与肯定形式的组合
    Q(title__startswith='P') | ~Q(pub_update__year=2005)
    
    
    models.Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1))
    )
    
    # Q对象可以与关键字参数查询一起使用,不过,一定要把Q对象放在关键字参数查询的前面:
    # 正确:
    models.Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1)),
        title__startswith='P')
    
    # 错误:
    models.Book.objects.get(
        title__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1))
    )
    

    4. admin 的配置

    • admin 是 Django 强大功能之一,它能从数据库中读取数据,呈现在页面上,进行管理。
    • 注册model类到admin的两种方式:
      • 使用register方法:admin.site.register(Book, MyAdmin)
      • 使用register的装饰器:@admin.register(Book)
    # 示例:
    # 一些常用的设置:
            # list_display: 指定要显示的字段
            # search_fields: 指定搜索的字段
            # list_filter: 指定列表过滤器
            # ordering: 指定排序字段
    
    from django.contrib import admin
    from app01.models import *
    
    # 注册model
    class MyAdmin(admin.ModelAdmin):
        list_display = ("title", "price", "publisher")
        search_fields = ("title", "publisher")
        list_filter = ("publisher",)
        ordering = ("price",)
        fieldsets = [
            (None,                {'fields': ['title']}),
            ('price information', {'fields': ['price', 'publisher'], 'classes': ['collapse']}),
        ]
    
    admin.site.register(Book, Admin)
    admin.site.register(Publish)
    admin.site.register(Author)
    

    参考资料:

  • 相关阅读:
    ORA-00119: invalid specification for system parameter REMOTE_LISTENER
    Standby Redo Log 的设定原则、创建、删除、查看、归档位置
    dataguard类型转换与模式转化
    Restore Points 制定回退方案
    11g dataguard 类型、保护模式、服务
    [置顶] 设计模式系列4-抽象工厂模式
    MediaInfo源代码分析 4:Inform()函数
    Codeforces Beta Round #51 D. Beautiful numbers
    如何将ER图转换成关系模式集
    建立简单的服务器端程序
  • 原文地址:https://www.cnblogs.com/linkworld/p/8689900.html
Copyright © 2011-2022 走看看