django DRF增删改查

本文在django所有的增删改查样例使用的DRF框架序列化操作,并非是简单的orm操作。

目录

  • ORM介绍
  • 数据库设计原则
  • DRF增删改操作
  • 高级查询
  • 原生sql处理

ORM介绍

Django ORM(Object-Relational Mapping,对象关系映射)是 Django 框架的一部分,它提供了一种高效的方式来查询和操作数据库,而无需编写原始的 SQL 语句。Django ORM 允许开发者使用 Python 代码来定义数据模型,然后将这些模型映射到数据库表中。这种抽象层帮助开发者用更直观、更符合对象导向思维的方式来处理数据库操作。
Django 自带数据库迁移工具,可以自动或半自动地将模型变更转化为数据库迁移脚本,从而更新数据库结构。这极大地简化了数据库的版本控制和迁移过程。

数据库设计原则

1.每一个实体,包含一个自增主键。
2.不进行硬删除,使用软删除(给每一个实体设置yn字段,yn=True表示存在,yn=False表示不存在,所以对查询实体,每一个都需要加上yn=true的判断)
3.不在数据库层面设置外键,所有外键关联操作全部用代码进行操作。(如果有外键完整性约束,需要应用程序控制外键会导致表与表之间的耦合,update和delete操作都会涉及相关联的表,影响SQL的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景数据库使用性能优先。)
4.实体与实体之间关系,比如一对多的时候,(例如用户表与企业表),只要在用户表中设置company_id,不需要在company_id设置user_id,因为company与user是一对多的关系,(将外键属性放在“多”的一端(用户));如果是多对多的关系(例如用户表与菜单表),要使用一个中间表(有时也称为联结表或关联表)。这个中间表包含了两个主表的主键作为外键。
5.对于价格字段,如果不涉及计算可以使用decimal,否则使用int来避免精度缺失。
6.不使用存储过程、视图、触发器、Event。高并发大数据的互联网业务,架构设计思想是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能会将数据库拖死,业务逻辑放在服务层具备更好的拓展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引。
7.对不同的系统的数据库设置不同的用户名-数据库名-密码(以此保证安全性)其他的用户进行访问,设置ip白名单对数据库访问进行设置。
8.必须使用varchar(20)存储手机号。(涉及到区号或者国家的代号,varchar可以支持模糊查询)

数据库三大范式:
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。

第一范式(确保每列保持原子性):
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

第二范式(确保表中的每列都和主键相关)
第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

第三范式(确保每列都和主键列直接相关,而不是间接相关)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

DRF增删改操作

这里以一个商品product model 序列化为例:

class ProductSerializer(serializers.ModelSerializer):create_time = serializers.DateTimeField(format="%Y-%m-%d", read_only=True)update_time = serializers.DateTimeField(format="%Y-%m-%d", read_only=True)class Meta:model = Productfields = ['id', 'category_id', 'product_code', 'product_name', 'brand_name', 'image_url_list','product_material', 'product_features', 'supple_price', 'retail_price', 'commission','remark', 'field_json', 'score', 'score_details', 'company_id', 'is_featured', 'stock','status', 'create_time', 'update_time', 'yn']read_only_fields = ('create_time', 'update_time')

同时这里使用DRF 框架进行orm操作,将实体的属性抽成一个serializers 进行序列化操作,方便更好地进行统一管理。
添加,后端拿到data后,直接放一个 ProductSerializer(data=data) 完成操作,注意需要和前端商定相关字段,尽量确保前后端接口的JSON字段一致,这样后端就不用再进行处理了。

        data = request.data.copy()data['company_id'] = user_expand.company_id# 解析请求数据,将company_id加入serializer = ProductSerializer(data=data)print(serializer)if serializer.is_valid():# 保存新产品serializer.save()return build_success_response()else:logger.info(f"ProductAddView: {serializer.errors}")raise BusinessException.build_by_dict(status_data.PARAM_ERROR_40001)def build_success_response(data=None):r_data = OK_200.copy()if data is not None:r_data["data"] = datareturn Response(r_data, status=status.HTTP_200_OK)

