学习日记之《Django3 Web应用开发实战》——第七章——模型与数据库

本文详细介绍了Django中的自定义ORM框架,包括如何定义模型字段、元类ModelMetaClass的使用,以及Django中常见的数据库操作,如数据表操作、多数据库连接、数据迁移和分表技巧。同时涵盖了Django命令和事务管理的示例。
摘要由CSDN通过智能技术生成

第七章——模型与数据库

自定义ORM框架

class Field(object):
    '''模型字段的基本类'''
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return "<%s:%s>" % (self.__class__.__name__, self.name)

class StringField(Field):
    '''模型字段的字符类型'''
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

class IntegerField(Field):
    '''模型字段的整数类型'''
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

class ModelMetaClass(type):
    '''定义元类,控制Modle对象的创建'''
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        for k, v in attrs.items():
            # 保存类属性和列的映射关系到mappings字典
            if isinstance(v, Field):
                print('Found mapping: %s==>%s' % (k, v))
                mappings[k] = v

        for k in mappings.keys():
            # 将类属性移除, 使定义的字段不污染USER类属性
            attrs.pop(k)
        # 创建类的时候添加一个__table__属性
        attrs['__table__'] = name.lower()
        # 保存属性和类的映射关系,创建类时添加一个__mappings__类属性
        attrs['__mappings__'] = mappings
        return super().__new__(cls, name, bases, attrs)

# 定义model类
class Model(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        self.kwargs = kwargs
        super().__init__()

    def save(self):
        fields, params = [], []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append(str(self.kwargs[k]))
        sql = 'insert into %s (%s) values (%s)'
        sql = sql % (self.__table__, ','.join(fields), ','.join(params))
        print("SQL: %s" % sql)

class User(Model):
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')


u = User(id=12, name='dj', email='dj@qq.com', password='123')
u.save()

Django 中的几个常用命令

python manage.py sqlmigrate index(应用名) 0001——initial(数据名)  # 查看其SQL 语句
python manage.py dumpdata > data.json   # 将整个项目的数据从数据库中导出并保存为json格式的数据
python manage.py dumpdata index(应用名) > data.json  # 导出index应用的数据
python manage.py dumpdata index.Somemodels > data.json  # 导出index应用下某个模型的数据

python manage.py loaddata data.json  # 将data.json 数据导入到数据库中

Django 中的数据表操作(多使用,多练习)原书172页面

Django 执行SQL语句

extra:结果集修改器,一种提供额外查询参数的机制
raw: 执行原始SQL并返回模型实例对象
execute: 直接执行自定义SQL,需要先连接数据库

数据库事务

from django.db import transaction
# transaction.atomic()  # 在视图类或者视图函数中使用事务
# transaction.savepoint()  # 开启事务
# transaction.savepoint_commit()  # 提交事务
# transaction.savepoint_rollback()  # 回滚事务
from django.db.models import F

@transaction.atomic  # 或者使用  with transaction.atomic()
def index(request):
    # 开启事务保护
    sid = transaction.savepoint()
    try:
        id = request.GET.get('id', '')
        if id:
            t = TimeInfo.objects.filter(id=id)
            t.update(payment=F('payment') + 1)
            print('Done')
            # 提交事务
            # 如不设置,程序执行完会自动提交
            transaction.savepoint_commit(sid)
        else:
            TimeInfo.objects.update(payment=F('payment') - 1)
            transaction.savepoint_rollback(sid)
    except Exception as e:
        transaction.savepoint_rollback(sid)
    return render(request, 'index.html', locals())

说明一下 F 的用法
from django.db.models import F

F() 的执行不经过 python解释器,不经过本机内存,是生成 SQL语句的执行。
# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()


# 等于

from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
 

简写了代码:
Reporter.objects.all().update(stories_filed=F('stories_filed') + 1)

多数据库的连接与使用

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    },
    'db1': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db1',   # mysql中创建对应的数据库
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '192.168.241.128',
        'PORT': '3306',
    },
    'db2': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db2',  # mysql中创建对应的数据库
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '192.168.241.128',
        'PORT': '3306',
    },
}

DATABASE_ROUTERS = ['web_test_01.dbRouter.DbAppsRouter']
# 设置每个app模型使用的数据库
DATABASE_APPS_MAPPINGS = {
    # {'app_name': 'database_name'}
    'admin': 'default',
    'app1': 'db1',
    'app2': 'db2',
}

定义数据库读写、数据表关系和数据迁移的方法

# settings.py 同目录创建dbRounter.py
from django.conf import settings

DATABASE_MAPPING = settings.DATABASE_APPS_MAPPINGS

class DbAppsRouter(object):
    def db_for_read(self, model, **kwargs):
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **kwargs):
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **kwargs):
        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None

    def allow_migrate(self, db, app_label, model_name=None, **kwargs):
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None

