django sql 操作

* db 迁移文件命名

python manage.py showmigrations  查看迁移状态
遇到没有 X 迁移的文件,但实际上已经迁移不需要再操作的,执行:(0053_librarygroup_permission 是具体的迁移文件名)
python manage.py migrate your_app_name 0053_librarygroup_permission --fake【fake忽略,在db的django_migrations表中会记录已迁移,后续不再操作】
尽量一个模型各自迁移各自的文件,避免文件之前产生关联,后续映射db遇到奇怪的问题,比如对表修改字段的新增或者减少,独立迁移自己的文件,不和别的表一起生成迁移文件,减少关联。
----------------------------------------
python manage.py makemigrations your_app_name  把实体模型生成迁移文件 python manage.py migrate  把迁移文件映射到 db
【有时可以使用:python manage.py makemigrations --merge】

【如果遇到执行后 db 没有生成 table,找到 db 的 django_migrations 表,把里面对应要生成的table的生成记录删除掉再执行即可】

=======================================================================================

* 查询 db 数据某个字段,然后在数据库层面累加

Book.objects.filter(acno=acno).update(download_count=F('download_count')+1)

* 截取字符串

使用字符串的切片功能来截取字符串的第一个字符。具体方法如下:

string = "Hello, World!"
first_char = string[0]
print(first_char)
在这个例子中,string[0] 会返回字符串 "Hello, World!" 的第一个字符 "H"。

截取多个字符,例如前几个字符,可以使用类似的方法:

# 截取前 5 个字符
first_five_chars = string[:5]
print(first_five_chars)  # 输出 "Hello"
这里 string[:5] 会返回字符串 "Hello, World!" 的前五个字符 "Hello"

# 移除末尾指定字符
keyword = '1,2,3,4,5,' keyword = keyword.rstrip(',') print(keyword) # 输出 '1,2,3,4,5'

* 非空判断

if param is None:  //只判断 null 值
if not param:  //判断 null 值同时判断字段是否空值

* bool 类型,取反写法:

flag = False
if flag:    //正常布尔判断
if not flag:    //取反判断

* if - else if - else 写法:

if self.returned_at:
  return "已還書"
elif self.overdue_settled:
  return "借閱中(已繳部分金額)"
else:
  return "借閱中(已繳部分金額)"

* 异常写法

try:
  histories = histories.filter(returned_at__date=returned_at)
except Exception as e:
  print("查询异常:",e)

* 时间获取

获取当前时间:
from datetime import datetime
current_time = datetime.now()  //精确到时分秒【对应 db 的 timestamptz 类型】
current_date = datetime.now().date()  //精确到天的时间【对应 db 的 date 类型,精确到天或者时分秒的变量,在 filter 中都不可混用】
today = date.today()  //直接获取到天的时间【对应 db 的 date 类型,和以上 datetime.now().date() 结果一致,只是效率更高】
自定义指定时间: time = datetime(2024, 4, 14, 0, 0, 0) time = date(2024, 4, 14)

字符串和时间之间的转换,假如有字符串时间为:date_str = %Y-%m-%d %H:%M:%S

字符串转成时间:【strptime 方法,str 字符串,time 时间,p 表示“解析”,解析成时间】
from datetime import datetime
date_time = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')

如果想提取到天的时间,则:date_time_day = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S').date()
如果想提取到秒的时间,则:date_time_day = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S').second()

时间转成字符串:【strftime 方法,str 字符串,time 时间,f 表示“格式化”,格式换成字符串】
str_now = now.strftime('%Y-%m-%d %H:%M:%S')

或者:
date_time = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')  //str -> time
date_str = datetime.strftime(date_time, '%Y-%m-%d %H:%M:%S')  //time -> str

两个时间求之间间隔的,直接相减:
date = dateEnd - dateStart
然后通过 .days 转成相差几天,如:date.days【相差几秒:date.seconds】,如果要求几年,几月等,也是通过转成天,再去计算

* 判断字符串中是否包含某个字符