获取单个的商品详情:

        try:product = Product.objects.get(pk=product_id)except Product.DoesNotExist:raise BusinessException.build_by_dict(status_data.OBJECT_NOT_EXIST_40003)serializer = ProductSerializer(product)return build_success_response(serializer.data)

更新单个商品信息:

        try:product = Product.objects.get(pk=product_id)except Product.DoesNotExist:raise BusinessException.build_by_dict(status_data.OBJECT_NOT_EXIST_40003)# Update data from requestserializer = ProductSerializer(product, data=request.data, partial=True)if serializer.is_valid():serializer.save()return build_success_response()else:logger.info(f"ProductAddView: {serializer.errors}")raise BusinessException.build_by_dict(status_data.PARAM_ERROR_40001)

ProductSerializer: 这是一个 Django REST Framework 的序列化器类,通常用于将模型实例(如 Django 模型)转换成 JSON 格式,或者将 JSON 数据解析回 Django 模型。
product: 这是一个 Django 模型的实例,表示一个特定的产品。这个实例作为第一个参数传递给 ProductSerializer,意味着序列化器将基于这个现有的模型实例进行操作。
data=request.data: 这是序列化器的第二个参数,通常包含了要更新或创建模型实例时的新数据。request.data 来自 DRF 的请求对象,包含了客户端提交的 JSON 或表单数据。
partial=True: 这个关键字参数告诉序列化器这是一个部分更新。在默认情况下,序列化器在更新模型时期望接收所有必需字段的数据。将 partial 设置为 True 允许更新操作仅包括提供的字段,而不需要完整的字段集合。这对于实现 PATCH HTTP 方法非常有用,其中只需提供需要更改的字段。

删除(注意是软删除)(或者更新一个字段,直接使用这个update较为方便):

        products_to_delete = Product.objects.filter(id__in=product_ids)if not products_to_delete.exists():raise BusinessException.build_by_dict(status_data.OBJECT_NOT_EXIST_40003)# 执行软删除操作,将 'yn' 设置为 Falseproducts_to_delete.update(yn=False)return build_success_response()

高级查询

在这里查询单独做一个模块,因为这里的查询较为复杂,一般来说涉及到如下:1.查询所有的商品列表。 2.基于某个关键词进行模糊查询 3.分页查询。(一般我直接统一归化为一个函数)
这里使用分页查询+Q查询来统一处理。
一般做查询操作都会涉及分页查询,当前端传入参数:

page_size = int(request.data.get('page_size', 10))
page_number = int(request.data.get('page_number', 1))

分页查询是一种常用的技术,用于将大量数据分批次展示给用户,从而提高响应速度,减少数据传输量,并提升用户体验。分页查询常见于列表显示,例如用户列表、产品目录等,特别是当数据量较大时,一次性加载全部数据是不现实的。
page_size(页面大小):这个参数定义了**每页显示的记录数。**page_size 是通过请求数据获取的,如果没有提供,默认为 10。这意味着每页将显示 10 条记录。
page_number(页码):这个参数指定了用户想要访问的页数。在你的示例中,page_number 也是通过请求数据获取的,如果没有提供,默认为第 1 页。这告诉服务器用户想要获取第一个数据批次。
SQL 中实现分页查询,你可以使用 LIMITOFFSET 子句。这些子句允许你限制 SQL 查询返回的结果数量(LIMIT),并指定从哪条记录开始返回(OFFSET)。使用这两个子句结合起来,就可以实现分页功能。

-- 假设传入的参数是 page_size = 10 和 page_number = 1SELECT *
FROM Products
LIMIT 10 OFFSET 0;  -- 对于第1页,OFFSET 是 0
转为代码:
products = Product.objects.filter(query).order_by('-create_time')[offset:offset + page_size]

