Django(十)模型:django模型类对数据库的:增/删/改/查、自关联、管理器、元选项(指定表名)

一、插入、更新和删除

  • 调用一个模型类对象的save方法的时候就可以实现对模型类对应数据表的插入和更新。
  • 调用一个模型类对象的delete方法的时候就可以实现对模型类对应数据表数据的删除。

二、自关联

在这里插入图片描述

自关联是一种特殊的一对多的关系。

【案例】:显示广州市的上级地区和下级地区。

  • 地区表:id, atitle, aParent_id;
  • mysql终端中批量执行sql语句:source areas.sql;

第1步,添加地区模型类 app1/models.py

【关系属性】,代表当前地区的父级地区

class AreaInfo(models.Model):
    '''地区模型类'''
    # 地区名称
    atitle = models.CharField(max_length=20)
    # 【关系属性】,代表当前地区的父级地区
    aParent = models.ForeignKey('self', null=True, blank=True)

    # class Meta:
    #     db_table = 'areas'

第2步,创建迁移,应用迁移创建对应表

D:\a\django-app\project1>py manage.py makemigrations
Migrations for 'app1':
  app1\migrations\0004_auto_20200107_1148.py
    - Alter field btitle on bookinfo
    - Create model AreaInfo

D:\a\django-app\project1>py manage.py migrate
Operations to perform:
  Apply all migrations: admin, app1, auth, contenttypes, sessions
Running migrations:
  Applying app1.0004_auto_20200107_1148... OK

查看表mysql命令窗:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sakila             |
| spiders            |
| sys                |
| test888            |
| world              |
+--------------------+
8 rows in set (0.07 sec)

mysql> use test888
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_test888          |
+----------------------------+
| app1_areainfo              |
| app1_bookinfo              |
| app1_heroinfo              |
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
13 rows in set (0.00 sec)

mysql> desc app1_areainfo; 
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| atitle     | varchar(20) | NO   |     | NULL    |                |
| aParent_id | int(11)     | YES  | MUL | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
3 rows in set (0.11 sec)

第3步,把地区数据表导入刚建的表

  • 表位置:q网盘\源码【area.sql】
  • 把例如:
    INSERT INTO app1_areainfo VALUES ('110000', '北京市', NULL);
    中的【app1_】改成 【对应的表名_】
进入sql命令窗:即可导入(路径不要有中文空格等)
use test888; #使用对应的表
source E:/pro_sql/test.sql; #导入数据

#执行效果: Query OK, 1 row affected (0.12 sec)

查询刚导入的表效果

select * from app1_areainfo;
| 659000 | 省直辖行政单位                                |     650000 |
| 659001 | 石河子市                                      |     659000 |
| 659002 | 阿拉尔市                                      |     659000 |
| 659003 | 图木舒克市                                    |     659000 |
| 659004 | 五家渠市                                      |     659000 |
| 990000 | 新疆建设兵团                                  |       NULL |
| 990100 | 第一师                                        |     990000 |
| 990200 | 第二师                                        |     990000 |
| 990300 | 第三师                                        |     990000 |
| 990400 | 第四师                                        |     990000 |
| 990500 | 第五师                                        |     990000 |
| 990600 | 第六师                                        |     990000 |
| 990700 | 第七师                                        |     990000 |
| 990800 | 第八师                                        |     990000 |
| 990900 | 第九师                                        |     990000 |
| 991000 | 第十师                                        |     990000 |
| 991100 | 建工师                                        |     990000 |
| 991200 | 第十二师                                      |     990000 |
| 991300 | 第十三师                                      |     990000 |
| 991400 | 第十四师                                      |     990000 |
+--------+-----------------------------------------------+------------+

第4步,app1/views.py 编辑对应函数,读取地区信息

from booktest.models import BookInfo,AreaInfo

def areas(request):
    '''获取广州市的上级地区和下级地区'''
    # 1.获取广州市的信息
    area = AreaInfo.objects.get(atitle='广州市')
    # 2.查询广州市的上级地区
    parent = area.aParent
    # 3.查询广州市的下级地址
    children = area.areainfo_set.all()
    # 使用模板
    return render(request, 'app1/areas.html', {'area':area,'parent':parent, 'children':children})

第5步,配置app1/urls.py

展示对应省市区信息

from django.urls import path,re_path
from . import views

urlpatterns=[
    path('app1/',views.index),
    path('books/',views.books),

    # 书详情页,通过url接收参数2种写法以下两种都可:
    # path(r"detail/<int:bookId>",views.detail), #参数用尖括号包起来<>
    re_path(r"^detail/(\d+)",views.detail), #参数必须要带括号

    path('addInfo/',views.addInfo), #添加三国书

    path(r'delete/<int:bid>',views.deleteInfo), #删除对应图书

    path(r'area/',views.areas), #展示对应省市区信息
]