if 'xx' not in username:  //检查 username 是否不包含 xx 片段(如果要检查包含,去掉 not 即可)
if not username.startswith('xx'):  //检查 username 前缀是否不包含 xx 片段(如果包含,去掉 not 即可)

* 大小写转换:

str.upper()    //转成大写
str.lower()    //转成小写

* 查询操作

History.objects.get(pk=xx)  查询一条数据【可以使用 pk 来代替表中的主键字段,也可以直接写主键字段名】
History.objects.filter(pk=xx)  查询一个集合

* and 操作【注意:判断 null 是直接根据 = 后面的 False 或者 True 来决定的,__isnull 是固定的,并没有 __isnotnull 的写法】

histories = histories.filter(returned_at__isnull=False,returned_at='2')
这句类型 sql 里面的
wherer returned_at is not null and returned_at = '2'

* or 操作

histories = histories.filter(Q(returned_at__isnull=True) | Q(returned_at=''))
这句类型 sql 里面的
wherer returned_at is null or returned_at = ''

【查询 sql 逻辑“并且”使用,逗号;“或者”使用 Q()|Q();在程序里面,“并且”使用 and,“或者”使用 or 】

并且
if isinstance(result, list) and isinstance(result, set):
或者
if isinstance(result, list) or isinstance(result, set):

* 模糊查询【同时不区分大小写,如果要区分大小写,去掉前面的 i 即可,即:contains,startswith,endswith】

students = User.objects.filter(email__icontains=key) --> sql where email like '%key%'
students = User.objects.filter(email__istartswith=key) --> sql where email like 'key%'
students = User.objects.filter(email__iendswith=key) --> sql where email like '%key'

* 精准匹配

students = User.objects.filter(email=key) --> sql where email = 'key'
(或者:students = User.objects.filter(email__exact=key),exact 表示区分大小写,在英文查询的时候有用,否则和上一句作用完全一样)
(所以:students = User.objects.filter(email__iexact=key),不区分大小写的精确匹配)

* django 很多查询操作,都是在字段后面使用两个下划线 __ 来操作的,比如:【包含等于使用 filter ,不包含不等于使用:exclude】

in 操作:User.objects.filter(email__in=[a,b]) --> where email in (a,b)
not in 操作:User.objects.exclude(email__in=[a,b]) --> where email not in (a,b)
= 操作:User.objects.filter(email=a)
!= 操作:User.objects.exclude(email=a) 
>= 操作:User.objects.filter(age__gte=a)
<= 操作:User.objects.filter(age__lte=a)

Histroy 关联了 book 表,利用 book 表的 acno 字段不区分大小写查询

history = History.objects.get(book__acno__iexact=acno)  //两个 __ 是链表里面的字段,history 表和 book 表链表,同时 book 表的 acno 字段参与判断
Histroy 单表利用自己表的 book_acno 字段不区分大小写查询
history = History.objects.get(book_acno__iexact=acno)  //一个 _ 是单表里面的字段,book_acon 是 history 表里面的字段,参与判断

* 正序排列和倒叙排列

history = History.objects.filter(book=acno).order_by('create_ts') 类型 sql 中的 where book = acno order by create_ts
history = History.objects.filter(book=acno).order_by('-create_ts') 类型 sql 中的 where book = acno order by create_ts desc

* 多表链表查询

histories = History.objects.select_related('user', 'book').order_by('-created_at')
(如果要添加查询条件:History.objects.filter(xx=xx).select_related('user', 'book').order_by('-created_at') ,理论上 filter 放到前面或者后面都可以,但建议放在前面提高查询效率)
-->
SELECT
  history.*
FROM
  history
  INNER JOIN user ON history.user_id = user.id
  INNER JOIN book ON history.book_id = book.id
ORDER BY
  history.created_at DESC;

* 指定返回字段:

User.objects.filter(age__gt=18).values('name', 'age')
类似:
select name,age from user where age > 18

 * 事务写法:

from django.db import transaction as db_transaction
with db_transaction.atomic():
    # 执行数据库操作
    # 如果有异常抛出,事务将会回滚,之前的操作都会被撤销
    # 如果没有异常抛出,事务会提交,将所有操作保存到数据库中