关系式子 : OFFSET=(page_number−1)×page_size 。
在DRF使用分页,使用自带:

from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import MyModel
from .serializers import MyModelSerializerclass ListMyModel(APIView):def get(self, request):# 获取分页参数page_size = int(request.query_params.get('page_size', 10))page_number = int(request.query_params.get('page_number', 1))# 查询数据queryset = MyModel.objects.all()# 应用分页paginator = PageNumberPagination()paginator.page_size = page_sizeresult_page = paginator.paginate_queryset(queryset, request)serializer = MyModelSerializer(result_page, many=True, context={'request': request})# 返回分页后的结果return paginator.get_paginated_response(serializer.data)

使用Q复杂查询:
Q 对象提供了一种灵活的方式来构建复杂的查询条件,特别是在需要执行 OR 查询时非常有用。使用 Q 对象,你可以组合使用 AND、OR 和 NOT 条件来构建查询,而不仅限于简单的过滤方法(如 .filter() 和 .exclude())所提供的功能。
Q 对象可以通过 Django 的 Q 类来使用,这个类定义在 django.db.models 模块中。一个 Q 对象代表了一个 SQL 表达式,可以是一个简单的条件,也可以是多个条件的组合。这些条件可以用 &(AND)、|(OR)和 ~(NOT)运算符组合起来。

from django.db.models import Q# 创建 Q 对象
q1 = Q(name='John')  # WHERE name = 'John'
q2 = Q(age__gt=30)   # AND age > 30
q3 = Q(name='John') | Q(name='Jane')  # OR name = 'Jane'# 使用 Q 对象构建复杂查询
people = Person.objects.filter(Q(name='John') | Q(age__gt=30))

用了 &= 操作符,它是 Python 中的一个赋值运算符,用于对原有变量进行位与操作后再赋值。在 Q 对象的上下文中,&= 用来添加一个新的条件到现有的 Q 对象中,并且这个新条件与原有条件之间是逻辑 AND 的关系。这意味着结果集必须同时满足这两个条件。
使用Q的时候,字段确定可以直接使用= ,否则就是字段__ (value):下面给出了value的一些类型情况

下面是一些常用的查找类型,可以在 Q 对象中使用:exact: 精确等于。如果不指定任何查找类型,exact 是默认的查找类型。例:Q(field_name__exact="value")
iexact: 不区分大小写的精确等于。例:Q(field_name__iexact="value")
contains: 包含给定的值(区分大小写)。例:Q(field_name__contains="value")
icontains: 包含给定的值(不区分大小写)。例:Q(field_name__icontains="value")
in: 字段值在给定的列表中。例:Q(field_name__in=[list_of_values])
gt, gte, lt, lte: 分别对应大于、大于等于、小于、小于等于。例:Q(field_name__gt=value)
startswith 和 istartswith: 字段值以某个字符串开始(区分和不区分大小写)。例:Q(field_name__startswith="value")
例:Q(field_name__istartswith="value")
endswith 和 iendswith: 字段值以某个字符串结束(区分和不区分大小写)。例:Q(field_name__endswith="value")
例:Q(field_name__iendswith="value")
range: 字段值在两个值之间(包含这两个值)。例:Q(field_name__range=(start_value, end_value))

这里要特别当心的时间的查询:
因为前端传入时间是yy-mm-dd形式,所以要自动+1操作,否则就是默认当天的0点。

        if start_time and end_time:start_date = timezone.datetime.strptime(start_time, '%Y-%m-%d')end_date = timezone.datetime.strptime(end_time, '%Y-%m-%d') + timezone.timedelta(days=1)query &= Q(create_time__range=(start_date, end_date))

下面给出了一个详细的列表查询,包括前端可以传的参数等。

