Versun

对待生命,不妨大胆一点,因为我们终将失去它



Django数据迁移历险记

2024-05-18

django-5e5c7dbb.png 595 Bytes
不到万不得已,不建议手写migration文件,大佬除外。

事情的起因:

在RSS翻译器的缓存模型中,有一个做为hash的主键,当初脑抽使用了BinaryField类型,导致数据库的检索速度一直不理想。
所以这次准备换成检索速度最快的Integer类型。
正如所料,事情没那么简单,这事得从hash的计算方式说起。

过程:

为了能在低配置的机器上尽可能快的运行,我使用了谷歌开发的CityHash64来计算hash,理由很简单,它的代码逻辑比SHA的要简单的多,对于长文的计算要快的多,又能保证唯一性,所以非常理想。

接着在将BinaryField改为BigIntegerField,并在migrate后,测试存储数据时,程序报错了:OverflowError: Python int too large to convert to SQLite INTEGER,一番GPT后才知道,SQLite只能存储64位的有符号整数,而CityHash64的值是无符号64位,超出了范围。

于是,为了方便后期维护和扩展,决定使用char类型来存储值,虽然比Integer的检索慢,但至少比Binary快呀。
既然使用了char类型,就没有大小限制,果断升级到CityHash128来计算hash,并限制39个字符数,相关代码在此

接下来就是重头戏:数据迁移
首先需要重新计算所有的hash值,所以简单的类型migrate只是做了第一步,还需要在migration文件中手动更新数据。
于是我更改了migration文件:

from django.db import migrations, models
import cityhash

def update_hash(apps, schema_editor):
    MyModel = apps.get_model('translator', 'translated_content')
    for obj in MyModel.objects.all():
        obj.hash = cityhash.CityHash128(f"{obj.original_content}{obj.translated_language}")
        obj.save()

class Migration(migrations.Migration):
    dependencies = [
        ('translator', '0030_alter_azureaitranslator_content_translate_prompt_and_more'),
    ]

    operations = [
        migrations.AlterField(
            model_name='translated_content',
            name='hash',
            field=models.CharField(editable=False, max_length=39, primary_key=True, serialize=False),
        ),
        migrations.RunPython(update_hash),
    ]

加了update_hash函数和RunPython,不要问为啥不用bulk_update,因为它无法用于主键类型字段的更新。
接着migrate没有报错,添加数据也正常,在我以为大功告成时,我检查了表里的数据,果然我还是太乐观了,表里原有的数据没有被更新,而是重新创建了新的一条数据。

又是一番GPT,了解到Django为了安全,任何migrate都不能修改原有的数据,但我不死心,改了update_hash函数:

def update_hash(apps, schema_editor):
    MyModel = apps.get_model('translator', 'translated_content')
    for obj in MyModel.objects.all():
        new_hash = cityhash.CityHash128(f"{obj.original_content}{obj.translated_language}")
        MyModel.objects.filter(hash=obj.hash).update(hash=new_hash)

这次没有意外,依旧无法修改,filter无法正常筛选出数据,一直为空。

只能另辟蹊径,创建多个migration来完成这次的迁移:

  1. 保留原有的hash字段(BinaryField),创建新字段hash_new(CharField)。(代码
  2. 计算hash_new值。(代码
  3. 删除hash字段,并设置hash_new为主键。(代码
  4. 重命名hash_new为hash。(代码
    同时,为了数据安全,写了一个backup_db函数,在执行迁移前,拷贝一份sqlite数据库做备份

好了,万事俱备,只欠东风。
本地运行测试,一切正常。
服务器dev环境运行测试,一切正常。
demo环境运行测试,一切正常。
个人使用的生产环境运行测试,一切正常。

在我以为肯定还有什么问题的时候,它运行正常了。。。。
我怀着忐忑不安的心,将新的docker镜像push了。。。。

接下来就是等待迎接新的Bug了。

PS:我相信我的方案肯定不是最完美的,如果你有更好的方案,欢迎留言联系我哈,感激不尽!


2024-05-18 08:46:58 UTC

2024-05-18

纪念,今天是我第一次徒手写Django的migration文件!


2024-05-17 12:33:30 UTC

2024-05-17

之前推特在严格模式下是可以开的,现在不行了,估计需要更多数据来训练AI。。。
至于图2中所说的像往常一样保持一致,有种此地无银三百两的感觉。。。。

2024-05-17-12-33-30-utc-26070ab1.png 67.2 KB
2024-05-17-12-33-30-utc-cd5e49b6.png 48.2 KB


2024-05-17 06:58:57 UTC

2024-05-17

看到LDAPjs的维护者因为一封恶意邮件而归档了项目,深感惋惜,开源环境正在变的越来越差,我们该做些什么呢?
还有一篇 这个项目还在维护吗?


2024-05-17 06:12:23 UTC

2024-05-17

很喜欢NetBSD的主页设计和颜色搭配


2024-05-16 07:35:18 UTC

2024-05-16

在我Debug了2天后,才发现Django的ModelAdmin模块中, list_filter也会调用get_search_results函数来查询数据库
因此如果你设置了list_filter列表,但在admin页面上并没有成功筛选
可能是你自定义了get_search_results函数,对筛选结果进行了额外的操作。


2024-05-15 01:46:40 UTC

2024-05-15

已将Perplexity的默认模型改为GPT-4o,即刻拥有Sonnet的简洁和快速,又有Opus的智商,非常完美

2024-05-15-01-46-40-utc-bdf9d9de.png 60.6 KB


关于Python的 __pycache__ 文件夹小记

2024-05-14

ref: https://realpython.com/python-pycache/#what-actions-invalidate-the-cache

  • Python 中的 __pycache__ 文件夹是什么?
    Python模块的缓存文件夹,将需要的模块编译为字节码,并缓存(.pyc)到该文件夹中,实现更快的导入速度。
    除了__pycache__ 文件夹,Python还在内存中创建了模块缓存,缓存需要导入多次的模块,减少导入模块的开销。
  • 如何判断缓存的模块是否过期?
    默认基于时间戳判断,也可以基于哈希值
  • 即使使用了 from … import 语法,Python 还是会读取并编译整个模块,包括未使用的。
  • 可以在python命令后使用-X importtime参数来显示每个模块的导入时间
  • 递归删除所有 __pycache__ 文件夹(linux):find . -type d -name __pycache__ -exec rm -rf {} +
  • 如何禁止Python创建缓存文件?
    向 python 命令传递 -B 选项,或者设置环境变量PYTHONDONTWRITEBYTECODE=1
  • 集中存储缓存
    方法1:python -X pycache_prefix=/tmp/pycache calculator.py
    方法2:设置环境变量PYTHONPYCACHEPREFIX=/tmp/pycache
    它会在指定的文件夹下镜像项目的目录结构,由于这种集中式缓存的层次结构与项目结构相匹配,因此可以在多个项目之间共享该缓存文件夹

2024-05-13 11:49:32 UTC

2024-05-13

给我的Home Server加了Status Page

2024-05-13-11-49-32-utc-b68fe20c.png 195 KB


2024-05-11 12:39:04 UTC

2024-05-11

我使用了几乎所有的RSS阅读器,目前体验最好的:
Web端:Feedbin(三栏式),CommaFeed(展开式),Miniflux(单栏式)
客户端:Reeder
目前最期待的:DIYgod正在开发的ReadOK/Follow