或者使用注解的方式:@transaction.atomic

* 定义集合赋值操作

# 定义一个空集合
my_set = set()

# 给集合放入数据
my_set.add(1)
my_set.add(2)
my_set.add(3)

print(my_set)  # 输出: {1, 2, 3}

或者:
my_set = {1, 2, 3}

* 定义一个数组赋值操作:

# 定义一个空列表
my_list = []

# 向列表添加数据
my_list.append(1)
my_list.append(2)
my_list.append(3)

print(my_list)  # 输出: [1, 2, 3]

或者:
my_list = [1, 2, 3]

* 从一个对象集合中获取某一个字段,并且组成一个数组:

user_ids = [h['user_id'] for h in historys]  //获取 historys 对象集合中的每个h对象.user_id 字段,组成 user_ids 数组
或者如下写法:
user_ids = [h.user_id for h in historys]  //如果要组成集合,[]改成{}即可

* 从 def xxx 方法中,从 request 获取数据,有两种方式:

第一种:
acno = request.data['acno']  //标准的字典对象获取方式,但是不能设置默认值
第二种:
acno = request.data.get('acno', None)  //可以获取的时候设置默认值

* new 一个对象保存的方式:

menu = Menu()
menu.status = 99
menu.remark = menus_json
menu.save()
或者直接:
menu = Menu.objects.create(status=99, remark=menus_json)

* 返回对象换个字段名写法:

# 要被换的字段,全程只在 def get_xxx 方法里面被 return 出来,其他地方不再定义它
class
GlobalMenuSerializer(serializers.ModelSerializer):
# 序列化器就是要返回给前端的json数据 # remark
= serializers.CharField(required=True, allow_null=False,allow_blank=False) menu_json = serializers.SerializerMethodField()  //这里使用新字段名,使用 SerializerMethodField
class Meta: model = Menu fields = ['menu_json']  //返回使用新字段 def get_menu_json(self,obj):  //固定写法 get_<新字段名> return obj.remark

* 两表外键关联写法:

# User 表
class User(models.Model):
name = models.CharField(max_length=64,null=True,blank=True,verbose_name="资源名称")