class ProductListView(views.APIView):permission_classes = [IsAuthenticated]def post(self, request):user = request.usertry:user_expand = UserExpand.objects.get(user=user)except UserExpand.DoesNotExist:raise BusinessException.build_by_dict(status_data.OBJECT_NOT_EXIST_40003)page_size = int(request.data.get('page_size', 10))page_number = int(request.data.get('page_number', 1))# Build the query based on user typequery = Q(yn=True)  # Only active productsif user_expand.user_maker == user_expand.UserMakerSource.Merchant:  # Merchantquery &= Q(company_id=user_expand.company_id)elif user_expand.user_maker in [user_expand.UserMakerSource.ADMIN,user_expand.UserMakerSource.Operator]:  # Admin or Operator# No additional restrictions for admins/operatorspasselse:raise BusinessException(status_code.PERMISSION_DENIED_40101, "无权限进行查看")# Apply filters from the requestcategory_id = request.data.get('category_id')product_name = request.data.get('product_name')product_status = request.data.get('product_status')min_score = request.data.get('min_score')max_score = request.data.get('max_score')start_time = request.data.get('submit_start_time')end_time = request.data.get('submit_end_time')if category_id:query &= Q(category_id=category_id)if product_name:query &= Q(product_name__icontains=product_name)if product_status:query &= Q(status=product_status)if min_score:query &= Q(score__gte=min_score)if max_score:query &= Q(score__lte=max_score)if start_time and end_time:start_date = timezone.datetime.strptime(start_time, '%Y-%m-%d')end_date = timezone.datetime.strptime(end_time, '%Y-%m-%d') + timezone.timedelta(days=1)query &= Q(create_time__range=(start_date, end_date))# django自带分页# products = Product.objects.filter(query).order_by('-create_time')# paginator = PageNumberPagination()# paginator.page_size = page_size# result_page = paginator.paginate_queryset(products, request)# serializer = ProductSerializer(result_page, many=True)# Append category names to the data# 使用Q 来进行分页查询offset = (page_number - 1) * page_sizeproducts = Product.objects.filter(query).order_by('-create_time')[offset:offset + page_size]serializer = ProductSerializer(products, many=True)# 由于我们处理的是多个对象,因此必须在初始化序列化器时设置 many=True。# 这是因为在默认情况下(即 many=False),序列化器期望单个对象实例,而不是列表。category_ids = {product['category_id'] for product in serializer.data}categories = Category.objects.filter(id__in=category_ids)category_dict = {category.id: category.category_name for category in categories}for product_data in serializer.data:product_data['category_name'] = category_dict.get(product_data['category_id'], 'Unknown')total_items = Product.objects.filter(query).count()total_pages = (total_items + page_size - 1) // page_sizeresponse_data = {"status": 200,"message": "OK","data": serializer.data,"total_items": total_items,# 表示查询出来的所有items"total_pages": total_pages,# 表示总共的页数"current_page": page_number #现在的页数,前端传过来的page_number}return Response(response_data)

原生sql处理

对于一些非常复杂的处理或者一些不得不用sql来处理的操作,django也提供了相应的措施。

1.使用 raw() 方法执行原始 SQL 查询
raw() 方法可以直接在模型上执行原始 SQL 查询并返回模型实例。这适用于需要直接从数据库获取数据的情况。

products = Product.objects.raw(sql)
products = Product.objects.raw(sql, []) #不使用参数请使用[],不要使用Nonesql = "SELECT * FROM app_product WHERE price > %s"
price_threshold = 100
products = Product.objects.raw(sql, [price_threshold])

注意:返回的类型是实体list,所以需要迭代,这意味着你可以遍历查询结果中的每个对象。

sql = "SELECT * FROM product WHERE price > 100"
products = Product.objects.raw(sql)for product in products:print(product.name, product.price)  # 假设产品模型有 'name' 和 'price' 字段

