fix: config.0006 半路失败恢复命令 recover_config_0006
This commit is contained in:
104
config/management/commands/recover_config_0006.py
Normal file
104
config/management/commands/recover_config_0006.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
"""修复 config.0006 半路失败:去重 + 补唯一约束/索引,然后 fake 0006。"""
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import connection
|
||||||
|
from django.db.models import Max
|
||||||
|
|
||||||
|
from config.models import PopupPage, Tupianpeizhi
|
||||||
|
|
||||||
|
|
||||||
|
def _table_indexes(table):
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(f'SHOW INDEX FROM `{table}`')
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
# Key_name, Column_name, Non_unique
|
||||||
|
by_key = {}
|
||||||
|
for row in rows:
|
||||||
|
key_name = row[2]
|
||||||
|
col = row[4]
|
||||||
|
non_unique = row[1]
|
||||||
|
by_key.setdefault(key_name, {'cols': [], 'unique': non_unique == 0})
|
||||||
|
by_key[key_name]['cols'].append(col)
|
||||||
|
return by_key
|
||||||
|
|
||||||
|
|
||||||
|
def _has_unique_on(table, columns):
|
||||||
|
cols = list(columns)
|
||||||
|
for info in _table_indexes(table).values():
|
||||||
|
if info['unique'] and info['cols'] == cols:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _drop_index(table, key_name):
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(f'ALTER TABLE `{table}` DROP INDEX `{key_name}`')
|
||||||
|
|
||||||
|
|
||||||
|
def _add_unique(table, name, columns):
|
||||||
|
cols = ', '.join(f'`{c}`' for c in columns)
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(f'ALTER TABLE `{table}` ADD UNIQUE `{name}` ({cols})')
|
||||||
|
|
||||||
|
|
||||||
|
def _add_index(table, name, columns):
|
||||||
|
cols = ', '.join(f'`{c}`' for c in columns)
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(f'CREATE INDEX `{name}` ON `{table}` ({cols})')
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'config.0006 半路失败时:清重复数据、补约束,再执行 migrate config 0006 --fake'
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
self.stdout.write('1) 清理重复数据…')
|
||||||
|
|
||||||
|
for row in Tupianpeizhi.objects.values('club_id', 'ImageType').annotate(max_id=Max('id')):
|
||||||
|
n = Tupianpeizhi.objects.filter(
|
||||||
|
club_id=row['club_id'],
|
||||||
|
ImageType=row['ImageType'],
|
||||||
|
).exclude(id=row['max_id']).delete()[0]
|
||||||
|
if n:
|
||||||
|
self.stdout.write(f' tupianpeizhi 删除 {n} 条重复')
|
||||||
|
|
||||||
|
for row in PopupPage.objects.values('club_id', 'page_key').annotate(max_id=Max('id')):
|
||||||
|
n = PopupPage.objects.filter(
|
||||||
|
club_id=row['club_id'],
|
||||||
|
page_key=row['page_key'],
|
||||||
|
).exclude(id=row['max_id']).delete()[0]
|
||||||
|
if n:
|
||||||
|
self.stdout.write(f' popup_page 删除 {n} 条重复')
|
||||||
|
|
||||||
|
self.stdout.write('2) 检查并补约束/索引…')
|
||||||
|
|
||||||
|
# popup_page:去掉 page_key 单独唯一,改为 (club_id, page_key)
|
||||||
|
popup_indexes = _table_indexes('popup_page')
|
||||||
|
for key_name, info in popup_indexes.items():
|
||||||
|
if info['unique'] and info['cols'] == ['page_key']:
|
||||||
|
self.stdout.write(f' 删除 popup_page 旧唯一索引 {key_name}')
|
||||||
|
_drop_index('popup_page', key_name)
|
||||||
|
|
||||||
|
if not _has_unique_on('popup_page', ['club_id', 'page_key']):
|
||||||
|
self.stdout.write(' 添加 popup_page UNIQUE (club_id, page_key)')
|
||||||
|
_add_unique('popup_page', 'popup_page_club_page_key_uniq', ['club_id', 'page_key'])
|
||||||
|
else:
|
||||||
|
self.stdout.write(' popup_page UNIQUE (club_id, page_key) 已存在')
|
||||||
|
|
||||||
|
if not _has_unique_on('tupianpeizhi', ['club_id', 'ImageType']):
|
||||||
|
self.stdout.write(' 添加 tupianpeizhi UNIQUE (club_id, ImageType)')
|
||||||
|
_add_unique('tupianpeizhi', 'tupianpeizhi_club_id_ImageType_uniq', ['club_id', 'ImageType'])
|
||||||
|
else:
|
||||||
|
self.stdout.write(' tupianpeizhi UNIQUE (club_id, ImageType) 已存在')
|
||||||
|
|
||||||
|
lunbo_indexes = _table_indexes('lunbo')
|
||||||
|
if 'lunbo_club_page_type_idx' not in lunbo_indexes:
|
||||||
|
self.stdout.write(' 添加 lunbo 索引 lunbo_club_page_type_idx')
|
||||||
|
_add_index('lunbo', 'lunbo_club_page_type_idx', ['club_id', 'page_key', 'ImageType'])
|
||||||
|
else:
|
||||||
|
self.stdout.write(' lunbo 索引 lunbo_club_page_type_idx 已存在')
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS(
|
||||||
|
'\n完成。请执行:\n'
|
||||||
|
' python manage.py migrate config 0006 --fake\n'
|
||||||
|
' python manage.py migrate config\n'
|
||||||
|
' python manage.py migrate\n'
|
||||||
|
))
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
-- 部署 jituan 前若 migrate 报 Duplicate entry 唯一约束冲突,在 dbshell 执行。
|
-- 部署 jituan 前若 migrate 报 Duplicate entry 唯一约束冲突时使用。
|
||||||
-- 每种组合只保留 id 最大的一条;不影响订单/资金主表。
|
-- 不要用 mysql 命令行(1Panel MySQL 不在本机 socket),请用:
|
||||||
|
-- python manage.py dbshell < deploy/pre_migrate_dedupe.sql
|
||||||
|
-- 或半路失败的 config.0006:
|
||||||
|
-- python manage.py recover_config_0006
|
||||||
|
-- python manage.py migrate config 0006 --fake
|
||||||
|
-- python manage.py migrate
|
||||||
|
|
||||||
-- config.0006 tupianpeizhi
|
-- config.0006 tupianpeizhi
|
||||||
DELETE FROM tupianpeizhi
|
DELETE FROM tupianpeizhi
|
||||||
|
|||||||
Reference in New Issue
Block a user