# Book 表
class
Book(models.Model):
name = models.CharField(max_length=64,null=True,blank=True,verbose_name="资源名称") user
= models.ForeignKey( # 这里就是 user 表的主键外键关联,到时候 db 会映射成 user_id User, on_delete=models.CASCADE, # 当用户记录被删除时,删除对应的书籍记录 related_name='books', # 可选:定义反向关系的名称 editable=False, # 如果不允许在 admin 或其他地方编辑这个字段 default=uuid.uuid4, # 可选:默认生成一个新的 UUID )

利用外键的好处,是可以直接使用 django 的写法,站在 User 的角度,在 Book 中定义好了 related_name='books' ,就可以直接使用:
user = User.objects.get(条件)
books = user.books.all() # 通过这种方式直接查到 user 下关联的所有 book

* 将一个对象集合中的某一个属性收集成为一个数组或者集合

# group_ids 允许有重复数据
userGroups = UserLibraryGroup.objects.filter(user_id=user_id)
group_ids = [group.group_id for group in userGroups]

# group_ids 都是非重复数据
userGroups = UserLibraryGroup.objects.filter(user_id=user_id)
group_ids = {group.group_id for group in userGroups}

* 判断一个参数是否为 “列表,元组,集合” 等方法:

if isinstance(result, list):
    # 如果 result 是列表类型的话,执行对应的逻辑
    pass
if isinstance(result, tuple):
    # 如果 result 是元组类型的话,执行对应的逻辑
    pass
if isinstance(result, set):
    # 如果 result 是集合类型的话,执行对应的逻辑
    pass

* 枚举

class RequestStatus(models.IntegerChoices):
    WISHLIST = 1, 'Wish List'
    REQUESTED = 2, 'Requested'
    ORDERED = 3, 'Ordered'
    REGISTERED = 4, 'Registered'
    CANCELLED = 5, 'Cancelled'

要获取中间的数字:print(RequestStatus.ORDERED.value)
要获取后面的单词:print(RequestStatus.ORDERED.label)
直接使用:print(RequestStatus.ORDERED),输出:(3, 'Ordered')

* 将字段(json对象)压缩成关键字传递【print_person_info(**person_info),使用 ** 直接传递对象】

假設有一個字典 person_info 包含了個人的姓名和年齡資訊:
person_info = {
    'name': 'Alice',
    'age': 30
}
現在如果我們有一個函數 print_person_info 接受兩個參數 name 和 age,我們可以使用 ** 將 person_info 的內容解壓縮為函數的參數:
def print_person_info(name, age):
    print(f"Name: {name}, Age: {age}")
# 使用 ** 解壓縮字典作為函數的參數
print_person_info(**person_info)
這樣,print_person_info(**person_info) 的效果等同於 print_person_info(name='Alice', age=30)。

* 原生 sql 写法

def execute_custom_sql(book_title_search, page, size):
    query = """
    SELECT
        a.*
    FROM
        library_book a
    WHERE
        a.title LIKE %(book_title)s
    LIMIT %(size)s OFFSET %(offset)s;
    """

    # 计算分页参数
    offset = (page - 1) * size

    # 执行查询
    with connection.cursor() as cursor:
        cursor.execute(query, {
            'book_title': f'%{book_title_search}%',
            'size': size,
            'offset': offset
        })
        results = cursor.fetchall()
    return results

可以使用 %s 当作占位符,然后在 cursor.execute(query,xxxx) 第二个参数这里使用中括号 [ xxx,xxx,xxx ] 按照顺序传入替换也可以,或者如上使用 %(xxx)s 具体值来替换

* 使用 select_related 和 prefetch_related 链表查询的时候,对于

1)一对一:select_related   
  使用这个的时候,里面放置的是外键的字段名
(2)一对多(或者多对多):prefetch_related
  使用这个的时候,里面放置的是外键定义的反向关联名:related_name = 'xx' 

* 序列化器中传递单个对象参数写法:

在视图层:
self.data = BookDetailSerializer(book,context={'user': user}).data
在序列化器层:
def get_read_progress(self,obj):
        # 獲取當前用戶
        user = self.context.get('user')

* 序列化器中传递多个对象参数写法:

# 将子评论的层级关系传递给序列化器
context = {'children': children_dict}
merged_context = {**context, 'user': request.user}
            
# self.data = CommentBookSerializer(root_comments, many=True, context=context).data
# self.data = CommentBookSerializer(commentBooks,context={'user': request.user},many=True).data
self.data = CommentBookSerializer(root_comments,context=merged_context,many=True).data

相减取绝对值

def get_overdue_days(self,obj):
        overdue_days = date.today()-obj.due_date
        # abs取絕對值
        return abs(overdue_days.days)

* 类似 java Map 的操作

def favorite_menu_book_type(books):
    # 初始化统计字典【单个Map,key 值和 value 值都用】
    book_type_counts = {
        'BOOk': 0,
        'ER': 0,
        'EBOOK': 0
    }
    # 遍历所有书籍,统计每种类型的书籍数量
    for book in books:
        book_type_name = book.book_type.name
        if book_type_name in ['ER', 'EBOOK']:
            book_type_counts[book_type_name] += 1
        else:
            book_type_counts['BOOk'] += 1

    # 构造最终结果【把单个Map转成集合Map,key值和value值分成两个字段使用】
    result = [
        {'bookType': book_type, 'count': count}
        for book_type, count in book_type_counts.items()
    ]
    return result

* 直接根据“年分”(或者“月份”,“日期”)来查询数据:【create_ts__month=11(月份), create_ts__day=24(日期)】

favoriteBook = FavoriteBook.objects.filter(create_ts__year=2024)

 

posted @ 2024-04-21 17:03  雪化山河  阅读(28)  评论(0编辑  收藏  举报