raw() 方法返回的对象支持部分 QuerySet 操作,如 count() 和 iterator(),但不支持 filter()、exclude() 等,因为这些需要 Django 的查询构建器来构造进一步的 SQL 语句。

2 使用 Django 的cursor()来执行一系列 SQL 命令
对于需要执行多条 SQL 语句的场景,比如在一个事务中执行多个插入或更新操作。
但通常这些都是在数据库游标的上下文中执行,并且主要用于执行不返回行的 SQL 命令(如 INSERT、UPDATE、DELETE 等)。

from django.db import connection
from myapp.models import Productdef get_products_by_raw_sql():# 手动编写的 SQL 查询sql = "SELECT id, name, price FROM myapp_product WHERE price > 100"with connection.cursor() as cursor:# 使用 connection.cursor() 来获取一个游标对象,通过这个对象执行 SQL 命令。cursor.execute(sql)# cursor.execute(sql) 执行 SQL 查询。# 获取查询结果的所有行rows = cursor.fetchall()# 将结果行转换为 Product 模型实例列表products = []for row in rows:product = Product(id=row[0], name=row[1], price=row[2])products.append(product)return products

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/32691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装vue时候发现npm淘宝镜像不能使用,报出:npm.taobao.org和registry.npm.taobao.or

2024.3.12 安装vue时候发现npm淘宝镜像不能使用,需要重新更换源,简单来说就是更换镜像 使用 npm config get registry 查看当前的镜像; npm config get registry 使用npm config set registry http://mirrors.cloud.tencent.com/npm/ &…

食品预包装:舌尖安全的第一道防线

在当今快节奏的生活中,食品预包装不仅是保护食品的屏障,更是传递品质与美味的使者。 预包装首先展现出的是其保鲜的魔力。它宛如一层坚固的护盾,有效地阻隔外界因素对食品的侵蚀,让食品的新鲜度和口感得以长久保存。无论是酥脆的点…

对接Shopify电商平台的流程

对接Shopify平台的流程通常包括以下关键步骤,在整个对接过程中,需要密切关注Shopify的API使用限制、认证机制、数据隐私政策等,确保应用的安全性和合规性。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合…

广东信息工程职业学院2024年成人高等继续教育招生简章

一、学校简介 广东信息工程职业学院位于广东省肇庆市,是一所具有一定办学规模,办学定位和培养目标明确,办学特色和追求鲜明,可持续发展的全日制普通高等学校,学院坚持以人为本,以德育人,以良好…

使用Jetpack Compose创建自定义可滚动网格

使用Jetpack Compose创建自定义可滚动网格 Jetpack Compose是Android开发中的现代UI工具包,极大地简化了界面构建过程。本文将介绍如何使用Jetpack Compose创建一个可滚动的网格布局,帮助开发者更高效地管理大量数据和动态内容。我们将通过示例代码详细…

python从入门到精通11:字符串是数组吗?

在Python中,字符串(str)和数组(通常指的是列表 list 或者在某些上下文中是NumPy数组 numpy.ndarray)是两种不同的数据结构,尽管它们在某些方面可能表现出相似性。 字符串(str)&#…

▶《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch5 蒙特卡洛方法【model-based ——> model-free】

PPT 截取必要信息。 课程网站做习题。总体 MOOC 过一遍 1、视频 学堂在线 习题 2、 过 电子书 是否遗漏 【下载:本章 PDF GitHub 页面链接 】 【第二轮 才整理的,忘光了。。。又看了一遍视频】 3、 过 MOOC 习题 看 PDF 迷迷糊糊, 恍恍惚惚。…

【Python/Pytorch 】-- SVM算法

文章目录 文章目录 00 写在前面01 SVM算法简介02 SVM算法的基本原理线性SVM非线性SVM 03 基于Python 版本的SVM算法04 优化目标表达式理解:05 约束条件表达式理解 00 写在前面 SVM算法可以结合鲸鱼算法、飞蛾扑火算法、粒子群算法、灰狼算法、蝙蝠算法等等各种优化…