第6步,添加展示模板templates/app1/areas.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>自关联案例</title>
</head>
<body>
<h1>当前地区</h1>
{{ area.atitle }}<br/>
<h1>父级地区</h1>
{{ parent.atitle }}<br/>
<h1>下级地址</h1>
<ul>
    {% for child in children %}
        <li>{{ child.atitle }}</li>
    {% endfor %}
</ul>
</body>
</html>

效果:

当前地区:
广州市

父级地区:
广东省

下级地址:
荔湾区
越秀区
海珠区
天河区
白云区
黄埔区
番禺区
花都区
南沙区
萝岗区
增城市
从化市

三、管理器(Manager)

问:BookInfo.objects.all()->objects是一个什么东西呢?

  • 答:objects是Django帮我自动生成的管理器对象,通过这个管理器可以实现对数据的查询。
  • objects是models.Manger类的一个对象。自定义管理器之后Django不再帮我们生成默认的objects管理器。
py manage.py shell
>>> from app1.models import BookInfo,HeroInfo
>>> type(BookInfo.objects)
<class 'django.db.models.manager.Manager'>

1 自定义模型管理器类

1)自定义一个管理器类,这个类继承models.Manger类。
2)再在具体的模型类里定义一个自定义管理器类的对象。

例1:在app1/models.py下定义一个管理器类

自定一个Manager类对象,管理器对象 book = models.Manager()

# 一类
# booktest2_bookinfo
class BookInfo(models.Model):
    '''图书模型类'''
    # 图书名称
    btitle = models.CharField(max_length=20, db_column='title')
    # 图书名字唯一
    # btitle = models.CharField(max_length=20, unique=True, db_index=True)
    # 价格,最大位数为10,小数为2
    # bprice = models.DecimalField(max_digits=10, decimal_places=2)
    # 出版日期
    bpub_date = models.DateField()
    # bpub_date = models.DateField(auto_now_add=True) # 创建时间
    # bpub_date = models.DateField(auto_now=True) # 更新时间
    # 阅读量
    bread = models.IntegerField(default=0)
    # 评论量
    bcomment = models.IntegerField(default=0)
    # 删除标记
    isDelete = models.BooleanField(default=False)

    book = models.Manager() # 自定一个Manager类对象,管理器对象
    #objects = BookInfoManager() # 自定义一个BookInfoManager类的对象

自定义管理器类之后再查询数据就变成如下

由:BookInfo.objects.all() 变:BookInfo.book.all()

>>> quit()

D:\a\django-app\project1>py manage.py shell
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD6
4)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from app1.models import BookInfo

>>> BookInfo.book.all() #【变】
<QuerySet [<BookInfo: 天龙八部>, <BookInfo: 三国演义>, <BookInfo: 红楼梦>, <Book
Info: 水浒传>]>

>>> BookInfo.objects.all() #【原】
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: type object 'BookInfo' has no attribute 'objects'

2 自定义管理器类的应用场景

1)改变查询的结果集。

  • 比如调用BookInfo.books.all()返回的是没有删除的图书的数据。

例2:自定义Manager类

【1】自定义一个BookInfoManager类,实现查寻数据只返回isDelete=False(删除标记(伪删除))的书
【2】使用自定义的manager类

from django.db import models
# 设计和表对应的类,模型类

#【1】自定义一个BookInfoManager类
class BookInfoManager(models.Manager): #继承自models.Manager
    '''图书模型管理器类'''
    # 1.改变原有查询的结果集
    def all(self):
        # 1.调用父类的all方法,获取所有数据
        books = super().all() # QuerySet
        # 2.对books中的数据进行过滤
        books = books.filter(isDelete=False)
        # 返回books
        return books

# 一类
# 图书类
class BookInfo(models.Model):
    '''图书模型类'''
    # 图书名称,CharField说明是一个字符串,max_length指定字符串的最大长度
    btitle = models.CharField(max_length=20,unique=True) #该字段不能重复,db_column='title'
    # 出版日期,DateField说明是一个日期类型
    bpub_date = models.DateField()
    # 阅读量
    bread = models.IntegerField(default=0)
    # 评论量
    bcomment = models.IntegerField(default=0)
    # 删除标记
    isDelete = models.BooleanField(default=False)

    #book = models.Manager() # 自定一个Manager类对象,管理器对象
    objects = BookInfoManager() # 自定义一个BookInfoManager类的对象 【2】使用自定义的manager类