多数据库的使用

# 创建多个应用,在其对应的modles.py 下创建模型
# app1.models.py
from django.db import models

# Create your models here.

class City(models.Model):
    name = models.CharField(max_length=20)
    def __str__(self):
        return self.name

    class Meta:
        app_label = 'app1'
        db_table = 'city'
        verbose_name = '城市信息表'



# app2.models.py
from django.db import models


# Create your models here.

class PersonInfo(models.Model):
    name = models.CharField(max_length=20)
    age = models.CharField(max_length=20)
    live = models.CharField(max_length=20)


    def __str__(self):
        return self.name

    class Meta:
        app_label = 'app2'
        db_table = 'personinfo'
        verbose_name = '个人信息表'

创建数据表

python manage.py makemigrations 
python manage.py migrate  # 在数据库default(db.sqlite3)中创建内置功能的数据表
python manage.py migrate --database=db1  # 在数据库db1(应用app1)中创建数据表
python manage.py migrate --database=db2  # 在数据库db1(应用app2)中创建数据表

动态创建模型和数据表

例如:一张储存商品信息的数据表,商品销量每天更新,如果将商品每天的销量存储在商品信息表,就需要365个字段。显然不仅合适。
为解决这类需求,将商品每天的销量用新的数据表来存储。
Django 并没有为我们提供动态创建模型和数据库的方法,需要自己在ORM的基础上定义

# 可在models.py 下添加如下函数
def createModel(name, fields, app_label, options=None):
    '''
    动态定义模型对象
    :param name: 模型的命名
    :param fields: 模型字段
    :param app_label: 模型所属的项目应用
    :param options: 模型Meta类的设置属性
    :return: 返回模型对象
    '''
    class Meta:
        pass

    setattr(Meta, 'app_label', app_label) # 用于设置属性值,该属性不一定是存在的
    # 设置模型Meta类的属性
    if options is not None:
        for key, value in options.items():
            setattr(Meta, key, value)

    # 添加模型属性和模型字段
    attrs = {'__module__':f'{app_label}.models', 'Meta': Meta}
    attrs.update(fields)
    # 使用type动态创建类
    return type(name, (models.Model,), attrs)


def createDB(model):
    '''
    使用ORM数据迁移创建数据表
    :param model: 模型对象
    :return:
    '''
    from django.db import connection, ConnectionHandler  # connection 默认使用default数据库
    from django.utils.connection import ConnectionProxy
    from django.db.backends.base.schema import BaseDatabaseSchemaEditor
    # 创建数据表必须使用try except 因为数据表已经存在的情况下会显示异常
    connections = ConnectionHandler()

    connection = ConnectionProxy(connections, alias='db1')  # 多数据下 可自定义连接的数据库
    try:
        with BaseDatabaseSchemaEditor(connection) as editor:
            editor.create_model(model=model)  # 在连接的数据库中生成数据表
    except Exception as e:
        print(f"error: {e}")

def createNewTab(model_name):
    '''
    定义模型对象和创建相应的数据表
    :param model_name: 模型名称(数据表名称)
    :return: 返回模型对象,便于视图执行增删改查
    '''
    fields = {
        'id': models.AutoField(primary_key=True),
        'product': models.CharField(max_length=20),
        'sales': models.IntegerField(),
        '__str__':lambda self:str(self.id),
    }
    options = {
        'verbose_name': model_name,
        'db_table': model_name,
    }
    m = createModel(name=model_name, fields=fields, app_label='app1', options=options)
    print('mm', m)  # mm <class 'app1.models.sales2024-02-29'>
    print('type', type(m))  # type <class 'django.db.models.base.ModelBase'>
    createDB(m)
    return m
# views.py
from .models import createNewTab
import time
# Create your views here.

def indexView(request):
    today = time.localtime(time.time())
    model_name = f"sales{time.strftime('%Y-%m-%d', today)}" # 每天更新模型名
    model_name = createNewTab(model_name)  # 创建对应的数据表
    model_name.objects.create(   # 填充数据
        product='Django',
        sales = 666,
    )
    return HttpResponse('done')

MySQL分表功能

当数据表的数据量过大的时候,数据读写速度会越来越慢,从而增加了网站的响应时间,不利于用户体验。为了提高性能,可以对数据量比较大的数据表进行分表处理,MySQL数据库内置了分表功能,分表功能使用数据表引擎MyISAM实现。

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    },
    'db1': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db1',   # mysql中创建对应的数据库
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '192.168.241.128',
        'PORT': '3306',
        'OPTIONS': {'init_command': 'SET default_storage_engine=MyISAM',},  # 添加分表引擎
    },
    'db2': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db2',  # mysql中创建对应的数据库
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '192.168.241.128',
        'PORT': '3306',
    },
}

定义数据表