OpenAPI

大家好我是苏麟 , 今天带来一个前端生成接口的工具 . 官网 : GitHub - ferdikoomen/openapi-typescript-codegen: NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification 安装命令 npm install openapi-typescript-codegen --sa…

一天了解23种设计模式

学习设计模式之前,我们要知道为什么需要设计模式?想想你在初中、高中时是怎么做数学题的?每遇到一个难题,你不需要重头开始思考解决方法,而是有一定的解题“套路”。设计模式就像是解题的“套路”,一个好的…

数据分析:微生物组差异丰度方法汇总

欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2 介绍 微生物数据具有一下的特点,这使得在做差异分析的时候需要考虑到更多的问题&…

在LangChain中,LLM(大型语言模型)和LLM Chain的区别是什么?

简单来说,LLM是一个大型语言模型,而LLM Chain是由多个LLM或其他组件组成的链式结构,用于在LangChain中构建复杂的自然语言处理流程。 Direct LLM Interface: 直接大型语言模型(LLM)接口: llm Open…

Okhttp响应Json数据

简介 OkHttp是一个高效、现代的HTTP客户端库,专为Android和Java应用程序设计,用于发送网络请求和处理响应。它支持HTTP/2和SPDY协议,允许连接复用,减少延迟,提高网络效率。OkHttp还处理了常见的网络问题,如…

python爬虫入门到精通路线

当谈及Python爬虫从入门到精通的路线时,我们可以将其分为几个关键阶段,每个阶段都有其特定的学习目标和内容。以下是一个清晰的路线规划: 1. 入门阶段 基础知识 学习Python的基础语法、数据类型、控制流等。了解基本的网络协议&#xff08…

电容的命名规则

给如下参数给采购,就可以获取 还有一些参数需要重视 容值随着环境温度而保持的程度 常规应用时是可以不用看材质,但是如果使用在新能源汽车和极端环境下的电子产品,就需要关注材质,曾有供应商把可用级电容供应车企,导致…

Transformer中的类别嵌入

类别嵌入 self.class_embedding nn.Parameter(scale * torch.randn(width))这一行代码的作用是在 VisionTransformer 类中创建并初始化一个类别嵌入向量(class embedding vector),用于表示输入序列的类别信息。 详细解释 类别嵌入 在 Tr…

昇思25天学习打卡营第4天|数据变换Transforms

学习内容复盘 1.1 数据变换 什么是数据变换、为何要数据变换 通常情况下,直接加载的原始数据并不能直接送入神经网络进行训练,此时我们需要对其进行数据预处理。MindSpore提供不同种类的数据变换(Transforms),配合数…

学习VXLAN -- 报文结构、原理和配置

目录 VXLAN背景什么是VXLANVXLAN的优势VXLAN报文结构一些特定名词BDVBDIFVAPVSIVSI-InterfaceAC VXLAN的实现原理图VXLAN MAC地址表项MAC地址动态学习 VXLAN隧道VXLAN隧道工作模式L2 GatewayIP Gateway VXLAN隧道的建立与关联VXLAN隧道建立的方式VXLAN对到与VXLAN关联的方式 配…

低成本STC32G8K64驱动控制BLDC开源入门学习方案

低成本STC32G8K64驱动控制BLDC开源入门学习方案 ✨采用STC32G8K64单片机,参考梁工的STC32G12K128-LQFP48驱动方案制作,梁工BLDC相关的资料:https://www.stcaimcu.com/forum.php?modviewthread&tid7472&extrapage%3D1,在此…

python tarfile解压失败怎么解决

问题原因 在使用tarfile模块解压一份Linux服务器上的打包文件时,出现了错误提示:IOError:[Errno 22] invalid mode (wb) or filename. 经过检查,发现是因为打包文件中有文件名存在“:”符号,而window下的…