2)添加额外的方法。

  • 管理器类中定义一个方法帮我们操作模型类对应的数据表。
  • 使用self.model()就可以创建一个跟自定义管理器对应的模型类对象。

例3,把插入图书各个语句封装成一个BookInfo类的函数:

app1/models.py
1.【封装插入图书方法为BookInfo的函数】
2.使用时只需要 BookInfo.create_book(书标题,日期) 即可使用详情:

'''使用下一步封装的函数'''
from app1.models import BookInfo
BookInfo.create_book('test','1991-1-1')   
book.btitle #(返回刚插入的标题)
'''定义类的封装插入图书函数'''
from django.db import models

# 一类
# booktest2_bookinfo
class BookInfo(models.Model):
    '''图书模型类'''
    # 图书名称
    btitle = models.CharField(max_length=20, db_column='title')
    # 图书名字唯一
    # btitle = models.CharField(max_length=20, unique=True, db_index=True)
    # 价格,最大位数为10,小数为2
    # bprice = models.DecimalField(max_digits=10, decimal_places=2)
    # 出版日期
    bpub_date = models.DateField()
    # bpub_date = models.DateField(auto_now_add=True) # 创建时间
    # bpub_date = models.DateField(auto_now=True) # 更新时间
    # 阅读量
    bread = models.IntegerField(default=0)
    # 评论量
    bcomment = models.IntegerField(default=0)
    # 删除标记
    isDelete = models.BooleanField(default=False)

    # book = models.Manager() # 自定一个Manager类对象,管理器对象
    #objects = BookInfoManager() # 自定义一个BookInfoManager类的对象

    @classmethod #【封装插入图书方法为BookInfo的函数】
    def create_book(cls, btitle, bpub_date):#cls即类名
        '''添加一本图书'''
        # 创建一个cls类的对象
        obj = cls()
        obj.btitle = btitle
        obj.bpub_date = bpub_date
        # 添加进数据库
        obj.save()
        # 返回obj
        return obj

例4,一般把增、删、改、查自定义函数写到Manger自定义类里

2.封装方法,操作模型类对应的数据表(增删改查)

class BookInfoManager(models.Manager):
    '''图书模型管理器类'''
    # 1.改变原有查询的结果集
    def all(self):
        # 1.调用父类的all方法,获取所有数据
        books = super().all() # QuerySet
        # 2.对books中的数据进行过滤
        books = books.filter(isDelete=False)
        # 返回books
        return books

    # 2.封装方法,操作模型类对应的数据表(增删改查)
    def create_book(self, btitle, bpub_date):
        '''添加一本图书'''
        # 1.创建一个图书对象
        # 获取self所在的模型类
        model_class = self.model
        book = model_class()
        # book = BookInfo()
        book.btitle = btitle
        book.bpub_date = bpub_date
        # 2.添加进数据库
        book.save()
        # 3.返回book
        return book

调用:

from app1.models import BookInfo

#必须通过关键字传参
BookInfo.objects.create(btitle='test3',bpub_ date='1990-10-10' )
<BookInfo:BookInfo object>

上一步写的自定义函数先注释掉:

    # @classmethod
    # def create_book(cls, btitle, bpub_date):
    #     '''添加一本图书'''
    #     # 创建一个cls类的对象
    #     obj = cls()
    #     obj.btitle = btitle
    #     obj.bpub_date = bpub_date
    #     # 添加进数据库
    #     obj.save()
    #     # 返回obj
    #     return obj

小结:

在这里插入图片描述
【模型管理器对象】

  1. 改变原有查询的结果集。
  2. 封装方法,用户操作管理器对象所在模型类对应的数据表。

3. 模型管理器类、模型类间关系

在这里插入图片描述

四、元选项(指定表名)

Django默认生成的表名: 应用名小写_模型类名小写。

元选项:

需要在模型类中定义一个元类Meta,在里面定义一个类属性db_table就可以指定表名。
在这里插入图片描述

应用场景:应用名(app1)和表名不一致情形

【问题】比如对应用名不满意想换个别的名字,此时表名已经生成了,这时应该怎么办呢?

  • 【解决】就可通过元选项解决。

实战app1/models.py

【1】指定表的名字

class AreaInfo(models.Model):
    '''地区模型类'''
    # 地区名称
    atitle = models.CharField(max_length=20)
    # 关系属性,代表当前地区的父级地区
    aParent = models.ForeignKey('self', null=True, blank=True)

    class Meta: #【1】指定表的名字
        db_table = 'areas'
posted @ 2020-01-10 16:27  晨光曦微  阅读(581)  评论(2编辑  收藏  举报