def create_table(sql):
    '''创建数据表必须使用try except 因为数据表已经存在的情况下会显示异常'''
    from django.db import ConnectionHandler  # connection 默认使用default数据库
    from django.utils.connection import ConnectionProxy
    from django.db.backends.base.schema import BaseDatabaseSchemaEditor
    # 创建数据表必须使用try except 因为数据表已经存在的情况下会显示异常
    connections = ConnectionHandler()

    connection = ConnectionProxy(connections, alias='db1')  # 多数据下 可自定义连接的数据库
    try:
        with BaseDatabaseSchemaEditor(connection) as editor:
            editor.execute(sql=sql)  # 在连接的数据库中生成数据表
    except Exception as e:
        print(f"error: {e}")


# 创建分表  # 建议分表的主键ID设为字符型
tb_list = []
for i in range(3):
    sql = f'''create table if not exists person{i} (
    id int primary key auto_increment ,
    name varchar(20), 
    sex tinyint not null default '0')
    ENGINE=MyISAM DEFAULT CHARSET=utf8
    AUTO_INCREMENT=1 ;'''
    create_table(sql=sql)
    tb_list.append(f"person{i}")

# 创建总表
# tb_str 将分表联合到总表
tb_str = ','.join(tb_list)
sql_all = f"""create table if not exists allperson(
    id int primary key auto_increment ,
    name varchar(20), 
    sex tinyint not null default '0')
    ENGINE=MERGE UNION=({tb_str}) INSERT_METHOD=LAST 
    CHARSET=utf8 AUTO_INCREMENT=1 ;"""
create_table(sql=sql_all)

class PerInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    sex = models.IntegerField()

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '人员信息'
        managed = False  # 代表在数据迁移过程中不会创建数据表
        db_table = 'allperson'  # 模型映射数据库中特定的数据表

Django——MySQL 分表功能, 将数据写入到数据量最小的分表中

import pymysql
# 连接 MySQL 数据库
conn = pymysql.connect(
    host='192.168.***.128',  # 主机名
    port=3306,         # 端口号,MySQL默认为3306
    user='root',       # 用户名
    password='123456', # 密码
    database='db1',   # 数据库名称
)
# 查询数据表的数据量
def func1(table_name, connection=conn):
    # try:
    with connection.cursor() as cursor:
        # 编写SQL查询语句,查询数据表中的数据量
        sql = f"SELECT COUNT(*) FROM {table_name}"
        cursor.execute(sql)

        # 获取查询结果
        result = cursor.fetchone()

        # 打印数据表中的数据量
        if result:
            count = result[0]
            print(f"数据表 {table_name} 中的数据量为: {count}")
            return count
        else:
            print(f"数据表 {table_name} 是空的")
    # finally:
    #     # 关闭数据库连接
    #     connection.close()

# 找出数据量最小的数据表
def func2():
    table_list = ['person0', 'person1', 'person2']
    tables = {}
    for table_name in table_list:
        c = func1(table_name)
        tables[table_name] = c
    sorted_items = sorted(tables.items(), key=lambda item: item[1])
    if sorted_items:
        return sorted_items[0]  # 返回元组(表名,数据量)
       
# 数据表中插入数据
def func3(table_name, connection=conn):
    try:
        with connection.cursor() as cursor:
            # 编写SQL插入语句
            sql = """  
            INSERT INTO {table_name} (id, name, sex) VALUES (%s, %s, %s)  
            """.format(table_name=table_name)
            # 要插入的数据
            data = ('8', 'hello8', '38')

            # 执行SQL语句
            cursor.execute(sql, data)

            # 提交事务
            connection.commit()

            # 打印插入成功的消息
            print("数据插入成功!")
    finally:
        # 关闭数据库连接
        connection.close()

Django——MySQL 分表功能, 将数据随机写入到各个分表中

import pymysql
# 连接 MySQL 数据库
conn = pymysql.connect(
    host='192.168.***.128',  # 主机名
    port=3306,         # 端口号,MySQL默认为3306
    user='root',       # 用户名
    password='123456', # 密码
    database='db1',   # 数据库名称
)
# 随机向数据表中插入数据
def func3(connection=conn):
    import random
    table_list = ['person0', 'person1', 'person2']  # 数据表
    table_name = random.choice(table_list)
    try:
        with connection.cursor() as cursor:
            # 编写SQL插入语句
            sql = """  
            INSERT INTO {table_name} (id, name, sex) VALUES (%s, %s, %s)  
            """.format(table_name=table_name)
            # 要插入的数据
            data = ('8', 'hello8', '38')

            # 执行SQL语句
            cursor.execute(sql, data)

            # 提交事务
            connection.commit()

            # 打印插入成功的消息
            print("数据插入成功!")
    finally:
        # 关闭数据库连接
        connection.close()
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值